windows-misc-20050102
[openafs.git] / src / WINNT / afsd / cm_vnodeops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #endif /* !DJGPP */
17 #include <stddef.h>
18 #include <malloc.h>
19 #include <string.h>
20 #include <stdlib.h>
21
22 #include <osi.h>
23
24 #include "afsd.h"
25
26 /* Used by cm_FollowMountPoint */
27 #define RWVOL   0
28 #define ROVOL   1
29 #define BACKVOL 2
30
31 #ifdef DEBUG
32 extern void afsi_log(char *pattern, ...);
33 #endif
34
35 unsigned int cm_mountRootGen = 0;
36
37 /*
38  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
39  * I do not know anything more about it.
40  */
41 unsigned char cm_foldUpper[256] = {
42      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
43      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
44     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
45     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
46     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
47     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
48     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
49     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
50     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
51     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
52     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
53     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
55     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
56     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
57     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
58     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
59     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
60     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
61     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
62     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
63     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
64     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
65     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
66     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
67     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
68     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
69     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
70     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
71     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
72     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
73     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
74 };
75
76 /*
77  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
78  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
79  * upper-case u-umlaut).
80  */
81 int cm_stricmp(const char *str1, const char *str2)
82 {
83     char c1, c2;
84
85     while (1) {
86         if (*str1 == 0)
87             if (*str2 == 0)
88                 return 0;
89             else
90                 return -1;
91         if (*str2 == 0)
92             return 1;
93         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
94         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
95         if (c1 < c2)
96             return -1;
97         if (c1 > c2)
98             return 1;
99     }
100 }
101
102 /* characters that are legal in an 8.3 name */
103 /*
104  * We used to have 1's for all characters from 128 to 254.  But
105  * the NT client behaves better if we create an 8.3 name for any
106  * name that has a character with the high bit on, and if we
107  * delete those characters from 8.3 names.  In particular, see
108  * Sybase defect 10859.
109  */
110 char cm_LegalChars[256] = {
111  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
114  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
115  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
116  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
117  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
118  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
119  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
127 };
128
129 /* return true iff component is a valid 8.3 name */
130 int cm_Is8Dot3(char *namep)
131 {
132     int sawDot = 0;
133     int sawUpper = 0, sawLower = 0;
134     unsigned char tc;
135     int charCount = 0;
136         
137     /*
138      * can't have a leading dot;
139      * special case for . and ..
140      */
141     if (namep[0] == '.') {
142         if (namep[1] == 0)
143             return 1;
144         if (namep[1] == '.' && namep[2] == 0)
145             return 1;
146         return 0;
147     }
148     while (tc = *namep++) {
149         if (tc == '.') {
150             /* saw another dot */
151             if (sawDot) return 0;       /* second dot */
152             sawDot = 1;
153             charCount = 0;
154             continue;
155         }
156         if (cm_LegalChars[tc] == 0)
157             return 0;
158         if (tc >= 'A' && tc <= 'Z')
159             sawUpper = 1;
160         else if (tc >= 'a' && tc <= 'z')
161             sawLower = 1;
162         charCount++;
163         if (!sawDot && charCount > 8)
164             /* more than 8 chars in name */
165             return 0;
166         if (sawDot && charCount > 3)
167             /* more than 3 chars in extension */
168             return 0;
169     }
170     /*
171      * Used to check that all characters were the same case.
172      * This doesn't help 16-bit apps, and meanwhile it causes the
173      * MS-DOS Command Prompt to misbehave; see Sybase defect 10709.
174      *
175      if (sawUpper && sawLower)
176          return 0;
177      */
178     return 1;
179 }
180
181 /*
182  * Number unparsing map for generating 8.3 names;
183  * Taken from DFS.
184  */
185 char cm_8Dot3Mapping[41] =
186 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
187  'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S',
188  'T', 'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '@', '%', '!', '&', 'E', 'O'
189 };
190 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
191
192 void cm_Gen8Dot3Name(cm_dirEntry_t *dep, char *shortName, char **shortNameEndp)
193 {
194     char number[12];
195     int i, nsize = 0;
196     int vnode = ntohl(dep->fid.vnode);
197     char *lastDot;
198     int validExtension = 0;
199     char tc, *temp, *name;
200
201     /* Unparse the file's vnode number to get a "uniquifier" */
202     do {
203         number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
204         nsize++;
205         vnode /= cm_8Dot3MapSize;
206     } while (vnode);
207
208     /*
209      * Look for valid extension.  There has to be a dot, and
210      * at least one of the characters following has to be legal.
211      */
212     lastDot = strrchr(dep->name, '.');
213     if (lastDot) {
214         temp = lastDot; temp++;
215         while (tc = *temp++)
216             if (cm_LegalChars[tc])
217                 break;
218         if (tc)
219             validExtension = 1;
220     }       
221
222     /* Copy name characters */
223     name = dep->name;
224     for (i = 0, name = dep->name;
225           i < (7 - nsize) && name != lastDot; ) {
226         tc = *name++;
227
228         if (tc == 0)
229             break;
230         if (!cm_LegalChars[tc])
231             continue;
232         i++;
233         *shortName++ = toupper(tc);
234     }
235
236     /* tilde */
237     *shortName++ = '~';
238
239     /* Copy uniquifier characters */
240     memcpy(shortName, number, nsize);
241     shortName += nsize;
242
243     if (validExtension) {
244         /* Copy extension characters */
245         *shortName++ = *lastDot++;      /* copy dot */
246         for (i = 0, tc = *lastDot++;
247               i < 3 && tc;
248               tc = *lastDot++) {
249             if (cm_LegalChars[tc]) {
250                 i++;
251                 *shortName++ = toupper(tc);
252             }
253         }
254     }
255
256     /* Trailing null */
257     *shortName = 0;
258
259     if (shortNameEndp)
260         *shortNameEndp = shortName;
261 }       
262
263 /* return success if we can open this file in this mode */
264 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
265                   cm_req_t *reqp)
266 {
267     long rights;
268     long code;
269
270     rights = 0;
271     if (openMode != 1) rights |= PRSFS_READ;
272     if (openMode == 1 || openMode == 2 || trunc) rights |= PRSFS_WRITE;
273         
274     lock_ObtainMutex(&scp->mx);
275
276     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
277                       CM_SCACHESYNC_GETSTATUS
278                       | CM_SCACHESYNC_NEEDCALLBACK);
279     lock_ReleaseMutex(&scp->mx);
280
281     return code;
282 }
283
284 /* return success if we can open this file in this mode */
285 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
286                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp)
287 {
288     long rights;
289     long code;
290
291     /* Always allow delete; the RPC will tell us if it's OK */
292     if (desiredAccess == DELETE)
293         return 0;
294
295     rights = 0;
296
297     if (desiredAccess & AFS_ACCESS_READ)
298         rights |= PRSFS_READ;
299
300     if ((desiredAccess & AFS_ACCESS_WRITE)
301          || createDisp == 4)
302         rights |= PRSFS_WRITE;
303
304     lock_ObtainMutex(&scp->mx);
305
306     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
307                       CM_SCACHESYNC_GETSTATUS
308                       | CM_SCACHESYNC_NEEDCALLBACK);
309     lock_ReleaseMutex(&scp->mx);
310
311     /*
312      * If the open will fail because the volume is readonly, then we will
313      * return an access denied error instead.  This is to help brain-dead
314      * apps run correctly on replicated volumes.
315      * See defect 10007 for more information.
316      */
317     if (code == CM_ERROR_READONLY)
318         code = CM_ERROR_NOACCESS;
319
320     return code;
321 }
322
323 /*
324  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
325  * done in three steps:
326  * (1) open for deletion (NT_CREATE_AND_X)
327  * (2) set for deletion on close (NTWTRANSACTION2, SET_FILE_INFO)
328  * (3) close (CLOSE)
329  * We must not do the RPC until step 3.  But if we are going to return an error
330  * code (e.g. directory not empty), we must return it by step 2, otherwise most
331  * clients will not notice it.  So we do a preliminary check.  For deleting
332  * files, this is almost free, since we have already done the RPC to get the
333  * parent directory's status bits.  But for deleting directories, we must do an
334  * additional RPC to get the directory's data to check if it is empty.  Sigh.
335  */
336 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
337         cm_req_t *reqp)
338 {
339     long code;
340     osi_hyper_t thyper;
341     cm_buf_t *bufferp;
342     cm_dirEntry_t *dep;
343     unsigned short *hashTable;
344     unsigned int i, idx;
345     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
346
347     /* First check permissions */
348     lock_ObtainMutex(&dscp->mx);
349     code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_DELETE,
350                       CM_SCACHESYNC_GETSTATUS
351                       | CM_SCACHESYNC_NEEDCALLBACK);
352     lock_ReleaseMutex(&dscp->mx);
353     if (code)
354         return code;
355
356     /* If deleting directory, must be empty */
357
358     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
359         return code;
360
361     thyper.HighPart = 0; thyper.LowPart = 0;
362     lock_ObtainRead(&scp->bufCreateLock);
363     code = buf_Get(scp, &thyper, &bufferp);
364     lock_ReleaseRead(&scp->bufCreateLock);
365     if (code)
366         return code;
367
368     lock_ObtainMutex(&bufferp->mx);
369     lock_ObtainMutex(&scp->mx);
370     while (1) {
371         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
372                           CM_SCACHESYNC_NEEDCALLBACK
373                           | CM_SCACHESYNC_READ
374                           | CM_SCACHESYNC_BUFLOCKED);
375         if (code)
376             break;
377
378         if (cm_HaveBuffer(scp, bufferp, 1))
379             break;
380
381         /* otherwise, load the buffer and try again */
382         lock_ReleaseMutex(&bufferp->mx);
383         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
384         lock_ReleaseMutex(&scp->mx);
385         lock_ObtainMutex(&bufferp->mx);
386         lock_ObtainMutex(&scp->mx);
387         if (code)
388             break;
389     }
390
391     /* We try to determine emptiness without looking beyond the first page,
392      * and without assuming "." and ".." are present and are on the first
393      * page (though these assumptions might, after all, be reasonable).
394      */
395     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
396     for (i=0; i<128; i++) {
397         idx = ntohs(hashTable[i]);
398         while (idx) {
399             if (idx >= 64) {
400                 BeyondPage = 1;
401                 break;
402             }
403             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
404             if (strcmp(dep->name, ".") == 0)
405                 HaveDot = 1;
406             else if (strcmp(dep->name, "..") == 0)
407                 HaveDotDot = 1;
408             else {
409                 code = CM_ERROR_NOTEMPTY;
410                 goto done;
411             }
412             idx = ntohs(dep->next);
413         }
414     }
415     if (BeyondPage && HaveDot && HaveDotDot)
416         code = CM_ERROR_NOTEMPTY;
417     else
418         code = 0;
419   done:   
420     lock_ReleaseMutex(&bufferp->mx);
421     buf_Release(bufferp);
422     lock_ReleaseMutex(&scp->mx);
423     return code;
424 }       
425
426 /*
427  * Iterate through all entries in a directory.
428  * When the function funcp is called, the buffer is locked but the
429  * directory vnode is not.
430  *
431  * If the retscp parameter is not NULL, the parmp must be a 
432  * cm_lookupSearch_t object.  
433  */
434 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
435                   osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
436                   cm_scache_t **retscp)
437 {
438     char *tp;
439     long code;
440     cm_dirEntry_t *dep;
441     cm_buf_t *bufferp;
442     long temp;
443     osi_hyper_t dirLength;
444     osi_hyper_t bufferOffset;
445     osi_hyper_t curOffset;
446     osi_hyper_t thyper;
447     long entryInDir;
448     long entryInBuffer;
449     cm_pageHeader_t *pageHeaderp;
450     int slotInPage;
451     long nextEntryCookie;
452     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
453         
454     /* get the directory size */
455     lock_ObtainMutex(&scp->mx);
456     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
457                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
458     if (code) {
459         lock_ReleaseMutex(&scp->mx);
460         return code;
461     }
462         
463     if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
464         lock_ReleaseMutex(&scp->mx);
465         return CM_ERROR_NOTDIR;
466     }   
467
468     if (retscp)                         /* if this is a lookup call */
469     {
470         cm_lookupSearch_t*      sp = parmp;
471
472 #ifdef AFS_FREELANCE_CLIENT
473         /* Freelance entries never end up in the DNLC because they
474          * do not have an associated cm_server_t
475          */
476     if ( !(cm_freelanceEnabled &&
477                         sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
478                         sp->fid.volume==AFS_FAKE_ROOT_VOL_ID ) )
479 #endif /* AFS_FREELANCE_CLIENT */
480                 {
481                 int casefold = sp->caseFold;
482                         sp->caseFold = 0; /* we have a strong preference for exact matches */
483                         if ( *retscp = cm_dnlcLookup(scp, sp))  /* dnlc hit */
484                         {
485                                 sp->caseFold = casefold;
486                                 lock_ReleaseMutex(&scp->mx);
487                                 return 0;
488                         }
489                 sp->caseFold = casefold;
490                 }
491     }   
492
493     /*
494      * XXX We only get the length once.  It might change when we drop the
495      * lock.
496      */
497     dirLength = scp->length;
498
499     lock_ReleaseMutex(&scp->mx);
500
501     bufferp = NULL;
502     bufferOffset.LowPart = bufferOffset.HighPart = 0;
503     if (startOffsetp)
504         curOffset = *startOffsetp;
505     else {
506         curOffset.HighPart = 0;
507         curOffset.LowPart = 0;
508     }   
509
510     while (1) {
511         /* make sure that curOffset.LowPart doesn't point to the first
512          * 32 bytes in the 2nd through last dir page, and that it
513          * doesn't point at the first 13 32-byte chunks in the first
514          * dir page, since those are dir and page headers, and don't
515          * contain useful information.
516          */
517         temp = curOffset.LowPart & (2048-1);
518         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
519             /* we're in the first page */
520             if (temp < 13*32) temp = 13*32;
521         }
522         else {
523             /* we're in a later dir page */
524             if (temp < 32) temp = 32;
525         }       
526                 
527         /* make sure the low order 5 bits are zero */
528         temp &= ~(32-1);
529                 
530         /* now put temp bits back ito curOffset.LowPart */
531         curOffset.LowPart &= ~(2048-1);
532         curOffset.LowPart |= temp;
533
534         /* check if we've passed the dir's EOF */
535         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
536             break;
537                 
538         /* see if we can use the bufferp we have now; compute in which
539          * page the current offset would be, and check whether that's
540          * the offset of the buffer we have.  If not, get the buffer.
541          */
542         thyper.HighPart = curOffset.HighPart;
543         thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
544         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
545             /* wrong buffer */
546             if (bufferp) {
547                 lock_ReleaseMutex(&bufferp->mx);
548                 buf_Release(bufferp);
549                 bufferp = NULL;
550             }
551
552             lock_ObtainRead(&scp->bufCreateLock);
553             code = buf_Get(scp, &thyper, &bufferp);
554             lock_ReleaseRead(&scp->bufCreateLock);
555                         if (code) {
556                                 /* if buf_Get() fails we do not have a buffer object to lock */
557                 bufferp = NULL;
558                 break;
559                         }
560
561             lock_ObtainMutex(&bufferp->mx);
562             bufferOffset = thyper;
563
564             /* now get the data in the cache */
565             while (1) {
566                 lock_ObtainMutex(&scp->mx);
567                 code = cm_SyncOp(scp, bufferp, userp, reqp,
568                                   PRSFS_LOOKUP,
569                                   CM_SCACHESYNC_NEEDCALLBACK
570                                   | CM_SCACHESYNC_READ
571                                   | CM_SCACHESYNC_BUFLOCKED);
572                 if (code) {
573                     lock_ReleaseMutex(&scp->mx);
574                     break;
575                 }
576                                 
577                 if (cm_HaveBuffer(scp, bufferp, 1)) {
578                     lock_ReleaseMutex(&scp->mx);
579                     break;
580                 }
581
582                 /* otherwise, load the buffer and try again */
583                 lock_ReleaseMutex(&bufferp->mx);
584                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
585                                     reqp);
586                 lock_ReleaseMutex(&scp->mx);
587                 lock_ObtainMutex(&bufferp->mx);
588                 if (code) 
589                     break;
590             }
591             if (code) {
592                 lock_ReleaseMutex(&bufferp->mx);
593                 buf_Release(bufferp);
594                 bufferp = NULL;
595                 break;
596             }
597         }       /* if (wrong buffer) ... */
598                 
599         /* now we have the buffer containing the entry we're interested
600          * in; copy it out if it represents a non-deleted entry.
601          */
602         entryInDir = curOffset.LowPart & (2048-1);
603         entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
604
605         /* page header will help tell us which entries are free.  Page
606          * header can change more often than once per buffer, since
607          * AFS 3 dir page size may be less than (but not more than) a
608          * buffer package buffer.
609          */
610         /* only look intra-buffer */
611         temp = curOffset.LowPart & (buf_bufferSize - 1);
612         temp &= ~(2048 - 1);    /* turn off intra-page bits */
613         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
614
615         /* now determine which entry we're looking at in the page.  If
616          * it is free (there's a free bitmap at the start of the dir),
617          * we should skip these 32 bytes.
618          */
619         slotInPage = (entryInDir & 0x7e0) >> 5;
620         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
621                & (1 << (slotInPage & 0x7)))) {
622             /* this entry is free */
623             numDirChunks = 1;   /* only skip this guy */
624             goto nextEntry;
625         }
626
627         tp = bufferp->datap + entryInBuffer;
628         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
629
630         /* while we're here, compute the next entry's location, too,
631          * since we'll need it when writing out the cookie into the
632          * dir listing stream.
633          */
634         numDirChunks = cm_NameEntries(dep->name, NULL);
635                 
636         /* compute the offset of the cookie representing the next entry */
637         nextEntryCookie = curOffset.LowPart
638             + (CM_DIR_CHUNKSIZE * numDirChunks);
639
640         if (dep->fid.vnode != 0) {
641             /* this is one of the entries to use: it is not deleted */
642             code = (*funcp)(scp, dep, parmp, &curOffset);
643             if (code) 
644                 break;
645         }       /* if we're including this name */
646                 
647       nextEntry:
648         /* and adjust curOffset to be where the new cookie is */
649         thyper.HighPart = 0;
650         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
651         curOffset = LargeIntegerAdd(thyper, curOffset);
652     }           /* while copying data for dir listing */
653
654     /* release the mutex */
655     if (bufferp) {
656         lock_ReleaseMutex(&bufferp->mx);
657         buf_Release(bufferp);
658     }
659     return code;
660 }
661
662 int cm_NoneUpper(char *s)
663 {
664     char c;
665     while (c = *s++)
666         if (c >= 'A' && c <= 'Z')
667             return 0;
668     return 1;
669 }
670
671 int cm_NoneLower(char *s)
672 {
673     char c;
674     while (c = *s++)
675         if (c >= 'a' && c <= 'z')
676             return 0;
677     return 1;
678 }
679
680 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
681                           osi_hyper_t *offp)
682 {
683     cm_lookupSearch_t *sp;
684     int match;
685     char shortName[13];
686     char *matchName;
687
688     sp = (cm_lookupSearch_t *) rockp;
689
690     matchName = dep->name;
691     if (sp->caseFold)
692         match = cm_stricmp(matchName, sp->searchNamep);
693     else
694         match = strcmp(matchName, sp->searchNamep);
695
696     if (match != 0
697          && sp->hasTilde
698          && !cm_Is8Dot3(dep->name)) {
699         matchName = shortName;
700         cm_Gen8Dot3Name(dep, shortName, NULL);
701         if (sp->caseFold)
702             match = cm_stricmp(matchName, sp->searchNamep);
703         else
704             match = strcmp(matchName, sp->searchNamep);
705     }       
706
707     if (match != 0)
708         return 0;
709
710     sp->found = 1;
711     if(!sp->caseFold) 
712         sp->ExactFound = 1;
713
714     if (!sp->caseFold || matchName == shortName) {
715         sp->fid.vnode = ntohl(dep->fid.vnode);
716         sp->fid.unique = ntohl(dep->fid.unique);
717         return CM_ERROR_STOPNOW;
718     }
719
720     /*
721      * If we get here, we are doing a case-insensitive search, and we
722      * have found a match.  Now we determine what kind of match it is:
723      * exact, lower-case, upper-case, or none of the above.  This is done
724      * in order to choose among matches, if there are more than one.
725      */
726
727     /* Exact matches are the best. */
728     match = strcmp(matchName, sp->searchNamep);
729     if (match == 0) {
730         sp->ExactFound = 1;
731         sp->fid.vnode = ntohl(dep->fid.vnode);
732         sp->fid.unique = ntohl(dep->fid.unique);
733         return CM_ERROR_STOPNOW;
734     }
735
736     /* Lower-case matches are next. */
737     if (sp->LCfound)
738         return 0;
739     if (cm_NoneUpper(matchName)) {
740         sp->LCfound = 1;
741         goto inexact;
742     }
743
744     /* Upper-case matches are next. */
745     if (sp->UCfound)
746         return 0;
747     if (cm_NoneLower(matchName)) {
748         sp->UCfound = 1;
749         goto inexact;
750     }
751
752     /* General matches are last. */
753     if (sp->NCfound)
754         return 0;
755     sp->NCfound = 1;
756
757   inexact:
758     sp->fid.vnode = ntohl(dep->fid.vnode);
759     sp->fid.unique = ntohl(dep->fid.unique);
760     return 0;
761 }       
762
763 /* read the contents of a mount point into the appropriate string.
764  * called with locked scp, and returns with locked scp.
765  */
766 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
767 {
768     long code;
769     cm_buf_t *bufp;
770     osi_hyper_t thyper;
771     int tlen;
772
773     if (scp->mountPointStringp) 
774         return 0;
775         
776     /* otherwise, we have to read it in */
777     lock_ReleaseMutex(&scp->mx);
778
779     lock_ObtainRead(&scp->bufCreateLock);
780     thyper.LowPart = thyper.HighPart = 0;
781     code = buf_Get(scp, &thyper, &bufp);
782     lock_ReleaseRead(&scp->bufCreateLock);
783
784     lock_ObtainMutex(&scp->mx);
785     if (code) {
786         return code;
787     }
788     while (1) {
789         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
790                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
791         if (code) {
792             goto done;
793         }
794
795         if (cm_HaveBuffer(scp, bufp, 0)) 
796             break;
797
798         /* otherwise load buffer */
799         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
800         if (code) {
801             goto done;
802         }
803     }
804     /* locked, has callback, has valid data in buffer */
805     if ((tlen = scp->length.LowPart) > 1000) 
806         return CM_ERROR_TOOBIG;
807     if (tlen <= 0) {
808         code = CM_ERROR_INVAL;
809         goto done;
810     }
811
812     /* someone else did the work while we were out */
813     if (scp->mountPointStringp) {
814         code = 0;
815         goto done;
816     }
817
818     /* otherwise, copy out the link */
819     scp->mountPointStringp = malloc(tlen);
820     memcpy(scp->mountPointStringp, bufp->datap, tlen);
821
822     /* now make it null-terminated.  Note that the original contents of a
823      * link that is a mount point is "#volname." where "." is there just to
824      * be turned into a null.  That is, we can trash the last char of the
825      * link without damaging the vol name.  This is a stupid convention,
826      * but that's the protocol.
827      */
828     scp->mountPointStringp[tlen-1] = 0;
829     code = 0;
830
831   done:
832     if (bufp) 
833         buf_Release(bufp);
834     return code;
835 }
836
837 /* called with a locked scp and chases the mount point, yielding outScpp.
838  * scp remains locked, just for simplicity of describing the interface.
839  */
840 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
841                           cm_req_t *reqp, cm_scache_t **outScpp)
842 {
843     char *cellNamep;
844     char *volNamep;
845     int tlen;
846     long code;
847     char *cp;
848     char *mpNamep;
849     cm_volume_t *volp;
850     cm_cell_t *cellp;
851     char mtType;
852     cm_fid_t tfid;
853     size_t vnLength;
854     int type;
855
856     if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
857         tfid = *scp->mountRootFidp;
858         lock_ReleaseMutex(&scp->mx);
859         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
860         lock_ObtainMutex(&scp->mx);
861         return code;
862     }
863
864     /* parse the volume name */
865     mpNamep = scp->mountPointStringp;
866     osi_assert(mpNamep);
867     tlen = strlen(scp->mountPointStringp);
868     mtType = *scp->mountPointStringp;
869     cellNamep = malloc(tlen);
870     volNamep = malloc(tlen);
871
872     cp = strrchr(mpNamep, ':');
873     if (cp) {
874         /* cellular mount point */
875         memset(cellNamep, 0, tlen);
876         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
877         strcpy(volNamep, cp+1);
878         /* now look up the cell */
879         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
880     }
881     else {
882         /* normal mt pt */
883         strcpy(volNamep, mpNamep+1);
884
885         cellp = cm_FindCellByID(scp->fid.cell);
886     }
887
888     if (!cellp) {
889         code = CM_ERROR_NOSUCHCELL;
890         goto done;
891     }
892
893     vnLength = strlen(volNamep);
894     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
895         type = BACKVOL;
896     else if (vnLength >= 10
897               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
898         type = ROVOL;
899     else
900         type = RWVOL;
901
902     /* check for backups within backups */
903     if (type == BACKVOL
904          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
905          == CM_SCACHEFLAG_RO) {
906         code = CM_ERROR_NOSUCHVOLUME;
907         goto done;
908     }
909
910     /* now we need to get the volume */
911     lock_ReleaseMutex(&scp->mx);
912     code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
913     lock_ObtainMutex(&scp->mx);
914         
915     if (code == 0) {
916         /* save the parent of the volume root for this is the 
917          * place where the volume is mounted and we must remember 
918          * this in the volume structure rather than just in the 
919          * scache entry lest the scache entry gets recycled 
920          * (defect 11489)
921          */
922         lock_ObtainMutex(&volp->mx);
923         if(volp->dotdotFidp == (cm_fid_t *) NULL) 
924             volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
925         *(volp->dotdotFidp) = dscp->fid;
926         lock_ReleaseMutex(&volp->mx);
927
928         if (scp->mountRootFidp == 0) {
929             scp->mountRootFidp = malloc(sizeof(cm_fid_t));
930         }
931         scp->mountRootFidp->cell = cellp->cellID;
932         /* if the mt pt is in a read-only volume (not just a
933          * backup), and if there is a read-only volume for the
934          * target, and if this is a type '#' mount point, use
935          * the read-only, otherwise use the one specified.
936          */
937         if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
938              && volp->roID != 0 && type == RWVOL)
939             type = ROVOL;
940         if (type == ROVOL)
941             scp->mountRootFidp->volume = volp->roID;
942         else if (type == BACKVOL)
943             scp->mountRootFidp->volume = volp->bkID;
944         else
945             scp->mountRootFidp->volume = volp->rwID;
946
947         /* the rest of the fid is a magic number */
948         scp->mountRootFidp->vnode = 1;
949         scp->mountRootFidp->unique = 1;
950         scp->mountRootGen = cm_mountRootGen;
951
952         tfid = *scp->mountRootFidp;
953         lock_ReleaseMutex(&scp->mx);
954         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
955         lock_ObtainMutex(&scp->mx);
956     }
957
958   done:
959     free(cellNamep);
960     free(volNamep);
961     return code;
962 }       
963
964 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
965                        cm_req_t *reqp, cm_scache_t **outpScpp)
966 {
967     long code;
968     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
969     cm_scache_t *tscp = NULL;
970     cm_scache_t *mountedScp;
971     cm_lookupSearch_t rock;
972     int getroot;
973
974     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
975          && strcmp(namep, "..") == 0) {
976         if (dscp->dotdotFidp == (cm_fid_t *)NULL
977              || dscp->dotdotFidp->volume == 0)
978             return CM_ERROR_NOSUCHVOLUME;
979         rock.fid = *dscp->dotdotFidp;
980         goto haveFid;
981     }
982
983     memset(&rock, 0, sizeof(rock));
984     rock.fid.cell = dscp->fid.cell;
985     rock.fid.volume = dscp->fid.volume;
986     rock.searchNamep = namep;
987     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
988     rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
989
990     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
991     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
992                         (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
993
994     /* code == 0 means we fell off the end of the dir, while stopnow means
995      * that we stopped early, probably because we found the entry we're
996      * looking for.  Any other non-zero code is an error.
997      */
998     if (code && code != CM_ERROR_STOPNOW) 
999         return code;
1000
1001     getroot = (dscp==cm_rootSCachep) ;
1002     if (!rock.found) {
1003         if (!cm_freelanceEnabled || !getroot) {
1004             if (flags & CM_FLAG_CHECKPATH)
1005                 return CM_ERROR_NOSUCHPATH;
1006             else
1007                 return CM_ERROR_NOSUCHFILE;
1008         }
1009         else {  /* nonexistent dir on freelance root, so add it */
1010             char fullname[200] = ".";
1011             int  found = 0;
1012
1013             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
1014                       osi_LogSaveString(afsd_logp,namep));
1015             if (namep[0] == '.') {
1016                 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1017                     found = 1;
1018                     if ( stricmp(&namep[1], &fullname[1]) )
1019                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1020                     else
1021                         code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1022                 }
1023             } else {
1024                 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1025                     found = 1;
1026                     if ( stricmp(namep, fullname) )
1027                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1028                     else
1029                         code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1030                 }
1031             }
1032             if (!found || code < 0) {   /* add mount point failed, so give up */
1033                 if (flags & CM_FLAG_CHECKPATH)
1034                     return CM_ERROR_NOSUCHPATH;
1035                 else
1036                     return CM_ERROR_NOSUCHFILE;
1037             }
1038             tscp = NULL;   /* to force call of cm_GetSCache */
1039         }
1040     }
1041
1042   haveFid:       
1043     if ( !tscp )    /* we did not find it in the dnlc */
1044     {
1045         dnlcHit = 0;    
1046         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1047         if (code) 
1048             return code;
1049     }       
1050     /* tscp is now held */
1051
1052     lock_ObtainMutex(&tscp->mx);
1053     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1054                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1055     if (code) { 
1056         lock_ReleaseMutex(&tscp->mx);
1057         cm_ReleaseSCache(tscp);
1058         return code;
1059     }
1060     /* tscp is now locked */
1061
1062     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1063          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1064         /* mount points are funny: they have a volume name to mount
1065          * the root of.
1066          */
1067         code = cm_ReadMountPoint(tscp, userp, reqp);
1068         if (code == 0)
1069             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1070                                         &mountedScp);
1071         lock_ReleaseMutex(&tscp->mx);
1072         cm_ReleaseSCache(tscp);
1073         if (code) {
1074             return code;
1075         }
1076         tscp = mountedScp;
1077     }
1078     else {
1079         lock_ReleaseMutex(&tscp->mx);
1080     }
1081
1082     /* copy back pointer */
1083     *outpScpp = tscp;
1084
1085     /* insert scache in dnlc */
1086     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1087         /* lock the directory entry to prevent racing callback revokes */
1088         lock_ObtainMutex(&dscp->mx);
1089         if ( dscp->cbServerp && dscp->cbExpires )
1090             cm_dnlcEnter(dscp, namep, tscp);
1091         lock_ReleaseMutex(&dscp->mx);
1092     }
1093
1094     /* and return */
1095     return 0;
1096 }
1097
1098 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1099 {
1100     char *tp;
1101     int prefixCount;
1102
1103     tp = strrchr(inp, '@');
1104     if (tp == NULL) 
1105         return 0;               /* no @sys */
1106
1107     if (strcmp(tp, "@sys") != 0) 
1108         return 0;       /* no @sys */
1109
1110     /* caller just wants to know if this is a valid @sys type of name */
1111     if (outp == NULL) 
1112         return 1;
1113
1114     if (index >= MAXNUMSYSNAMES)
1115         return -1;
1116
1117     /* otherwise generate the properly expanded @sys name */
1118     prefixCount = tp - inp;
1119
1120     strncpy(outp, inp, prefixCount);    /* copy out "a." from "a.@sys" */
1121     outp[prefixCount] = 0;              /* null terminate the "a." */
1122     strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1123     return 1;
1124 }   
1125
1126 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1127                cm_req_t *reqp, cm_scache_t **outpScpp)
1128 {
1129     long code;
1130     char tname[256];
1131     int sysNameIndex = 0;
1132     cm_scache_t *scp = 0;
1133
1134     if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1135         if (flags & CM_FLAG_CHECKPATH)
1136             return CM_ERROR_NOSUCHPATH;
1137         else
1138             return CM_ERROR_NOSUCHFILE;
1139     }
1140
1141     for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1142         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1143         if (code > 0) {
1144             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1145             if (code == 0) {
1146                 *outpScpp = scp;
1147                 return 0;
1148             }
1149             if (scp) {
1150                 cm_ReleaseSCache(scp);
1151                 scp = 0;
1152             }
1153         } else {
1154             return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1155         }
1156     }
1157
1158     /* None of the possible sysName expansions could be found */
1159     if (flags & CM_FLAG_CHECKPATH)
1160         return CM_ERROR_NOSUCHPATH;
1161     else
1162         return CM_ERROR_NOSUCHFILE;
1163 }
1164
1165 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1166 {
1167     long code;
1168     cm_conn_t *connp;
1169     AFSFid afsFid;
1170     int sflags;
1171     AFSFetchStatus newDirStatus;
1172     AFSVolSync volSync;
1173     struct rx_connection * callp;
1174
1175 #ifdef AFS_FREELANCE_CLIENT
1176     if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1177         /* deleting a mount point from the root dir. */
1178         code = cm_FreelanceRemoveMount(namep);
1179         return code;
1180     }
1181 #endif  
1182
1183     /* make sure we don't screw up the dir status during the merge */
1184     lock_ObtainMutex(&dscp->mx);
1185     sflags = CM_SCACHESYNC_STOREDATA;
1186     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1187     lock_ReleaseMutex(&dscp->mx);
1188     if (code) 
1189         return code;
1190
1191     /* make the RPC */
1192     afsFid.Volume = dscp->fid.volume;
1193     afsFid.Vnode = dscp->fid.vnode;
1194     afsFid.Unique = dscp->fid.unique;
1195     do {
1196         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1197         if (code) 
1198             continue;
1199
1200         callp = cm_GetRxConn(connp);
1201         code = RXAFS_RemoveFile(callp, &afsFid, namep,
1202                                  &newDirStatus, &volSync);
1203         rx_PutConnection(callp);
1204
1205     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1206     code = cm_MapRPCError(code, reqp);
1207
1208     lock_ObtainMutex(&dscp->mx);
1209     cm_dnlcRemove(dscp, namep);
1210     cm_SyncOpDone(dscp, NULL, sflags);
1211     if (code == 0) 
1212         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1213     lock_ReleaseMutex(&dscp->mx);
1214
1215     return code;
1216 }
1217
1218 /* called with a locked vnode, and fills in the link info.
1219  * returns this the vnode still locked.
1220  */
1221 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1222 {
1223     long code;
1224     cm_buf_t *bufp;
1225     long temp;
1226     osi_hyper_t thyper;
1227
1228     lock_AssertMutex(&linkScp->mx);
1229     if (!linkScp->mountPointStringp) {
1230         /* read the link data */
1231         lock_ReleaseMutex(&linkScp->mx);
1232         thyper.LowPart = thyper.HighPart = 0;
1233         code = buf_Get(linkScp, &thyper, &bufp);
1234         lock_ObtainMutex(&linkScp->mx);
1235         if (code) 
1236             return code;
1237         while (1) {
1238             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1239                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1240             if (code) {
1241                 buf_Release(bufp);
1242                 return code;
1243             }
1244             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1245                 break;
1246
1247             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1248             if (code) {
1249                 buf_Release(bufp);
1250                 return code;
1251             }
1252         } /* while loop to get the data */
1253                 
1254         /* now if we still have no link read in,
1255          * copy the data from the buffer */
1256         if ((temp = linkScp->length.LowPart) >= 1024) {
1257             buf_Release(bufp);
1258             return CM_ERROR_TOOBIG;
1259         }
1260
1261         /* otherwise, it fits; make sure it is still null (could have
1262          * lost race with someone else referencing this link above),
1263          * and if so, copy in the data.
1264          */
1265         if (linkScp->mountPointStringp == NULL) {
1266             linkScp->mountPointStringp = malloc(temp+1);
1267             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1268             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1269         }
1270         buf_Release(bufp);
1271     }   /* don't have sym link contents cached */
1272
1273     return 0;
1274 }       
1275
1276 /* called with a held vnode and a path suffix, with the held vnode being a
1277  * symbolic link.  Our goal is to generate a new path to interpret, and return
1278  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1279  * other than the directory containing the symbolic link, then the new root is
1280  * returned in *newRootScpp, otherwise a null is returned there.
1281  */
1282 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1283                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1284                       cm_user_t *userp, cm_req_t *reqp)
1285 {
1286     long code;
1287     char *linkp;
1288     cm_space_t *tsp;
1289
1290     lock_ObtainMutex(&linkScp->mx);
1291     code = cm_HandleLink(linkScp, userp, reqp);
1292     if (code) 
1293         goto done;
1294
1295     /* if we may overflow the buffer, bail out; buffer is signficantly
1296      * bigger than max path length, so we don't really have to worry about
1297      * being a little conservative here.
1298      */
1299     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1300          >= CM_UTILS_SPACESIZE)
1301         return CM_ERROR_TOOBIG;
1302
1303     tsp = cm_GetSpace();
1304     linkp = linkScp->mountPointStringp;
1305     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1306         if (strlen(linkp) > cm_mountRootLen)
1307             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1308         else
1309             tsp->data[0] = 0;
1310         *newRootScpp = cm_rootSCachep;
1311         cm_HoldSCache(cm_rootSCachep);
1312     } else if (*linkp == '\\' || *linkp == '/') {
1313         /* formerly, this was considered to be from the AFS root,
1314          * but this seems to create problems.  instead, we will just
1315          * reject the link */
1316 #if 0   
1317         strcpy(tsp->data, linkp+1);
1318         *newRootScpp = cm_rootSCachep;
1319         cm_HoldSCache(cm_rootSCachep);
1320 #else
1321         code = CM_ERROR_NOSUCHPATH;
1322         goto done;
1323 #endif  
1324     }
1325     else {
1326         /* a relative link */
1327         strcpy(tsp->data, linkp);
1328         *newRootScpp = NULL;
1329     }
1330     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1331         strcat(tsp->data, "\\");
1332         strcat(tsp->data, pathSuffixp);
1333     }
1334     *newSpaceBufferp = tsp;
1335     code = 0;
1336
1337   done:
1338     lock_ReleaseMutex(&linkScp->mx);
1339     return code;
1340 }
1341
1342 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1343                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1344 {
1345     long code;
1346     char *tp;                   /* ptr moving through input buffer */
1347     char tc;                    /* temp char */
1348     int haveComponent;          /* has new component started? */
1349     char component[256];                /* this is the new component */
1350     char *cp;                   /* component name being assembled */
1351     cm_scache_t *tscp;          /* current location in the hierarchy */
1352     cm_scache_t *nscp;          /* next dude down */
1353     cm_scache_t *dirScp;                /* last dir we searched */
1354     cm_scache_t *linkScp;               /* new root for the symlink we just
1355     * looked up */
1356     cm_space_t *psp;            /* space for current path, if we've hit
1357     * any symlinks */
1358     cm_space_t *tempsp;         /* temp vbl */
1359     char *restp;                        /* rest of the pathname to interpret */
1360     int symlinkCount;           /* count of # of symlinks traversed */
1361     int extraFlag;                      /* avoid chasing mt pts for dir cmd */
1362     int phase = 1;                      /* 1 = tidPathp, 2 = pathp */
1363
1364     tp = tidPathp;
1365     if (tp == NULL) {
1366         tp = pathp;
1367         phase = 2;
1368     }
1369     if (tp == NULL) {
1370         tp = "";
1371     }
1372     haveComponent = 0;
1373     psp = NULL;
1374     tscp = rootSCachep;
1375     cm_HoldSCache(tscp);
1376     symlinkCount = 0;
1377     while (1) {
1378         tc = *tp++;
1379
1380         /* map Unix slashes into DOS ones so we can interpret Unix
1381          * symlinks properly
1382          */
1383         if (tc == '/') 
1384             tc = '\\';
1385
1386         if (!haveComponent) {
1387             if (tc == '\\') 
1388                 continue;
1389             else if (tc == 0) {
1390                 if (phase == 1) {
1391                     phase = 2;
1392                     tp = pathp;
1393                     continue;
1394                 }
1395                 code = 0;
1396                 break;
1397             }
1398             else {
1399                 haveComponent = 1;
1400                 cp = component;
1401                 *cp++ = tc;
1402             }
1403         }
1404         else {
1405             /* we have a component here */
1406             if (tc == 0 || tc == '\\') {
1407                 /* end of the component; we're at the last
1408                  * component if tc == 0.  However, if the last
1409                  * is a symlink, we have more to do.
1410                  */
1411                 *cp++ = 0;      /* add null termination */
1412                 extraFlag = 0;
1413                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1414                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1415                 code = cm_Lookup(tscp, component,
1416                                   flags | extraFlag,
1417                                   userp, reqp, &nscp);
1418
1419                 if (code) {
1420                     cm_ReleaseSCache(tscp);
1421                     if (psp) 
1422                         cm_FreeSpace(psp);
1423                     return code;
1424                 }
1425                 haveComponent = 0;      /* component done */
1426                 dirScp = tscp;          /* for some symlinks */
1427                 tscp = nscp;    /* already held */
1428                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1429                     code = 0;
1430                     cm_ReleaseSCache(dirScp);
1431                     break;
1432                 }
1433
1434                 /* now, if tscp is a symlink, we should follow
1435                  * it and assemble the path again.
1436                  */
1437                 lock_ObtainMutex(&tscp->mx);
1438                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1439                                   CM_SCACHESYNC_GETSTATUS
1440                                   | CM_SCACHESYNC_NEEDCALLBACK);
1441                 if (code) {
1442                     lock_ReleaseMutex(&tscp->mx);
1443                     cm_ReleaseSCache(tscp);
1444                     cm_ReleaseSCache(dirScp);
1445                     break;
1446                 }
1447                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1448                     /* this is a symlink; assemble a new buffer */
1449                     lock_ReleaseMutex(&tscp->mx);
1450                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1451                         cm_ReleaseSCache(tscp);
1452                         cm_ReleaseSCache(dirScp);
1453                         if (psp) 
1454                             cm_FreeSpace(psp);
1455                         return CM_ERROR_TOO_MANY_SYMLINKS;
1456                     }
1457                     if (tc == 0) 
1458                         restp = "";
1459                     else 
1460                         restp = tp;
1461                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1462                     if (code) {
1463                         /* something went wrong */
1464                         cm_ReleaseSCache(tscp);
1465                         cm_ReleaseSCache(dirScp);
1466                         break;
1467                     }
1468
1469                     /* otherwise, tempsp has the new path,
1470                      * and linkScp is the new root from
1471                      * which to interpret that path.
1472                      * Continue with the namei processing,
1473                      * also doing the bookkeeping for the
1474                      * space allocation and tracking the
1475                      * vnode reference counts.
1476                      */
1477                     if (psp) 
1478                         cm_FreeSpace(psp);
1479                     psp = tempsp;
1480                     tp = psp->data;
1481                     cm_ReleaseSCache(tscp);
1482                     tscp = linkScp;     
1483                     /* already held
1484                      * by AssembleLink
1485                      * now, if linkScp is null, that's
1486                      * AssembleLink's way of telling us that
1487                      * the sym link is relative to the dir
1488                      * containing the link.  We have a ref
1489                      * to it in dirScp, and we hold it now
1490                      * and reuse it as the new spot in the
1491                      * dir hierarchy.
1492                      */
1493                     if (tscp == NULL) {
1494                         cm_HoldSCache(dirScp);
1495                         tscp = dirScp;
1496                     }
1497                 }       /* if we have a sym link */
1498                 else {
1499                     /* not a symlink, we may be done */
1500                     lock_ReleaseMutex(&tscp->mx);
1501                     if (tc == 0) {
1502                         if (phase == 1) {
1503                             phase = 2;
1504                             tp = pathp;
1505                             continue;
1506                         }
1507                         cm_ReleaseSCache(dirScp);
1508                         code = 0;
1509                         break;
1510                     }
1511                 }
1512                 cm_ReleaseSCache(dirScp);
1513             } /* end of a component */
1514             else *cp++ = tc;
1515         } /* we have a component */
1516     } /* big while loop over all components */
1517
1518     /* already held */
1519     if (psp) 
1520         cm_FreeSpace(psp);
1521     if (code == 0) 
1522         *outScpp = tscp;
1523     return code;
1524 }
1525
1526 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1527  * We chase the link, and return a held pointer to the target, if it exists,
1528  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1529  * and do not hold or return a target vnode.
1530  *
1531  * This is very similar to calling cm_NameI with the last component of a name,
1532  * which happens to be a symlink, except that we've already passed by the name.
1533  *
1534  * This function is typically called by the directory listing functions, which
1535  * encounter symlinks but need to return the proper file length so programs
1536  * like "more" work properly when they make use of the attributes retrieved from
1537  * the dir listing.
1538  *
1539  * The input vnode should not be locked when this function is called.
1540  */
1541 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1542                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1543 {
1544     long code;
1545     cm_space_t *spacep;
1546     cm_scache_t *newRootScp;
1547
1548     osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1549
1550     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1551     if (code) 
1552         return code;
1553
1554     /* now, if newRootScp is NULL, we're really being told that the symlink
1555      * is relative to the current directory (dscp).
1556      */
1557     if (newRootScp == NULL) {
1558         newRootScp = dscp;
1559         cm_HoldSCache(dscp);
1560     }
1561
1562     code = cm_NameI(newRootScp, spacep->data,
1563                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1564                      userp, NULL, reqp, outScpp);
1565
1566     /* this stuff is allocated no matter what happened on the namei call,
1567      * so free it */
1568     cm_FreeSpace(spacep);
1569     cm_ReleaseSCache(newRootScp);
1570
1571     return code;
1572 }
1573
1574 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1575  * check anyway, but we want to minimize the chance that we have to leave stuff
1576  * unstat'd.
1577  */
1578 #define CM_BULKMAX              128
1579
1580 /* rock for bulk stat calls */
1581 typedef struct cm_bulkStat {
1582     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
1583
1584     /* info for the actual call */
1585     int counter;                        /* next free slot */
1586     AFSFid fids[CM_BULKMAX];
1587     AFSFetchStatus stats[CM_BULKMAX];
1588     AFSCallBack callbacks[CM_BULKMAX];
1589 } cm_bulkStat_t;
1590
1591 /* for a given entry, make sure that it isn't in the stat cache, and then
1592  * add it to the list of file IDs to be obtained.
1593  *
1594  * Don't bother adding it if we already have a vnode.  Note that the dir
1595  * is locked, so we have to be careful checking the vnode we're thinking of
1596  * processing, to avoid deadlocks.
1597  */
1598 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1599                      osi_hyper_t *offp)
1600 {
1601     osi_hyper_t thyper;
1602     cm_bulkStat_t *bsp;
1603     int i;
1604     cm_scache_t *tscp;
1605     cm_fid_t tfid;
1606
1607     bsp = rockp;
1608
1609     /* Don't overflow bsp. */
1610     if (bsp->counter >= CM_BULKMAX)
1611         return CM_ERROR_STOPNOW;
1612
1613     thyper.LowPart = buf_bufferSize;
1614     thyper.HighPart = 0;
1615     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1616
1617     /* thyper is now the first byte past the end of the record we're
1618      * interested in, and bsp->bufOffset is the first byte of the record
1619      * we're interested in.
1620      * Skip data in the others.
1621      * Skip '.' and '..'
1622      */
1623     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1624         return 0;
1625     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1626         return CM_ERROR_STOPNOW;
1627     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1628         return 0;
1629
1630     tfid.cell = scp->fid.cell;
1631     tfid.volume = scp->fid.volume;
1632     tfid.vnode = ntohl(dep->fid.vnode);
1633     tfid.unique = ntohl(dep->fid.unique);
1634     tscp = cm_FindSCache(&tfid);
1635     if (tscp) {
1636         if (lock_TryMutex(&tscp->mx)) {
1637             /* we have an entry that we can look at */
1638             if (cm_HaveCallback(tscp)) {
1639                 /* we have a callback on it.  Don't bother
1640                  * fetching this stat entry, since we're happy
1641                  * with the info we have.
1642                  */
1643                 lock_ReleaseMutex(&tscp->mx);
1644                 cm_ReleaseSCache(tscp);
1645                 return 0;
1646             }
1647             lock_ReleaseMutex(&tscp->mx);
1648         }       /* got lock */
1649         cm_ReleaseSCache(tscp);
1650     }   /* found entry */
1651
1652 #ifdef AFS_FREELANCE_CLIENT
1653     // yj: if this is a mountpoint under root.afs then we don't want it
1654     // to be bulkstat-ed, instead, we call getSCache directly and under
1655     // getSCache, it is handled specially.
1656     if  ( cm_freelanceEnabled &&
1657           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
1658           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1659           !(tfid.vnode==0x1 && tfid.unique==0x1) )
1660     {       
1661         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1662         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1663     }
1664 #endif /* AFS_FREELANCE_CLIENT */
1665
1666     i = bsp->counter++;
1667     bsp->fids[i].Volume = scp->fid.volume;
1668     bsp->fids[i].Vnode = tfid.vnode;
1669     bsp->fids[i].Unique = tfid.unique;
1670     return 0;
1671 }       
1672
1673 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1674  * calls on all undeleted files in the page of the directory specified.
1675  */
1676 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1677                      cm_req_t *reqp)
1678 {
1679     long code;
1680     cm_bulkStat_t bb;   /* this is *BIG*, probably 12K or so;
1681                          * watch for stack problems */
1682     AFSCBFids fidStruct;
1683     AFSBulkStats statStruct;
1684     cm_conn_t *connp;
1685     AFSCBs callbackStruct;
1686     long filex;
1687     AFSVolSync volSync;
1688     cm_callbackRequest_t cbReq;
1689     long filesThisCall;
1690     long i;
1691     long j;
1692     cm_scache_t *scp;
1693     cm_fid_t tfid;
1694     struct rx_connection * callp;
1695
1696     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1697
1698     /* should be on a buffer boundary */
1699     osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1700
1701     bb.counter = 0;
1702     bb.bufOffset = *offsetp;
1703
1704     lock_ReleaseMutex(&dscp->mx);
1705     /* first, assemble the file IDs we need to stat */
1706     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1707     lock_ObtainMutex(&dscp->mx);
1708
1709     /* if we failed, bail out early */
1710     if (code && code != CM_ERROR_STOPNOW) return;
1711
1712     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1713      * make the calls to create the entries.  Handle AFSCBMAX files at a
1714      * time.
1715      */
1716     filex = 0;
1717     while (filex < bb.counter) {
1718         filesThisCall = bb.counter - filex;
1719         if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1720
1721         fidStruct.AFSCBFids_len = filesThisCall;
1722         fidStruct.AFSCBFids_val = &bb.fids[filex];
1723         statStruct.AFSBulkStats_len = filesThisCall;
1724         statStruct.AFSBulkStats_val = &bb.stats[filex];
1725         callbackStruct.AFSCBs_len = filesThisCall;
1726         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1727         cm_StartCallbackGrantingCall(NULL, &cbReq);
1728         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1729         do {
1730             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1731             if (code) 
1732                 continue;
1733
1734             callp = cm_GetRxConn(connp);
1735             code = RXAFS_BulkStatus(callp, &fidStruct,
1736                                      &statStruct, &callbackStruct, &volSync);
1737             rx_PutConnection(callp);
1738
1739         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1740                              &volSync, NULL, &cbReq, code));
1741         code = cm_MapRPCError(code, reqp);
1742
1743         osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1744
1745         /* may as well quit on an error, since we're not going to do
1746          * much better on the next immediate call, either.
1747          */
1748         if (code) 
1749             break;
1750
1751         /* otherwise, we should do the merges */
1752         for(i = 0; i<filesThisCall; i++) {
1753             j = filex + i;
1754             tfid.cell = dscp->fid.cell;
1755             tfid.volume = bb.fids[j].Volume;
1756             tfid.vnode = bb.fids[j].Vnode;
1757             tfid.unique = bb.fids[j].Unique;
1758             code = cm_GetSCache(&tfid, &scp, userp, reqp);
1759             if (code != 0) 
1760                 continue;
1761
1762             /* otherwise, if this entry has no callback info, 
1763              * merge in this.
1764              */
1765             lock_ObtainMutex(&scp->mx);
1766             /* now, we have to be extra paranoid on merging in this
1767              * information, since we didn't use cm_SyncOp before
1768              * starting the fetch to make sure that no bad races
1769              * were occurring.  Specifically, we need to make sure
1770              * we don't obliterate any newer information in the
1771              * vnode than have here.
1772              *
1773              * Right now, be pretty conservative: if there's a
1774              * callback or a pending call, skip it.
1775              */
1776             if (scp->cbServerp == NULL
1777                  && !(scp->flags &
1778                        (CM_SCACHEFLAG_FETCHING
1779                          | CM_SCACHEFLAG_STORING
1780                          | CM_SCACHEFLAG_SIZESTORING))) {
1781                 cm_EndCallbackGrantingCall(scp, &cbReq,
1782                                             &bb.callbacks[j],
1783                                             CM_CALLBACK_MAINTAINCOUNT);
1784                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1785                                 userp, 0);
1786             }       
1787             lock_ReleaseMutex(&scp->mx);
1788             cm_ReleaseSCache(scp);
1789         } /* all files in the response */
1790         /* now tell it to drop the count,
1791          * after doing the vnode processing above */
1792         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1793
1794         filex += filesThisCall;
1795     }   /* while there are still more files to process */
1796     osi_Log0(afsd_logp, "END cm_TryBulkStat");
1797 }       
1798
1799 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1800 {
1801     long mask;
1802
1803     /* initialize store back mask as inexpensive local variable */
1804     mask = 0;
1805     memset(statusp, 0, sizeof(AFSStoreStatus));
1806
1807     /* copy out queued info from scache first, if scp passed in */
1808     if (scp) {
1809         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1810             statusp->ClientModTime = scp->clientModTime;
1811             mask |= AFS_SETMODTIME;
1812             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1813         }
1814     }
1815
1816     if (attrp) {
1817         /* now add in our locally generated request */
1818         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1819             statusp->ClientModTime = attrp->clientModTime;
1820             mask |= AFS_SETMODTIME;
1821         }
1822         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1823             statusp->UnixModeBits = attrp->unixModeBits;
1824             mask |= AFS_SETMODE;
1825         }
1826         if (attrp->mask & CM_ATTRMASK_OWNER) {
1827             statusp->Owner = attrp->owner;
1828             mask |= AFS_SETOWNER;
1829         }
1830         if (attrp->mask & CM_ATTRMASK_GROUP) {
1831             statusp->Group = attrp->group;
1832             mask |= AFS_SETGROUP;
1833         }
1834     }
1835     statusp->Mask = mask;
1836 }       
1837
1838 /* set the file size, and make sure that all relevant buffers have been
1839  * truncated.  Ensure that any partially truncated buffers have been zeroed
1840  * to the end of the buffer.
1841  */
1842 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1843                    cm_req_t *reqp)
1844 {
1845     long code;
1846     int shrinking;
1847
1848     /* start by locking out buffer creation */
1849     lock_ObtainWrite(&scp->bufCreateLock);
1850
1851     /* verify that this is a file, not a dir or a symlink */
1852     lock_ObtainMutex(&scp->mx);
1853     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1854                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1855     if (code) 
1856         goto done;
1857         
1858     if (scp->fileType != CM_SCACHETYPE_FILE) {
1859         code = CM_ERROR_ISDIR;
1860         goto done;
1861     }
1862
1863   startover:
1864     if (LargeIntegerLessThan(*sizep, scp->length))
1865         shrinking = 1;
1866     else
1867         shrinking = 0;
1868
1869     lock_ReleaseMutex(&scp->mx);
1870
1871     /* can't hold scp->mx lock here, since we may wait for a storeback to
1872      * finish if the buffer package is cleaning a buffer by storing it to
1873      * the server.
1874      */
1875     if (shrinking)
1876         buf_Truncate(scp, userp, reqp, sizep);
1877
1878     /* now ensure that file length is short enough, and update truncPos */
1879     lock_ObtainMutex(&scp->mx);
1880
1881     /* make sure we have a callback (so we have the right value for the
1882      * length), and wait for it to be safe to do a truncate.
1883      */
1884     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1885                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1886                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1887     if (code) 
1888         goto done;
1889
1890     if (LargeIntegerLessThan(*sizep, scp->length)) {
1891         /* a real truncation.  If truncPos is not set yet, or is bigger
1892          * than where we're truncating the file, set truncPos to this
1893          * new value.
1894          */
1895         if (!shrinking)
1896             goto startover;
1897         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1898              || LargeIntegerLessThan(*sizep, scp->length)) {
1899             /* set trunc pos */
1900             scp->truncPos = *sizep;
1901             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1902         }
1903         /* in either case, the new file size has been changed */
1904         scp->length = *sizep;
1905         scp->mask |= CM_SCACHEMASK_LENGTH;
1906     }
1907     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1908         /* really extending the file */
1909         scp->length = *sizep;
1910         scp->mask |= CM_SCACHEMASK_LENGTH;
1911     }
1912
1913     /* done successfully */
1914     code = 0;
1915
1916   done:
1917     lock_ReleaseMutex(&scp->mx);
1918     lock_ReleaseWrite(&scp->bufCreateLock);
1919
1920     return code;
1921 }
1922
1923 /* set the file size or other attributes (but not both at once) */
1924 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1925                 cm_req_t *reqp)
1926 {
1927     long code;
1928     int flags;
1929     AFSFetchStatus afsOutStatus;
1930     AFSVolSync volSync;
1931     cm_conn_t *connp;
1932     AFSFid tfid;
1933     AFSStoreStatus afsInStatus;
1934     struct rx_connection * callp;
1935
1936     /* handle file length setting */
1937     if (attrp->mask & CM_ATTRMASK_LENGTH)
1938         return cm_SetLength(scp, &attrp->length, userp, reqp);
1939
1940     flags = CM_SCACHESYNC_STORESTATUS;
1941
1942     lock_ObtainMutex(&scp->mx);
1943     /* otherwise, we have to make an RPC to get the status */
1944     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1945
1946     /* make the attr structure */
1947     cm_StatusFromAttr(&afsInStatus, scp, attrp);
1948
1949     lock_ReleaseMutex(&scp->mx);
1950     if (code) 
1951         return code;
1952
1953     /* now make the RPC */
1954     osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1955     tfid.Volume = scp->fid.volume;
1956     tfid.Vnode = scp->fid.vnode;
1957     tfid.Unique = scp->fid.unique;
1958     do {
1959         code = cm_Conn(&scp->fid, userp, reqp, &connp);
1960         if (code) 
1961             continue;
1962
1963         callp = cm_GetRxConn(connp);
1964         code = RXAFS_StoreStatus(callp, &tfid,
1965                                   &afsInStatus, &afsOutStatus, &volSync);
1966         rx_PutConnection(callp);
1967
1968     } while (cm_Analyze(connp, userp, reqp,
1969                          &scp->fid, &volSync, NULL, NULL, code));
1970     code = cm_MapRPCError(code, reqp);
1971
1972     osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1973
1974     lock_ObtainMutex(&scp->mx);
1975     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1976     if (code == 0)
1977         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1978                         CM_MERGEFLAG_FORCE);
1979         
1980     /* if we're changing the mode bits, discard the ACL cache, 
1981      * since we changed the mode bits.
1982      */
1983     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1984     lock_ReleaseMutex(&scp->mx);
1985     return code;
1986 }       
1987
1988 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1989                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1990 {       
1991     cm_conn_t *connp;
1992     long code;
1993     AFSFid dirAFSFid;
1994     cm_callbackRequest_t cbReq;
1995     AFSFid newAFSFid;
1996     cm_fid_t newFid;
1997     cm_scache_t *scp;
1998     int didEnd;
1999     AFSStoreStatus inStatus;
2000     AFSFetchStatus updatedDirStatus;
2001     AFSFetchStatus newFileStatus;
2002     AFSCallBack newFileCallback;
2003     AFSVolSync volSync;
2004     struct rx_connection * callp;
2005
2006     /* can't create names with @sys in them; must expand it manually first.
2007      * return "invalid request" if they try.
2008      */
2009     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2010         return CM_ERROR_ATSYS;
2011     }
2012
2013     /* before starting the RPC, mark that we're changing the file data, so
2014      * that someone who does a chmod will know to wait until our call
2015      * completes.
2016      */
2017     lock_ObtainMutex(&dscp->mx);
2018     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2019     if (code == 0) {
2020         cm_StartCallbackGrantingCall(NULL, &cbReq);
2021     }
2022     lock_ReleaseMutex(&dscp->mx);
2023     if (code) {
2024         return code;
2025     }
2026     didEnd = 0;
2027
2028     cm_StatusFromAttr(&inStatus, NULL, attrp);
2029
2030     /* try the RPC now */
2031     do {
2032         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2033         if (code) 
2034             continue;
2035
2036         dirAFSFid.Volume = dscp->fid.volume;
2037         dirAFSFid.Vnode = dscp->fid.vnode;
2038         dirAFSFid.Unique = dscp->fid.unique;
2039
2040         callp = cm_GetRxConn(connp);
2041         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2042                                  &inStatus, &newAFSFid, &newFileStatus,
2043                                  &updatedDirStatus, &newFileCallback,
2044                                  &volSync);
2045         rx_PutConnection(callp);
2046
2047     } while (cm_Analyze(connp, userp, reqp,
2048                          &dscp->fid, &volSync, NULL, &cbReq, code));
2049     code = cm_MapRPCError(code, reqp);
2050         
2051     lock_ObtainMutex(&dscp->mx);
2052     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2053     if (code == 0) {
2054         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2055     }
2056     lock_ReleaseMutex(&dscp->mx);
2057
2058     /* now try to create the file's entry, too, but be careful to 
2059      * make sure that we don't merge in old info.  Since we weren't locking
2060      * out any requests during the file's creation, we may have pretty old
2061      * info.
2062      */
2063     if (code == 0) {
2064         newFid.cell = dscp->fid.cell;
2065         newFid.volume = dscp->fid.volume;
2066         newFid.vnode = newAFSFid.Vnode;
2067         newFid.unique = newAFSFid.Unique;
2068         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2069         if (code == 0) {
2070             lock_ObtainMutex(&scp->mx);
2071             if (!cm_HaveCallback(scp)) {
2072                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2073                                 userp, 0);
2074                 cm_EndCallbackGrantingCall(scp, &cbReq,
2075                                             &newFileCallback, 0);
2076                 didEnd = 1;     
2077             }       
2078             lock_ReleaseMutex(&scp->mx);
2079             *scpp = scp;
2080         }
2081     }
2082
2083     /* make sure we end things properly */
2084     if (!didEnd)
2085         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2086
2087     return code;
2088 }       
2089
2090 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2091 {
2092     long code;
2093
2094     lock_ObtainWrite(&scp->bufCreateLock);
2095     code = buf_CleanVnode(scp, userp, reqp);
2096     lock_ReleaseWrite(&scp->bufCreateLock);
2097     if (code == 0) {
2098         lock_ObtainMutex(&scp->mx);
2099         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2100                          | CM_SCACHEFLAG_OUTOFSPACE);
2101         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2102                           | CM_SCACHEMASK_CLIENTMODTIME
2103                           | CM_SCACHEMASK_LENGTH))
2104             code = cm_StoreMini(scp, userp, reqp);
2105         lock_ReleaseMutex(&scp->mx);
2106     }
2107     return code;
2108 }
2109
2110 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2111                  cm_user_t *userp, cm_req_t *reqp)
2112 {
2113     cm_conn_t *connp;
2114     long code;
2115     AFSFid dirAFSFid;
2116     cm_callbackRequest_t cbReq;
2117     AFSFid newAFSFid;
2118     cm_fid_t newFid;
2119     cm_scache_t *scp;
2120     int didEnd;
2121     AFSStoreStatus inStatus;
2122     AFSFetchStatus updatedDirStatus;
2123     AFSFetchStatus newDirStatus;
2124     AFSCallBack newDirCallback;
2125     AFSVolSync volSync;
2126     struct rx_connection * callp;
2127
2128     /* can't create names with @sys in them; must expand it manually first.
2129      * return "invalid request" if they try.
2130      */
2131     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2132         return CM_ERROR_ATSYS;
2133     }
2134
2135     /* before starting the RPC, mark that we're changing the directory
2136      * data, so that someone who does a chmod on the dir will wait until
2137      * our call completes.
2138      */
2139     lock_ObtainMutex(&dscp->mx);
2140     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2141     if (code == 0) {
2142         cm_StartCallbackGrantingCall(NULL, &cbReq);
2143     }
2144     lock_ReleaseMutex(&dscp->mx);
2145     if (code) {
2146         return code;
2147     }
2148     didEnd = 0;
2149
2150     cm_StatusFromAttr(&inStatus, NULL, attrp);
2151
2152     /* try the RPC now */
2153     do {
2154         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2155         if (code) 
2156             continue;
2157
2158         dirAFSFid.Volume = dscp->fid.volume;
2159         dirAFSFid.Vnode = dscp->fid.vnode;
2160         dirAFSFid.Unique = dscp->fid.unique;
2161
2162         callp = cm_GetRxConn(connp);
2163         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2164                               &inStatus, &newAFSFid, &newDirStatus,
2165                               &updatedDirStatus, &newDirCallback,
2166                               &volSync);
2167         rx_PutConnection(callp);
2168
2169     } while (cm_Analyze(connp, userp, reqp,
2170                          &dscp->fid, &volSync, NULL, &cbReq, code));
2171     code = cm_MapRPCError(code, reqp);
2172         
2173     lock_ObtainMutex(&dscp->mx);
2174     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2175     if (code == 0) {
2176         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2177     }
2178     lock_ReleaseMutex(&dscp->mx);
2179
2180     /* now try to create the new dir's entry, too, but be careful to 
2181      * make sure that we don't merge in old info.  Since we weren't locking
2182      * out any requests during the file's creation, we may have pretty old
2183      * info.
2184      */
2185     if (code == 0) {
2186         newFid.cell = dscp->fid.cell;
2187         newFid.volume = dscp->fid.volume;
2188         newFid.vnode = newAFSFid.Vnode;
2189         newFid.unique = newAFSFid.Unique;
2190         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2191         if (code == 0) {
2192             lock_ObtainMutex(&scp->mx);
2193             if (!cm_HaveCallback(scp)) {
2194                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2195                                 userp, 0);
2196                 cm_EndCallbackGrantingCall(scp, &cbReq,
2197                                             &newDirCallback, 0);
2198                 didEnd = 1;             
2199             }
2200             lock_ReleaseMutex(&scp->mx);
2201             cm_ReleaseSCache(scp);
2202         }
2203     }
2204
2205     /* make sure we end things properly */
2206     if (!didEnd)
2207         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2208
2209     /* and return error code */
2210     return code;
2211 }       
2212
2213 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2214              cm_user_t *userp, cm_req_t *reqp)
2215 {
2216     cm_conn_t *connp;
2217     long code = 0;
2218     AFSFid dirAFSFid;
2219     AFSFid existingAFSFid;
2220     AFSFetchStatus updatedDirStatus;
2221     AFSFetchStatus newLinkStatus;
2222     AFSVolSync volSync;
2223     struct rx_connection * callp;
2224
2225     if (dscp->fid.cell != sscp->fid.cell ||
2226         dscp->fid.volume != sscp->fid.volume) {
2227         return CM_ERROR_CROSSDEVLINK;
2228     }
2229
2230     lock_ObtainMutex(&dscp->mx);
2231     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2232     lock_ReleaseMutex(&dscp->mx);
2233
2234     if (code)
2235         return code;
2236
2237     do {
2238         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2239         if (code) continue;
2240
2241         dirAFSFid.Volume = dscp->fid.volume;
2242         dirAFSFid.Vnode = dscp->fid.vnode;
2243         dirAFSFid.Unique = dscp->fid.unique;
2244
2245         existingAFSFid.Volume = sscp->fid.volume;
2246         existingAFSFid.Vnode = sscp->fid.vnode;
2247         existingAFSFid.Unique = sscp->fid.unique;
2248
2249         callp = cm_GetRxConn(connp);
2250         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2251             &newLinkStatus, &updatedDirStatus, &volSync);
2252         rx_PutConnection(callp);
2253         osi_Log1(smb_logp,"  RXAFS_Link returns %d", code);
2254
2255     } while (cm_Analyze(connp, userp, reqp,
2256         &dscp->fid, &volSync, NULL, NULL, code));
2257
2258     code = cm_MapRPCError(code, reqp);
2259
2260     lock_ObtainMutex(&dscp->mx);
2261     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2262     if (code == 0) {
2263         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2264     }
2265     lock_ReleaseMutex(&dscp->mx);
2266
2267     return code;
2268 }
2269
2270 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2271                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2272 {
2273     cm_conn_t *connp;
2274     long code;
2275     AFSFid dirAFSFid;
2276     AFSFid newAFSFid;
2277     cm_fid_t newFid;
2278     cm_scache_t *scp;
2279     AFSStoreStatus inStatus;
2280     AFSFetchStatus updatedDirStatus;
2281     AFSFetchStatus newLinkStatus;
2282     AFSVolSync volSync;
2283     struct rx_connection * callp;
2284
2285     /* before starting the RPC, mark that we're changing the directory data,
2286      * so that someone who does a chmod on the dir will wait until our
2287      * call completes.
2288      */
2289     lock_ObtainMutex(&dscp->mx);
2290     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2291     lock_ReleaseMutex(&dscp->mx);
2292     if (code) {
2293         return code;
2294     }
2295
2296     cm_StatusFromAttr(&inStatus, NULL, attrp);
2297
2298     /* try the RPC now */
2299     do {
2300         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2301         if (code) 
2302             continue;
2303
2304         dirAFSFid.Volume = dscp->fid.volume;
2305         dirAFSFid.Vnode = dscp->fid.vnode;
2306         dirAFSFid.Unique = dscp->fid.unique;
2307
2308         callp = cm_GetRxConn(connp);
2309         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2310                               &inStatus, &newAFSFid, &newLinkStatus,
2311                               &updatedDirStatus, &volSync);
2312         rx_PutConnection(callp);
2313
2314     } while (cm_Analyze(connp, userp, reqp,
2315                          &dscp->fid, &volSync, NULL, NULL, code));
2316     code = cm_MapRPCError(code, reqp);
2317         
2318     lock_ObtainMutex(&dscp->mx);
2319     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2320     if (code == 0) {
2321         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2322     }
2323     lock_ReleaseMutex(&dscp->mx);
2324
2325     /* now try to create the new dir's entry, too, but be careful to 
2326      * make sure that we don't merge in old info.  Since we weren't locking
2327      * out any requests during the file's creation, we may have pretty old
2328      * info.
2329      */
2330     if (code == 0) {
2331         newFid.cell = dscp->fid.cell;
2332         newFid.volume = dscp->fid.volume;
2333         newFid.vnode = newAFSFid.Vnode;
2334         newFid.unique = newAFSFid.Unique;
2335         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2336         if (code == 0) {
2337             lock_ObtainMutex(&scp->mx);
2338             if (!cm_HaveCallback(scp)) {
2339                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2340                                 userp, 0);
2341             }       
2342             lock_ReleaseMutex(&scp->mx);
2343             cm_ReleaseSCache(scp);
2344         }
2345     }
2346         
2347     /* and return error code */
2348     return code;
2349 }
2350
2351 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2352                    cm_req_t *reqp)
2353 {
2354     cm_conn_t *connp;
2355     long code;
2356     AFSFid dirAFSFid;
2357     int didEnd;
2358     AFSFetchStatus updatedDirStatus;
2359     AFSVolSync volSync;
2360     struct rx_connection * callp;
2361
2362     /* before starting the RPC, mark that we're changing the directory data,
2363      * so that someone who does a chmod on the dir will wait until our
2364      * call completes.
2365      */
2366     lock_ObtainMutex(&dscp->mx);
2367     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2368     lock_ReleaseMutex(&dscp->mx);
2369     if (code) {
2370         return code;
2371     }
2372     didEnd = 0;
2373
2374     /* try the RPC now */
2375     do {
2376         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2377         if (code) 
2378             continue;
2379
2380         dirAFSFid.Volume = dscp->fid.volume;
2381         dirAFSFid.Vnode = dscp->fid.vnode;
2382         dirAFSFid.Unique = dscp->fid.unique;
2383
2384         callp = cm_GetRxConn(connp);
2385         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2386                                 &updatedDirStatus, &volSync);
2387         rx_PutConnection(callp);
2388
2389     } while (cm_Analyze(connp, userp, reqp,
2390                          &dscp->fid, &volSync, NULL, NULL, code));
2391     code = cm_MapRPCErrorRmdir(code, reqp);
2392         
2393     lock_ObtainMutex(&dscp->mx);
2394     cm_dnlcRemove(dscp, namep); 
2395     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2396     if (code == 0) {
2397         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2398     }
2399     lock_ReleaseMutex(&dscp->mx);
2400
2401     /* and return error code */
2402     return code;
2403 }
2404
2405 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2406 {
2407     /* grab mutex on contents */
2408     lock_ObtainMutex(&scp->mx);
2409
2410     /* reset the prefetch info */
2411     scp->prefetch.base.LowPart = 0;             /* base */
2412     scp->prefetch.base.HighPart = 0;
2413     scp->prefetch.end.LowPart = 0;              /* and end */
2414     scp->prefetch.end.HighPart = 0;
2415
2416     /* release mutex on contents */
2417     lock_ReleaseMutex(&scp->mx);
2418
2419     /* we're done */
2420     return 0;
2421 }       
2422
2423 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2424                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2425 {
2426     cm_conn_t *connp;
2427     long code;
2428     AFSFid oldDirAFSFid;
2429     AFSFid newDirAFSFid;
2430     int didEnd;
2431     AFSFetchStatus updatedOldDirStatus;
2432     AFSFetchStatus updatedNewDirStatus;
2433     AFSVolSync volSync;
2434     int oneDir;
2435     struct rx_connection * callp;
2436
2437     /* before starting the RPC, mark that we're changing the directory data,
2438      * so that someone who does a chmod on the dir will wait until our call
2439      * completes.  We do this in vnode order so that we don't deadlock,
2440      * which makes the code a little verbose.
2441      */
2442     if (oldDscp == newDscp) {
2443         /* check for identical names */
2444         if (strcmp(oldNamep, newNamep) == 0)
2445             return CM_ERROR_RENAME_IDENTICAL;
2446
2447         oneDir = 1;
2448         lock_ObtainMutex(&oldDscp->mx);
2449         cm_dnlcRemove(oldDscp, oldNamep);
2450         cm_dnlcRemove(oldDscp, newNamep);
2451         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2452                           CM_SCACHESYNC_STOREDATA);
2453         lock_ReleaseMutex(&oldDscp->mx);
2454     }
2455     else {
2456         /* two distinct dir vnodes */
2457         oneDir = 0;
2458         if (oldDscp->fid.cell != newDscp->fid.cell ||
2459              oldDscp->fid.volume != newDscp->fid.volume)
2460             return CM_ERROR_CROSSDEVLINK;
2461
2462         /* shouldn't happen that we have distinct vnodes for two
2463          * different files, but could due to deliberate attack, or
2464          * stale info.  Avoid deadlocks and quit now.
2465          */
2466         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2467             return CM_ERROR_CROSSDEVLINK;
2468
2469         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2470             lock_ObtainMutex(&oldDscp->mx);
2471             cm_dnlcRemove(oldDscp, oldNamep);
2472             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2473                               CM_SCACHESYNC_STOREDATA);
2474             lock_ReleaseMutex(&oldDscp->mx);
2475             if (code == 0) {
2476                 lock_ObtainMutex(&newDscp->mx);
2477                 cm_dnlcRemove(newDscp, newNamep);
2478                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2479                                   CM_SCACHESYNC_STOREDATA);
2480                 lock_ReleaseMutex(&newDscp->mx);
2481                 if (code) {
2482                     /* cleanup first one */
2483                     cm_SyncOpDone(oldDscp, NULL,
2484                                    CM_SCACHESYNC_STOREDATA);
2485                 }       
2486             }
2487         }
2488         else {
2489             /* lock the new vnode entry first */
2490             lock_ObtainMutex(&newDscp->mx);
2491             cm_dnlcRemove(newDscp, newNamep);
2492             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2493                               CM_SCACHESYNC_STOREDATA);
2494             lock_ReleaseMutex(&newDscp->mx);
2495             if (code == 0) {
2496                 lock_ObtainMutex(&oldDscp->mx);
2497                 cm_dnlcRemove(oldDscp, oldNamep);
2498                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2499                                   CM_SCACHESYNC_STOREDATA);
2500                 lock_ReleaseMutex(&oldDscp->mx);
2501                 if (code) {
2502                     /* cleanup first one */
2503                     cm_SyncOpDone(newDscp, NULL,
2504                                    CM_SCACHESYNC_STOREDATA);
2505                 }       
2506             }
2507         }
2508     }   /* two distinct vnodes */
2509
2510     if (code) {
2511         return code;
2512     }
2513     didEnd = 0;
2514
2515     /* try the RPC now */
2516     do {
2517         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2518         if (code) 
2519             continue;
2520
2521         oldDirAFSFid.Volume = oldDscp->fid.volume;
2522         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2523         oldDirAFSFid.Unique = oldDscp->fid.unique;
2524         newDirAFSFid.Volume = newDscp->fid.volume;
2525         newDirAFSFid.Vnode = newDscp->fid.vnode;
2526         newDirAFSFid.Unique = newDscp->fid.unique;
2527
2528         callp = cm_GetRxConn(connp);
2529         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2530                              &newDirAFSFid, newNamep,
2531                              &updatedOldDirStatus, &updatedNewDirStatus,
2532                              &volSync);
2533         rx_PutConnection(callp);
2534
2535     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2536                          &volSync, NULL, NULL, code));
2537     code = cm_MapRPCError(code, reqp);
2538         
2539     /* update the individual stat cache entries for the directories */
2540     lock_ObtainMutex(&oldDscp->mx);
2541     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2542     if (code == 0) {
2543         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2544                         userp, 0);
2545     }
2546     lock_ReleaseMutex(&oldDscp->mx);
2547
2548     /* and update it for the new one, too, if necessary */
2549     if (!oneDir) {
2550         lock_ObtainMutex(&newDscp->mx);
2551         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2552         if (code == 0) {
2553             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2554                             userp, 0);
2555         }
2556         lock_ReleaseMutex(&newDscp->mx);
2557     }
2558
2559     /* and return error code */
2560     return code;
2561 }
2562
2563 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2564               LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2565               u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2566               void **lockpp)
2567 {
2568     long code;
2569     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2570     AFSFid tfid;
2571     AFSVolSync volSync;
2572     cm_conn_t *connp;
2573     cm_file_lock_t *fileLock;
2574     osi_queue_t *q;
2575     int found = 0;
2576     struct rx_connection * callp;
2577
2578     /* Look for a conflict.  Also, if we are asking for a shared lock,
2579      * look for another shared lock, so we don't have to do an RPC.
2580      */
2581     q = scp->fileLocks;
2582     while (q) {
2583         fileLock = (cm_file_lock_t *)
2584             ((char *) q - offsetof(cm_file_lock_t, fileq));
2585         if ((fileLock->flags &
2586               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2587              == 0) {
2588             if ((LockType & 0x1) == 0
2589                  || (fileLock->LockType & 0x1) == 0)
2590                 return CM_ERROR_WOULDBLOCK;
2591             found = 1;
2592         }
2593         q = osi_QNext(q);
2594     }
2595
2596     if (found)
2597         code = 0;
2598     else {
2599         tfid.Volume = scp->fid.volume;
2600         tfid.Vnode = scp->fid.vnode;
2601         tfid.Unique = scp->fid.unique;
2602         lock_ReleaseMutex(&scp->mx);
2603         do {
2604             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2605             if (code) 
2606                 break;
2607
2608             callp = cm_GetRxConn(connp);
2609             code = RXAFS_SetLock(callp, &tfid, Which,
2610                                   &volSync);
2611             rx_PutConnection(callp);
2612
2613         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2614                              NULL, NULL, code));
2615         lock_ObtainMutex(&scp->mx);
2616         code = cm_MapRPCError(code, reqp);
2617     }
2618
2619     if (code == 0 || Timeout != 0) {
2620         fileLock = malloc(sizeof(cm_file_lock_t));
2621         fileLock->LockType = LockType;
2622         cm_HoldUser(userp);
2623         fileLock->userp = userp;
2624         fileLock->fid = scp->fid;
2625         fileLock->LOffset = LOffset;
2626         fileLock->LLength = LLength;
2627         fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2628         osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2629         lock_ObtainWrite(&cm_scacheLock);
2630         osi_QAdd(&cm_allFileLocks, &fileLock->q);
2631         lock_ReleaseWrite(&cm_scacheLock);
2632         if (code != 0) 
2633             *lockpp = fileLock;
2634     }
2635     return code;
2636 }
2637
2638 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2639                 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2640                 cm_user_t *userp, cm_req_t *reqp)
2641 {
2642     long code = 0;
2643     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2644     AFSFid tfid;
2645     AFSVolSync volSync;
2646     cm_conn_t *connp;
2647     cm_file_lock_t *fileLock, *ourLock;
2648     osi_queue_t *q, *qq;
2649     int anotherReader = 0;
2650     int smallLock = 0;
2651     int found = 0;
2652     struct rx_connection * callp;
2653
2654     if (LargeIntegerLessThan(LLength, scp->length))
2655         smallLock = 1;
2656
2657     /* Look for our own lock on the list, so as to remove it.
2658      * Also, determine if we're the last reader; if not, avoid an RPC.
2659      */
2660     q = scp->fileLocks;
2661     while (q) {
2662         fileLock = (cm_file_lock_t *)
2663             ((char *) q - offsetof(cm_file_lock_t, fileq));
2664         if (!found
2665              && fileLock->userp == userp
2666              && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2667              && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2668             found = 1;
2669             ourLock = fileLock;
2670             qq = q;
2671         }
2672         else if (fileLock->LockType & 0x1)
2673             anotherReader = 1;
2674         q = osi_QNext(q);
2675     }
2676
2677     /* ignore byte ranges */
2678     if (smallLock && !found)
2679         return 0;
2680
2681     /* don't try to unlock other people's locks */
2682     if (!found)
2683         return CM_ERROR_WOULDBLOCK;
2684
2685     /* discard lock record */
2686     osi_QRemove(&scp->fileLocks, qq);
2687     /*
2688      * Don't delete it here; let the daemon delete it, to simplify
2689      * the daemon's traversal of the list.
2690      */
2691     lock_ObtainWrite(&cm_scacheLock);
2692     ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2693     cm_ReleaseUser(ourLock->userp);
2694     lock_ReleaseWrite(&cm_scacheLock);
2695
2696     if (!anotherReader) {
2697         tfid.Volume = scp->fid.volume;
2698         tfid.Vnode = scp->fid.vnode;
2699         tfid.Unique = scp->fid.unique;
2700         lock_ReleaseMutex(&scp->mx);
2701         do {
2702             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2703             if (code) 
2704                 break;
2705
2706             callp = cm_GetRxConn(connp);
2707             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2708             rx_PutConnection(callp);
2709
2710         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2711                              NULL, NULL, code));
2712         code = cm_MapRPCError(code, reqp);
2713         lock_ObtainMutex(&scp->mx);
2714     }
2715
2716     return code;
2717 }
2718
2719 void cm_CheckLocks()
2720 {
2721     osi_queue_t *q, *nq;
2722     cm_file_lock_t *fileLock;
2723     cm_req_t req;
2724     AFSFid tfid;
2725     AFSVolSync volSync;
2726     cm_conn_t *connp;
2727     long code;
2728     struct rx_connection * callp;
2729
2730     cm_InitReq(&req);
2731
2732     lock_ObtainWrite(&cm_scacheLock);
2733     q = cm_allFileLocks;
2734     while (q) {
2735         fileLock = (cm_file_lock_t *) q;
2736         nq = osi_QNext(q);
2737         if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2738             osi_QRemove(&cm_allFileLocks, q);
2739             free(fileLock);
2740         }
2741         else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2742             tfid.Volume = fileLock->fid.volume;
2743             tfid.Vnode = fileLock->fid.vnode;
2744             tfid.Unique = fileLock->fid.unique;
2745             lock_ReleaseWrite(&cm_scacheLock);
2746             do {
2747                 code = cm_Conn(&fileLock->fid, fileLock->userp,
2748                                 &req, &connp);
2749                 if (code) 
2750                     break;
2751
2752                 callp = cm_GetRxConn(connp);
2753                 code = RXAFS_ExtendLock(callp, &tfid,
2754                                          &volSync);
2755                 rx_PutConnection(callp);
2756
2757             } while (cm_Analyze(connp, fileLock->userp, &req,
2758                                  &fileLock->fid, &volSync, NULL, NULL,
2759                                  code));
2760             code = cm_MapRPCError(code, &req);
2761             lock_ObtainWrite(&cm_scacheLock);
2762         }
2763         q = nq;
2764     }
2765     lock_ReleaseWrite(&cm_scacheLock);
2766 }       
2767
2768 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2769 {
2770     long code;
2771     int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2772     cm_scache_t *scp;
2773     AFSFid tfid;
2774     AFSVolSync volSync;
2775     cm_conn_t *connp;
2776     cm_file_lock_t *fileLock;
2777     osi_queue_t *q;
2778     cm_req_t req;
2779     int found = 0;
2780     struct rx_connection * callp;
2781
2782     if (vcp_is_dead) {
2783         code = CM_ERROR_TIMEDOUT;
2784         goto handleCode;
2785     }
2786
2787     cm_InitReq(&req);
2788
2789     /* Look for a conflict.  Also, if we are asking for a shared lock,
2790      * look for another shared lock, so we don't have to do an RPC.
2791      */
2792     code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2793     if (code)
2794         return code;
2795
2796     q = scp->fileLocks;
2797     while (q) {
2798         fileLock = (cm_file_lock_t *)
2799             ((char *) q - offsetof(cm_file_lock_t, fileq));
2800         if ((fileLock->flags &
2801               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2802              == 0) {
2803             if ((oldFileLock->LockType & 0x1) == 0
2804                  || (fileLock->LockType & 0x1) == 0) {
2805                 cm_ReleaseSCache(scp);
2806                 return CM_ERROR_WOULDBLOCK;
2807             }
2808             found = 1;
2809         }
2810         q = osi_QNext(q);
2811     }
2812
2813     if (found)
2814         code = 0;
2815     else {
2816         tfid.Volume = oldFileLock->fid.volume;
2817         tfid.Vnode = oldFileLock->fid.vnode;
2818         tfid.Unique = oldFileLock->fid.unique;
2819         do {
2820             code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2821                             &req, &connp);
2822             if (code) 
2823                 break;
2824
2825             callp = cm_GetRxConn(connp);
2826             code = RXAFS_SetLock(callp, &tfid, Which,
2827                                   &volSync);
2828             rx_PutConnection(callp);
2829
2830         } while (cm_Analyze(connp, oldFileLock->userp, &req,
2831                              &oldFileLock->fid, &volSync,
2832                              NULL, NULL, code));
2833         code = cm_MapRPCError(code, &req);
2834     }
2835
2836   handleCode:
2837     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2838         lock_ObtainMutex(&scp->mx);
2839         osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2840         lock_ReleaseMutex(&scp->mx);
2841     }
2842     lock_ObtainWrite(&cm_scacheLock);
2843     if (code == 0)
2844         oldFileLock->flags = 0;
2845     else if (code != CM_ERROR_WOULDBLOCK) {
2846         oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2847         cm_ReleaseUser(oldFileLock->userp);
2848         oldFileLock->userp = NULL;
2849     }
2850     lock_ReleaseWrite(&cm_scacheLock);
2851
2852     return code;
2853 }