windows-combined-20041010
[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 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
432                   osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
433                   cm_scache_t **retscp)
434 {
435     char *tp;
436     long code;
437     cm_dirEntry_t *dep;
438     cm_buf_t *bufferp;
439     long temp;
440     osi_hyper_t dirLength;
441     osi_hyper_t bufferOffset;
442     osi_hyper_t curOffset;
443     osi_hyper_t thyper;
444     long entryInDir;
445     long entryInBuffer;
446     cm_pageHeader_t *pageHeaderp;
447     int slotInPage;
448     long nextEntryCookie;
449     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
450         
451     /* get the directory size */
452     lock_ObtainMutex(&scp->mx);
453     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
454                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
455     if (code) {
456         lock_ReleaseMutex(&scp->mx);
457         return code;
458     }
459         
460     if (scp->fileType != CM_SCACHETYPE_DIRECTORY) {
461         lock_ReleaseMutex(&scp->mx);
462         return CM_ERROR_NOTDIR;
463     }   
464
465     if (retscp)                         /* if this is a lookup call */
466     {
467         cm_lookupSearch_t*      sp = parmp;
468         int casefold = sp->caseFold;
469
470         sp->caseFold = 0; /* we have a strong preference for exact matches */
471         if ( *retscp = cm_dnlcLookup(scp, sp))  /* dnlc hit */
472         {
473             sp->caseFold = casefold;
474             lock_ReleaseMutex(&scp->mx);
475             return 0;
476         }
477
478         sp->caseFold = casefold;
479     }   
480
481     /*
482      * XXX We only get the length once.  It might change when we drop the
483      * lock.
484      */
485     dirLength = scp->length;
486
487     lock_ReleaseMutex(&scp->mx);
488
489     bufferp = NULL;
490     bufferOffset.LowPart = bufferOffset.HighPart = 0;
491     if (startOffsetp)
492         curOffset = *startOffsetp;
493     else {
494         curOffset.HighPart = 0;
495         curOffset.LowPart = 0;
496     }   
497
498     while (1) {
499         /* make sure that curOffset.LowPart doesn't point to the first
500          * 32 bytes in the 2nd through last dir page, and that it
501          * doesn't point at the first 13 32-byte chunks in the first
502          * dir page, since those are dir and page headers, and don't
503          * contain useful information.
504          */
505         temp = curOffset.LowPart & (2048-1);
506         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
507             /* we're in the first page */
508             if (temp < 13*32) temp = 13*32;
509         }
510         else {
511             /* we're in a later dir page */
512             if (temp < 32) temp = 32;
513         }       
514                 
515         /* make sure the low order 5 bits are zero */
516         temp &= ~(32-1);
517                 
518         /* now put temp bits back ito curOffset.LowPart */
519         curOffset.LowPart &= ~(2048-1);
520         curOffset.LowPart |= temp;
521
522         /* check if we've passed the dir's EOF */
523         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
524             break;
525                 
526         /* see if we can use the bufferp we have now; compute in which
527          * page the current offset would be, and check whether that's
528          * the offset of the buffer we have.  If not, get the buffer.
529          */
530         thyper.HighPart = curOffset.HighPart;
531         thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
532         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
533             /* wrong buffer */
534             if (bufferp) {
535                 lock_ReleaseMutex(&bufferp->mx);
536                 buf_Release(bufferp);
537                 bufferp = NULL;
538             }
539
540             lock_ObtainRead(&scp->bufCreateLock);
541             code = buf_Get(scp, &thyper, &bufferp);
542             lock_ReleaseRead(&scp->bufCreateLock);
543
544             lock_ObtainMutex(&bufferp->mx);
545             if (code) 
546                 break;
547             bufferOffset = thyper;
548
549             /* now get the data in the cache */
550             while (1) {
551                 lock_ObtainMutex(&scp->mx);
552                 code = cm_SyncOp(scp, bufferp, userp, reqp,
553                                   PRSFS_LOOKUP,
554                                   CM_SCACHESYNC_NEEDCALLBACK
555                                   | CM_SCACHESYNC_READ
556                                   | CM_SCACHESYNC_BUFLOCKED);
557                 if (code) {
558                     lock_ReleaseMutex(&scp->mx);
559                     break;
560                 }
561                                 
562                 if (cm_HaveBuffer(scp, bufferp, 1)) {
563                     lock_ReleaseMutex(&scp->mx);
564                     break;
565                 }
566
567                 /* otherwise, load the buffer and try again */
568                 lock_ReleaseMutex(&bufferp->mx);
569                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
570                                     reqp);
571                 lock_ReleaseMutex(&scp->mx);
572                 lock_ObtainMutex(&bufferp->mx);
573                 if (code) 
574                     break;
575             }
576             if (code) {
577                 lock_ReleaseMutex(&bufferp->mx);
578                 buf_Release(bufferp);
579                 bufferp = NULL;
580                 break;
581             }
582         }       /* if (wrong buffer) ... */
583                 
584         /* now we have the buffer containing the entry we're interested
585          * in; copy it out if it represents a non-deleted entry.
586          */
587         entryInDir = curOffset.LowPart & (2048-1);
588         entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
589
590         /* page header will help tell us which entries are free.  Page
591          * header can change more often than once per buffer, since
592          * AFS 3 dir page size may be less than (but not more than) a
593          * buffer package buffer.
594          */
595         /* only look intra-buffer */
596         temp = curOffset.LowPart & (buf_bufferSize - 1);
597         temp &= ~(2048 - 1);    /* turn off intra-page bits */
598         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
599
600         /* now determine which entry we're looking at in the page.  If
601          * it is free (there's a free bitmap at the start of the dir),
602          * we should skip these 32 bytes.
603          */
604         slotInPage = (entryInDir & 0x7e0) >> 5;
605         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
606                & (1 << (slotInPage & 0x7)))) {
607             /* this entry is free */
608             numDirChunks = 1;   /* only skip this guy */
609             goto nextEntry;
610         }
611
612         tp = bufferp->datap + entryInBuffer;
613         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
614
615         /* while we're here, compute the next entry's location, too,
616          * since we'll need it when writing out the cookie into the
617          * dir listing stream.
618          */
619         numDirChunks = cm_NameEntries(dep->name, NULL);
620                 
621         /* compute the offset of the cookie representing the next entry */
622         nextEntryCookie = curOffset.LowPart
623             + (CM_DIR_CHUNKSIZE * numDirChunks);
624
625         if (dep->fid.vnode != 0) {
626             /* this is one of the entries to use: it is not deleted */
627             code = (*funcp)(scp, dep, parmp, &curOffset);
628             if (code) 
629                 break;
630         }       /* if we're including this name */
631                 
632       nextEntry:
633         /* and adjust curOffset to be where the new cookie is */
634         thyper.HighPart = 0;
635         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
636         curOffset = LargeIntegerAdd(thyper, curOffset);
637     }           /* while copying data for dir listing */
638
639     /* release the mutex */
640     if (bufferp) {
641         lock_ReleaseMutex(&bufferp->mx);
642         buf_Release(bufferp);
643     }
644     return code;
645 }
646
647 int cm_NoneUpper(char *s)
648 {
649     char c;
650     while (c = *s++)
651         if (c >= 'A' && c <= 'Z')
652             return 0;
653     return 1;
654 }
655
656 int cm_NoneLower(char *s)
657 {
658     char c;
659     while (c = *s++)
660         if (c >= 'a' && c <= 'z')
661             return 0;
662     return 1;
663 }
664
665 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
666                           osi_hyper_t *offp)
667 {
668     cm_lookupSearch_t *sp;
669     int match;
670     char shortName[13];
671     char *matchName;
672
673     sp = (cm_lookupSearch_t *) rockp;
674
675     matchName = dep->name;
676     if (sp->caseFold)
677         match = cm_stricmp(matchName, sp->searchNamep);
678     else
679         match = strcmp(matchName, sp->searchNamep);
680
681     if (match != 0
682          && sp->hasTilde
683          && !cm_Is8Dot3(dep->name)) {
684         matchName = shortName;
685         cm_Gen8Dot3Name(dep, shortName, NULL);
686         if (sp->caseFold)
687             match = cm_stricmp(matchName, sp->searchNamep);
688         else
689             match = strcmp(matchName, sp->searchNamep);
690     }       
691
692     if (match != 0)
693         return 0;
694
695     sp->found = 1;
696     if(!sp->caseFold) 
697         sp->ExactFound = 1;
698
699     if (!sp->caseFold || matchName == shortName) {
700         sp->fid.vnode = ntohl(dep->fid.vnode);
701         sp->fid.unique = ntohl(dep->fid.unique);
702         return CM_ERROR_STOPNOW;
703     }
704
705     /*
706      * If we get here, we are doing a case-insensitive search, and we
707      * have found a match.  Now we determine what kind of match it is:
708      * exact, lower-case, upper-case, or none of the above.  This is done
709      * in order to choose among matches, if there are more than one.
710      */
711
712     /* Exact matches are the best. */
713     match = strcmp(matchName, sp->searchNamep);
714     if (match == 0) {
715         sp->ExactFound = 1;
716         sp->fid.vnode = ntohl(dep->fid.vnode);
717         sp->fid.unique = ntohl(dep->fid.unique);
718         return CM_ERROR_STOPNOW;
719     }
720
721     /* Lower-case matches are next. */
722     if (sp->LCfound)
723         return 0;
724     if (cm_NoneUpper(matchName)) {
725         sp->LCfound = 1;
726         goto inexact;
727     }
728
729     /* Upper-case matches are next. */
730     if (sp->UCfound)
731         return 0;
732     if (cm_NoneLower(matchName)) {
733         sp->UCfound = 1;
734         goto inexact;
735     }
736
737     /* General matches are last. */
738     if (sp->NCfound)
739         return 0;
740     sp->NCfound = 1;
741
742   inexact:
743     sp->fid.vnode = ntohl(dep->fid.vnode);
744     sp->fid.unique = ntohl(dep->fid.unique);
745     return 0;
746 }       
747
748 /* read the contents of a mount point into the appropriate string.
749  * called with locked scp, and returns with locked scp.
750  */
751 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
752 {
753     long code;
754     cm_buf_t *bufp;
755     osi_hyper_t thyper;
756     int tlen;
757
758     if (scp->mountPointStringp) 
759         return 0;
760         
761     /* otherwise, we have to read it in */
762     lock_ReleaseMutex(&scp->mx);
763
764     lock_ObtainRead(&scp->bufCreateLock);
765     thyper.LowPart = thyper.HighPart = 0;
766     code = buf_Get(scp, &thyper, &bufp);
767     lock_ReleaseRead(&scp->bufCreateLock);
768
769     lock_ObtainMutex(&scp->mx);
770     if (code) {
771         return code;
772     }
773     while (1) {
774         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
775                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
776         if (code) {
777             goto done;
778         }
779
780         if (cm_HaveBuffer(scp, bufp, 0)) 
781             break;
782
783         /* otherwise load buffer */
784         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
785         if (code) {
786             goto done;
787         }
788     }
789     /* locked, has callback, has valid data in buffer */
790     if ((tlen = scp->length.LowPart) > 1000) return CM_ERROR_TOOBIG;
791     if (tlen <= 0) {
792         code = CM_ERROR_INVAL;
793         goto done;
794     }
795
796     /* someone else did the work while we were out */
797     if (scp->mountPointStringp) {
798         code = 0;
799         goto done;
800     }
801
802     /* otherwise, copy out the link */
803     scp->mountPointStringp = malloc(tlen);
804     memcpy(scp->mountPointStringp, bufp->datap, tlen);
805
806     /* now make it null-terminated.  Note that the original contents of a
807      * link that is a mount point is "#volname." where "." is there just to
808      * be turned into a null.  That is, we can trash the last char of the
809      * link without damaging the vol name.  This is a stupid convention,
810      * but that's the protocol.
811      */
812     scp->mountPointStringp[tlen-1] = 0;
813     code = 0;
814
815   done:
816     if (bufp) 
817         buf_Release(bufp);
818     return code;
819 }
820
821 /* called with a locked scp and chases the mount point, yielding outScpp.
822  * scp remains locked, just for simplicity of describing the interface.
823  */
824 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
825                           cm_req_t *reqp, cm_scache_t **outScpp)
826 {
827     char *cellNamep;
828     char *volNamep;
829     int tlen;
830     long code;
831     char *cp;
832     char *mpNamep;
833     cm_volume_t *volp;
834     cm_cell_t *cellp;
835     char mtType;
836     cm_fid_t tfid;
837     size_t vnLength;
838     int type;
839
840     if (scp->mountRootFidp && scp->mountRootGen >= cm_mountRootGen) {
841         tfid = *scp->mountRootFidp;
842         lock_ReleaseMutex(&scp->mx);
843         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
844         lock_ObtainMutex(&scp->mx);
845         return code;
846     }
847
848     /* parse the volume name */
849     mpNamep = scp->mountPointStringp;
850     osi_assert(mpNamep);
851     tlen = strlen(scp->mountPointStringp);
852     mtType = *scp->mountPointStringp;
853     cellNamep = malloc(tlen);
854     volNamep = malloc(tlen);
855
856     cp = strrchr(mpNamep, ':');
857     if (cp) {
858         /* cellular mount point */
859         memset(cellNamep, 0, tlen);
860         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
861         strcpy(volNamep, cp+1);
862         /* now look up the cell */
863         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
864     }
865     else {
866         /* normal mt pt */
867         strcpy(volNamep, mpNamep+1);
868
869         cellp = cm_FindCellByID(scp->fid.cell);
870     }
871
872     if (!cellp) {
873         code = CM_ERROR_NOSUCHCELL;
874         goto done;
875     }
876
877     vnLength = strlen(volNamep);
878     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
879         type = BACKVOL;
880     else if (vnLength >= 10
881               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
882         type = ROVOL;
883     else
884         type = RWVOL;
885
886     /* check for backups within backups */
887     if (type == BACKVOL
888          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
889          == CM_SCACHEFLAG_RO) {
890         code = CM_ERROR_NOSUCHVOLUME;
891         goto done;
892     }
893
894     /* now we need to get the volume */
895     lock_ReleaseMutex(&scp->mx);
896     code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 0, &volp);
897     lock_ObtainMutex(&scp->mx);
898         
899     if (code == 0) {
900         /* save the parent of the volume root for this is the 
901          * place where the volume is mounted and we must remember 
902          * this in the volume structure rather than just in the 
903          * scache entry lest the scache entry gets recycled 
904          * (defect 11489)
905          */
906         lock_ObtainMutex(&volp->mx);
907         if(volp->dotdotFidp == (cm_fid_t *) NULL) 
908             volp->dotdotFidp = (cm_fid_t *) malloc(sizeof(cm_fid_t));
909         *(volp->dotdotFidp) = dscp->fid;
910         lock_ReleaseMutex(&volp->mx);
911
912         if (scp->mountRootFidp == 0) {
913             scp->mountRootFidp = malloc(sizeof(cm_fid_t));
914         }
915         scp->mountRootFidp->cell = cellp->cellID;
916         /* if the mt pt is in a read-only volume (not just a
917          * backup), and if there is a read-only volume for the
918          * target, and if this is a type '#' mount point, use
919          * the read-only, otherwise use the one specified.
920          */
921         if (mtType == '#' && (scp->flags & CM_SCACHEFLAG_PURERO)
922              && volp->roID != 0 && type == RWVOL)
923             type = ROVOL;
924         if (type == ROVOL)
925             scp->mountRootFidp->volume = volp->roID;
926         else if (type == BACKVOL)
927             scp->mountRootFidp->volume = volp->bkID;
928         else
929             scp->mountRootFidp->volume = volp->rwID;
930
931         /* the rest of the fid is a magic number */
932         scp->mountRootFidp->vnode = 1;
933         scp->mountRootFidp->unique = 1;
934         scp->mountRootGen = cm_mountRootGen;
935
936         tfid = *scp->mountRootFidp;
937         lock_ReleaseMutex(&scp->mx);
938         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
939         lock_ObtainMutex(&scp->mx);
940     }
941
942   done:
943     free(cellNamep);
944     free(volNamep);
945     return code;
946 }       
947
948 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
949                        cm_req_t *reqp, cm_scache_t **outpScpp)
950 {
951     long code;
952     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
953     cm_scache_t *tscp = NULL;
954     cm_scache_t *mountedScp;
955     cm_lookupSearch_t rock;
956     int getroot;
957
958     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
959          && strcmp(namep, "..") == 0) {
960         if (dscp->dotdotFidp == (cm_fid_t *)NULL
961              || dscp->dotdotFidp->volume == 0)
962             return CM_ERROR_NOSUCHVOLUME;
963         rock.fid = *dscp->dotdotFidp;
964         goto haveFid;
965     }
966
967     memset(&rock, 0, sizeof(rock));
968     rock.fid.cell = dscp->fid.cell;
969     rock.fid.volume = dscp->fid.volume;
970     rock.searchNamep = namep;
971     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
972     rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
973
974     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
975     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
976                         (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
977
978     /* code == 0 means we fell off the end of the dir, while stopnow means
979      * that we stopped early, probably because we found the entry we're
980      * looking for.  Any other non-zero code is an error.
981      */
982     if (code && code != CM_ERROR_STOPNOW) 
983         return code;
984
985     getroot = (dscp==cm_rootSCachep) ;
986     if (!rock.found) {
987         if (!cm_freelanceEnabled || !getroot) {
988             if (flags & CM_FLAG_CHECKPATH)
989                 return CM_ERROR_NOSUCHPATH;
990             else
991                 return CM_ERROR_NOSUCHFILE;
992         }
993         else {  /* nonexistent dir on freelance root, so add it */
994             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
995                       osi_LogSaveString(afsd_logp,namep));
996             code = cm_FreelanceAddMount(namep, namep, "root.cell.", namep[0] == '.', &rock.fid);
997             if (code < 0) {   /* add mount point failed, so give up */
998                 if (flags & CM_FLAG_CHECKPATH)
999                     return CM_ERROR_NOSUCHPATH;
1000                 else
1001                     return CM_ERROR_NOSUCHFILE;
1002             }
1003             tscp = NULL;   /* to force call of cm_GetSCache */
1004         }
1005     }
1006
1007   haveFid:       
1008     if ( !tscp )    /* we did not find it in the dnlc */
1009     {
1010         dnlcHit = 0;    
1011         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1012         if (code) 
1013             return code;
1014     }       
1015     /* tscp is now held */
1016
1017     lock_ObtainMutex(&tscp->mx);
1018     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1019                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1020     if (code) { 
1021         lock_ReleaseMutex(&tscp->mx);
1022         cm_ReleaseSCache(tscp);
1023         return code;
1024     }
1025     /* tscp is now locked */
1026
1027     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1028          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1029         /* mount points are funny: they have a volume name to mount
1030          * the root of.
1031          */
1032         code = cm_ReadMountPoint(tscp, userp, reqp);
1033         if (code == 0)
1034             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1035                                         &mountedScp);
1036         lock_ReleaseMutex(&tscp->mx);
1037         cm_ReleaseSCache(tscp);
1038         if (code) {
1039             return code;
1040         }
1041         tscp = mountedScp;
1042     }
1043     else {
1044         lock_ReleaseMutex(&tscp->mx);
1045     }
1046
1047     /* copy back pointer */
1048     *outpScpp = tscp;
1049
1050     /* insert scache in dnlc */
1051     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1052         /* lock the directory entry to prevent racing callback revokes */
1053         lock_ObtainMutex(&dscp->mx);
1054         if ( dscp->cbServerp && dscp->cbExpires )
1055             cm_dnlcEnter(dscp, namep, tscp);
1056         lock_ReleaseMutex(&dscp->mx);
1057     }
1058
1059     /* and return */
1060     return 0;
1061 }
1062
1063 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1064 {
1065     char *tp;
1066     int prefixCount;
1067
1068     tp = strrchr(inp, '@');
1069     if (tp == NULL) 
1070         return 0;               /* no @sys */
1071
1072     if (strcmp(tp, "@sys") != 0) 
1073         return 0;       /* no @sys */
1074
1075     /* caller just wants to know if this is a valid @sys type of name */
1076     if (outp == NULL) 
1077         return 1;
1078
1079     if (index >= MAXNUMSYSNAMES)
1080         return -1;
1081
1082     /* otherwise generate the properly expanded @sys name */
1083     prefixCount = tp - inp;
1084
1085     strncpy(outp, inp, prefixCount);    /* copy out "a." from "a.@sys" */
1086     outp[prefixCount] = 0;              /* null terminate the "a." */
1087     strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1088     return 1;
1089 }   
1090
1091 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1092                cm_req_t *reqp, cm_scache_t **outpScpp)
1093 {
1094     long code;
1095     char tname[256];
1096     int sysNameIndex = 0;
1097     cm_scache_t *scp = 0;
1098
1099     for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1100         code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1101         if (code > 0) {
1102             code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1103             if (code == 0) {
1104                 *outpScpp = scp;
1105                 return 0;
1106             }
1107                         if (scp) {
1108                                 cm_ReleaseSCache(scp);
1109                                 scp = 0;
1110                         }
1111         } else {
1112             return cm_LookupInternal(dscp, namep, flags, userp, reqp, outpScpp);
1113         }
1114     }
1115
1116     /* None of the possible sysName expansions could be found */
1117     if (flags & CM_FLAG_CHECKPATH)
1118         return CM_ERROR_NOSUCHPATH;
1119     else
1120         return CM_ERROR_NOSUCHFILE;
1121 }
1122
1123 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1124 {
1125     long code;
1126     cm_conn_t *connp;
1127     AFSFid afsFid;
1128     int sflags;
1129     AFSFetchStatus newDirStatus;
1130     AFSVolSync volSync;
1131
1132 #ifdef AFS_FREELANCE_CLIENT
1133     if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1134         /* deleting a mount point from the root dir. */
1135         code = cm_FreelanceRemoveMount(namep);
1136         return code;
1137     }
1138 #endif  
1139
1140     /* make sure we don't screw up the dir status during the merge */
1141     lock_ObtainMutex(&dscp->mx);
1142     sflags = CM_SCACHESYNC_STOREDATA;
1143     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1144     lock_ReleaseMutex(&dscp->mx);
1145     if (code) 
1146         return code;
1147
1148     /* make the RPC */
1149     afsFid.Volume = dscp->fid.volume;
1150     afsFid.Vnode = dscp->fid.vnode;
1151     afsFid.Unique = dscp->fid.unique;
1152     do {
1153         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1154         if (code) 
1155             continue;
1156
1157         lock_ObtainMutex(&connp->mx);
1158         code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1159                                  &newDirStatus, &volSync);
1160         lock_ReleaseMutex(&connp->mx);
1161     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1162     code = cm_MapRPCError(code, reqp);
1163
1164     lock_ObtainMutex(&dscp->mx);
1165     cm_dnlcRemove(dscp, namep);
1166     cm_SyncOpDone(dscp, NULL, sflags);
1167     if (code == 0) 
1168         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1169     lock_ReleaseMutex(&dscp->mx);
1170
1171     return code;
1172 }
1173
1174 /* called with a locked vnode, and fills in the link info.
1175  * returns this the vnode still locked.
1176  */
1177 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1178 {
1179     long code;
1180     cm_buf_t *bufp;
1181     long temp;
1182     osi_hyper_t thyper;
1183
1184     lock_AssertMutex(&linkScp->mx);
1185     if (!linkScp->mountPointStringp) {
1186         /* read the link data */
1187         lock_ReleaseMutex(&linkScp->mx);
1188         thyper.LowPart = thyper.HighPart = 0;
1189         code = buf_Get(linkScp, &thyper, &bufp);
1190         lock_ObtainMutex(&linkScp->mx);
1191         if (code) 
1192             return code;
1193         while (1) {
1194             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1195                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1196             if (code) {
1197                 buf_Release(bufp);
1198                 return code;
1199             }
1200             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1201                 break;
1202
1203             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1204             if (code) {
1205                 buf_Release(bufp);
1206                 return code;
1207             }
1208         } /* while loop to get the data */
1209                 
1210         /* now if we still have no link read in,
1211          * copy the data from the buffer */
1212         if ((temp = linkScp->length.LowPart) >= 1024) {
1213             buf_Release(bufp);
1214             return CM_ERROR_TOOBIG;
1215         }
1216
1217         /* otherwise, it fits; make sure it is still null (could have
1218          * lost race with someone else referencing this link above),
1219          * and if so, copy in the data.
1220          */
1221         if (linkScp->mountPointStringp == NULL) {
1222             linkScp->mountPointStringp = malloc(temp+1);
1223             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1224             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1225         }
1226         buf_Release(bufp);
1227     }   /* don't have sym link contents cached */
1228
1229     return 0;
1230 }       
1231
1232 /* called with a held vnode and a path suffix, with the held vnode being a
1233  * symbolic link.  Our goal is to generate a new path to interpret, and return
1234  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1235  * other than the directory containing the symbolic link, then the new root is
1236  * returned in *newRootScpp, otherwise a null is returned there.
1237  */
1238 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1239                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1240                       cm_user_t *userp, cm_req_t *reqp)
1241 {
1242     long code;
1243     char *linkp;
1244     cm_space_t *tsp;
1245
1246     lock_ObtainMutex(&linkScp->mx);
1247     code = cm_HandleLink(linkScp, userp, reqp);
1248     if (code) 
1249         goto done;
1250
1251     /* if we may overflow the buffer, bail out; buffer is signficantly
1252      * bigger than max path length, so we don't really have to worry about
1253      * being a little conservative here.
1254      */
1255     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1256          >= CM_UTILS_SPACESIZE)
1257         return CM_ERROR_TOOBIG;
1258
1259     tsp = cm_GetSpace();
1260     linkp = linkScp->mountPointStringp;
1261     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1262         if (strlen(linkp) > cm_mountRootLen)
1263             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1264         else
1265             tsp->data[0] = 0;
1266         *newRootScpp = cm_rootSCachep;
1267         cm_HoldSCache(cm_rootSCachep);
1268     } else if (*linkp == '\\' || *linkp == '/') {
1269         /* formerly, this was considered to be from the AFS root,
1270          * but this seems to create problems.  instead, we will just
1271          * reject the link */
1272 #if 0   
1273         strcpy(tsp->data, linkp+1);
1274         *newRootScpp = cm_rootSCachep;
1275         cm_HoldSCache(cm_rootSCachep);
1276 #else
1277         code = CM_ERROR_NOSUCHPATH;
1278         goto done;
1279 #endif  
1280     }
1281     else {
1282         /* a relative link */
1283         strcpy(tsp->data, linkp);
1284         *newRootScpp = NULL;
1285     }
1286     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1287         strcat(tsp->data, "\\");
1288         strcat(tsp->data, pathSuffixp);
1289     }
1290     *newSpaceBufferp = tsp;
1291     code = 0;
1292
1293   done:
1294     lock_ReleaseMutex(&linkScp->mx);
1295     return code;
1296 }
1297
1298 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1299                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1300 {
1301     long code;
1302     char *tp;                   /* ptr moving through input buffer */
1303     char tc;                    /* temp char */
1304     int haveComponent;          /* has new component started? */
1305     char component[256];                /* this is the new component */
1306     char *cp;                   /* component name being assembled */
1307     cm_scache_t *tscp;          /* current location in the hierarchy */
1308     cm_scache_t *nscp;          /* next dude down */
1309     cm_scache_t *dirScp;                /* last dir we searched */
1310     cm_scache_t *linkScp;               /* new root for the symlink we just
1311     * looked up */
1312     cm_space_t *psp;            /* space for current path, if we've hit
1313     * any symlinks */
1314     cm_space_t *tempsp;         /* temp vbl */
1315     char *restp;                        /* rest of the pathname to interpret */
1316     int symlinkCount;           /* count of # of symlinks traversed */
1317     int extraFlag;                      /* avoid chasing mt pts for dir cmd */
1318     int phase = 1;                      /* 1 = tidPathp, 2 = pathp */
1319
1320     tp = tidPathp;
1321     if (tp == NULL) {
1322         tp = pathp;
1323         phase = 2;
1324     }
1325     if (tp == NULL) {
1326         tp = "";
1327     }
1328     haveComponent = 0;
1329     psp = NULL;
1330     tscp = rootSCachep;
1331     cm_HoldSCache(tscp);
1332     symlinkCount = 0;
1333     while (1) {
1334         tc = *tp++;
1335
1336         /* map Unix slashes into DOS ones so we can interpret Unix
1337          * symlinks properly
1338          */
1339         if (tc == '/') 
1340             tc = '\\';
1341
1342         if (!haveComponent) {
1343             if (tc == '\\') 
1344                 continue;
1345             else if (tc == 0) {
1346                 if (phase == 1) {
1347                     phase = 2;
1348                     tp = pathp;
1349                     continue;
1350                 }
1351                 code = 0;
1352                 break;
1353             }
1354             else {
1355                 haveComponent = 1;
1356                 cp = component;
1357                 *cp++ = tc;
1358             }
1359         }
1360         else {
1361             /* we have a component here */
1362             if (tc == 0 || tc == '\\') {
1363                 /* end of the component; we're at the last
1364                  * component if tc == 0.  However, if the last
1365                  * is a symlink, we have more to do.
1366                  */
1367                 *cp++ = 0;      /* add null termination */
1368                 extraFlag = 0;
1369                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1370                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1371                 code = cm_Lookup(tscp, component,
1372                                   flags | extraFlag,
1373                                   userp, reqp, &nscp);
1374
1375                 if (code) {
1376                     cm_ReleaseSCache(tscp);
1377                     if (psp) 
1378                         cm_FreeSpace(psp);
1379                     return code;
1380                 }
1381                 haveComponent = 0;      /* component done */
1382                 dirScp = tscp;          /* for some symlinks */
1383                 tscp = nscp;    /* already held */
1384                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1385                     code = 0;
1386                     cm_ReleaseSCache(dirScp);
1387                     break;
1388                 }
1389
1390                 /* now, if tscp is a symlink, we should follow
1391                  * it and assemble the path again.
1392                  */
1393                 lock_ObtainMutex(&tscp->mx);
1394                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1395                                   CM_SCACHESYNC_GETSTATUS
1396                                   | CM_SCACHESYNC_NEEDCALLBACK);
1397                 if (code) {
1398                     lock_ReleaseMutex(&tscp->mx);
1399                     cm_ReleaseSCache(tscp);
1400                     cm_ReleaseSCache(dirScp);
1401                     break;
1402                 }
1403                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1404                     /* this is a symlink; assemble a new buffer */
1405                     lock_ReleaseMutex(&tscp->mx);
1406                     if (symlinkCount++ >= 16) {
1407                         cm_ReleaseSCache(tscp);
1408                         cm_ReleaseSCache(dirScp);
1409                         if (psp) 
1410                             cm_FreeSpace(psp);
1411                         return CM_ERROR_TOOBIG;
1412                     }
1413                     if (tc == 0) 
1414                         restp = "";
1415                     else 
1416                         restp = tp;
1417                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1418                     if (code) {
1419                         /* something went wrong */
1420                         cm_ReleaseSCache(tscp);
1421                         cm_ReleaseSCache(dirScp);
1422                         break;
1423                     }
1424
1425                     /* otherwise, tempsp has the new path,
1426                      * and linkScp is the new root from
1427                      * which to interpret that path.
1428                      * Continue with the namei processing,
1429                      * also doing the bookkeeping for the
1430                      * space allocation and tracking the
1431                      * vnode reference counts.
1432                      */
1433                     if (psp) 
1434                         cm_FreeSpace(psp);
1435                     psp = tempsp;
1436                     tp = psp->data;
1437                     cm_ReleaseSCache(tscp);
1438                     tscp = linkScp;     
1439                     /* already held
1440                      * by AssembleLink
1441                      * now, if linkScp is null, that's
1442                      * AssembleLink's way of telling us that
1443                      * the sym link is relative to the dir
1444                      * containing the link.  We have a ref
1445                      * to it in dirScp, and we hold it now
1446                      * and reuse it as the new spot in the
1447                      * dir hierarchy.
1448                      */
1449                     if (tscp == NULL) {
1450                         cm_HoldSCache(dirScp);
1451                         tscp = dirScp;
1452                     }
1453                 }       /* if we have a sym link */
1454                 else {
1455                     /* not a symlink, we may be done */
1456                     lock_ReleaseMutex(&tscp->mx);
1457                     if (tc == 0) {
1458                         if (phase == 1) {
1459                             phase = 2;
1460                             tp = pathp;
1461                             continue;
1462                         }
1463                         cm_ReleaseSCache(dirScp);
1464                         code = 0;
1465                         break;
1466                     }
1467                 }
1468                 cm_ReleaseSCache(dirScp);
1469             } /* end of a component */
1470             else *cp++ = tc;
1471         } /* we have a component */
1472     } /* big while loop over all components */
1473
1474     /* already held */
1475     if (psp) 
1476         cm_FreeSpace(psp);
1477     if (code == 0) 
1478         *outScpp = tscp;
1479     return code;
1480 }
1481
1482 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1483  * We chase the link, and return a held pointer to the target, if it exists,
1484  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1485  * and do not hold or return a target vnode.
1486  *
1487  * This is very similar to calling cm_NameI with the last component of a name,
1488  * which happens to be a symlink, except that we've already passed by the name.
1489  *
1490  * This function is typically called by the directory listing functions, which
1491  * encounter symlinks but need to return the proper file length so programs
1492  * like "more" work properly when they make use of the attributes retrieved from
1493  * the dir listing.
1494  *
1495  * The input vnode should not be locked when this function is called.
1496  */
1497 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1498                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1499 {
1500     long code;
1501     cm_space_t *spacep;
1502     cm_scache_t *newRootScp;
1503
1504     osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1505
1506     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1507     if (code) 
1508         return code;
1509
1510     /* now, if newRootScp is NULL, we're really being told that the symlink
1511      * is relative to the current directory (dscp).
1512      */
1513     if (newRootScp == NULL) {
1514         newRootScp = dscp;
1515         cm_HoldSCache(dscp);
1516     }
1517
1518     code = cm_NameI(newRootScp, spacep->data,
1519                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1520                      userp, NULL, reqp, outScpp);
1521
1522     /* this stuff is allocated no matter what happened on the namei call,
1523      * so free it */
1524     cm_FreeSpace(spacep);
1525     cm_ReleaseSCache(newRootScp);
1526
1527     return code;
1528 }
1529
1530 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1531  * check anyway, but we want to minimize the chance that we have to leave stuff
1532  * unstat'd.
1533  */
1534 #define CM_BULKMAX              128
1535
1536 /* rock for bulk stat calls */
1537 typedef struct cm_bulkStat {
1538     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
1539
1540     /* info for the actual call */
1541     int counter;                        /* next free slot */
1542     AFSFid fids[CM_BULKMAX];
1543     AFSFetchStatus stats[CM_BULKMAX];
1544     AFSCallBack callbacks[CM_BULKMAX];
1545 } cm_bulkStat_t;
1546
1547 /* for a given entry, make sure that it isn't in the stat cache, and then
1548  * add it to the list of file IDs to be obtained.
1549  *
1550  * Don't bother adding it if we already have a vnode.  Note that the dir
1551  * is locked, so we have to be careful checking the vnode we're thinking of
1552  * processing, to avoid deadlocks.
1553  */
1554 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1555                      osi_hyper_t *offp)
1556 {
1557     osi_hyper_t thyper;
1558     cm_bulkStat_t *bsp;
1559     int i;
1560     cm_scache_t *tscp;
1561     cm_fid_t tfid;
1562
1563     bsp = rockp;
1564
1565     /* Don't overflow bsp. */
1566     if (bsp->counter >= CM_BULKMAX)
1567         return CM_ERROR_STOPNOW;
1568
1569     thyper.LowPart = buf_bufferSize;
1570     thyper.HighPart = 0;
1571     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1572
1573     /* thyper is now the first byte past the end of the record we're
1574      * interested in, and bsp->bufOffset is the first byte of the record
1575      * we're interested in.
1576      * Skip data in the others.
1577      * Skip '.' and '..'
1578      */
1579     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1580         return 0;
1581     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1582         return CM_ERROR_STOPNOW;
1583     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1584         return 0;
1585
1586     tfid.cell = scp->fid.cell;
1587     tfid.volume = scp->fid.volume;
1588     tfid.vnode = ntohl(dep->fid.vnode);
1589     tfid.unique = ntohl(dep->fid.unique);
1590     tscp = cm_FindSCache(&tfid);
1591     if (tscp) {
1592         if (lock_TryMutex(&tscp->mx)) {
1593             /* we have an entry that we can look at */
1594             if (cm_HaveCallback(tscp)) {
1595                 /* we have a callback on it.  Don't bother
1596                  * fetching this stat entry, since we're happy
1597                  * with the info we have.
1598                  */
1599                 lock_ReleaseMutex(&tscp->mx);
1600                 cm_ReleaseSCache(tscp);
1601                 return 0;
1602             }
1603             lock_ReleaseMutex(&tscp->mx);
1604         }       /* got lock */
1605         cm_ReleaseSCache(tscp);
1606     }   /* found entry */
1607
1608 #ifdef AFS_FREELANCE_CLIENT
1609     // yj: if this is a mountpoint under root.afs then we don't want it
1610     // to be bulkstat-ed, instead, we call getSCache directly and under
1611     // getSCache, it is handled specially.
1612     if  ( cm_freelanceEnabled &&
1613           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
1614           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1615           !(tfid.vnode==0x1 && tfid.unique==0x1) )
1616     {       
1617         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1618         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1619     }
1620 #endif /* AFS_FREELANCE_CLIENT */
1621
1622     i = bsp->counter++;
1623     bsp->fids[i].Volume = scp->fid.volume;
1624     bsp->fids[i].Vnode = tfid.vnode;
1625     bsp->fids[i].Unique = tfid.unique;
1626     return 0;
1627 }       
1628
1629 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1630  * calls on all undeleted files in the page of the directory specified.
1631  */
1632 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1633                      cm_req_t *reqp)
1634 {
1635     long code;
1636     cm_bulkStat_t bb;   /* this is *BIG*, probably 12K or so;
1637                          * watch for stack problems */
1638     AFSCBFids fidStruct;
1639     AFSBulkStats statStruct;
1640     cm_conn_t *connp;
1641     AFSCBs callbackStruct;
1642     long filex;
1643     AFSVolSync volSync;
1644     cm_callbackRequest_t cbReq;
1645     long filesThisCall;
1646     long i;
1647     long j;
1648     cm_scache_t *scp;
1649     cm_fid_t tfid;
1650
1651     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1652
1653     /* should be on a buffer boundary */
1654     osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1655
1656     bb.counter = 0;
1657     bb.bufOffset = *offsetp;
1658
1659     /* first, assemble the file IDs we need to stat */
1660     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1661                         reqp, NULL);
1662
1663     /* if we failed, bail out early */
1664     if (code && code != CM_ERROR_STOPNOW) return;
1665
1666     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1667      * make the calls to create the entries.  Handle AFSCBMAX files at a
1668      * time.
1669      */
1670     filex = 0;
1671     while(filex < bb.counter) {
1672         filesThisCall = bb.counter - filex;
1673         if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1674
1675         fidStruct.AFSCBFids_len = filesThisCall;
1676         fidStruct.AFSCBFids_val = &bb.fids[filex];
1677         statStruct.AFSBulkStats_len = filesThisCall;
1678         statStruct.AFSBulkStats_val = &bb.stats[filex];
1679         callbackStruct.AFSCBs_len = filesThisCall;
1680         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1681         cm_StartCallbackGrantingCall(NULL, &cbReq);
1682         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1683         do {
1684             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1685             if (code) 
1686                 continue;
1687
1688             lock_ObtainMutex(&connp->mx);
1689             code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1690                                      &statStruct, &callbackStruct, &volSync);
1691             lock_ReleaseMutex(&connp->mx);
1692         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1693                              &volSync, NULL, &cbReq, code));
1694         code = cm_MapRPCError(code, reqp);
1695
1696         osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1697
1698         /* may as well quit on an error, since we're not going to do
1699          * much better on the next immediate call, either.
1700          */
1701         if (code) 
1702             break;
1703
1704         /* otherwise, we should do the merges */
1705         for(i = 0; i<filesThisCall; i++) {
1706             j = filex + i;
1707             tfid.cell = dscp->fid.cell;
1708             tfid.volume = bb.fids[j].Volume;
1709             tfid.vnode = bb.fids[j].Vnode;
1710             tfid.unique = bb.fids[j].Unique;
1711             code = cm_GetSCache(&tfid, &scp, userp, reqp);
1712             if (code != 0) 
1713                 continue;
1714
1715             /* otherwise, if this entry has no callback info, 
1716              * merge in this.
1717              */
1718             lock_ObtainMutex(&scp->mx);
1719             /* now, we have to be extra paranoid on merging in this
1720              * information, since we didn't use cm_SyncOp before
1721              * starting the fetch to make sure that no bad races
1722              * were occurring.  Specifically, we need to make sure
1723              * we don't obliterate any newer information in the
1724              * vnode than have here.
1725              *
1726              * Right now, be pretty conservative: if there's a
1727              * callback or a pending call, skip it.
1728              */
1729             if (scp->cbServerp == NULL
1730                  && !(scp->flags &
1731                        (CM_SCACHEFLAG_FETCHING
1732                          | CM_SCACHEFLAG_STORING
1733                          | CM_SCACHEFLAG_SIZESTORING))) {
1734                 cm_EndCallbackGrantingCall(scp, &cbReq,
1735                                             &bb.callbacks[j],
1736                                             CM_CALLBACK_MAINTAINCOUNT);
1737                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1738                                 userp, 0);
1739             }       
1740             lock_ReleaseMutex(&scp->mx);
1741             cm_ReleaseSCache(scp);
1742         } /* all files in the response */
1743         /* now tell it to drop the count,
1744          * after doing the vnode processing above */
1745         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1746
1747         filex += filesThisCall;
1748     }   /* while there are still more files to process */
1749     osi_Log0(afsd_logp, "END cm_TryBulkStat");
1750 }       
1751
1752 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1753 {
1754     long mask;
1755
1756     /* initialize store back mask as inexpensive local variable */
1757     mask = 0;
1758     memset(statusp, 0, sizeof(AFSStoreStatus));
1759
1760     /* copy out queued info from scache first, if scp passed in */
1761     if (scp) {
1762         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1763             statusp->ClientModTime = scp->clientModTime;
1764             mask |= AFS_SETMODTIME;
1765             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1766         }
1767     }
1768
1769     if (attrp) {
1770         /* now add in our locally generated request */
1771         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1772             statusp->ClientModTime = attrp->clientModTime;
1773             mask |= AFS_SETMODTIME;
1774         }
1775         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1776             statusp->UnixModeBits = attrp->unixModeBits;
1777             mask |= AFS_SETMODE;
1778         }
1779         if (attrp->mask & CM_ATTRMASK_OWNER) {
1780             statusp->Owner = attrp->owner;
1781             mask |= AFS_SETOWNER;
1782         }
1783         if (attrp->mask & CM_ATTRMASK_GROUP) {
1784             statusp->Group = attrp->group;
1785             mask |= AFS_SETGROUP;
1786         }
1787     }
1788     statusp->Mask = mask;
1789 }       
1790
1791 /* set the file size, and make sure that all relevant buffers have been
1792  * truncated.  Ensure that any partially truncated buffers have been zeroed
1793  * to the end of the buffer.
1794  */
1795 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1796                    cm_req_t *reqp)
1797 {
1798     long code;
1799     int shrinking;
1800
1801     /* start by locking out buffer creation */
1802     lock_ObtainWrite(&scp->bufCreateLock);
1803
1804     /* verify that this is a file, not a dir or a symlink */
1805     lock_ObtainMutex(&scp->mx);
1806     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1807                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1808     if (code) 
1809         goto done;
1810         
1811     if (scp->fileType != CM_SCACHETYPE_FILE) {
1812         code = CM_ERROR_ISDIR;
1813         goto done;
1814     }
1815
1816   startover:
1817     if (LargeIntegerLessThan(*sizep, scp->length))
1818         shrinking = 1;
1819     else
1820         shrinking = 0;
1821
1822     lock_ReleaseMutex(&scp->mx);
1823
1824     /* can't hold scp->mx lock here, since we may wait for a storeback to
1825      * finish if the buffer package is cleaning a buffer by storing it to
1826      * the server.
1827      */
1828     if (shrinking)
1829         buf_Truncate(scp, userp, reqp, sizep);
1830
1831     /* now ensure that file length is short enough, and update truncPos */
1832     lock_ObtainMutex(&scp->mx);
1833
1834     /* make sure we have a callback (so we have the right value for the
1835      * length), and wait for it to be safe to do a truncate.
1836      */
1837     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1838                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1839                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1840     if (code) 
1841         goto done;
1842
1843     if (LargeIntegerLessThan(*sizep, scp->length)) {
1844         /* a real truncation.  If truncPos is not set yet, or is bigger
1845          * than where we're truncating the file, set truncPos to this
1846          * new value.
1847          */
1848         if (!shrinking)
1849             goto startover;
1850         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1851              || LargeIntegerLessThan(*sizep, scp->length)) {
1852             /* set trunc pos */
1853             scp->truncPos = *sizep;
1854             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1855         }
1856         /* in either case, the new file size has been changed */
1857         scp->length = *sizep;
1858         scp->mask |= CM_SCACHEMASK_LENGTH;
1859     }
1860     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1861         /* really extending the file */
1862         scp->length = *sizep;
1863         scp->mask |= CM_SCACHEMASK_LENGTH;
1864     }
1865
1866     /* done successfully */
1867     code = 0;
1868
1869   done:
1870     lock_ReleaseMutex(&scp->mx);
1871     lock_ReleaseWrite(&scp->bufCreateLock);
1872
1873     return code;
1874 }
1875
1876 /* set the file size or other attributes (but not both at once) */
1877 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1878                 cm_req_t *reqp)
1879 {
1880     long code;
1881     int flags;
1882     AFSFetchStatus afsOutStatus;
1883     AFSVolSync volSync;
1884     cm_conn_t *connp;
1885     AFSFid tfid;
1886     AFSStoreStatus afsInStatus;
1887
1888     /* handle file length setting */
1889     if (attrp->mask & CM_ATTRMASK_LENGTH)
1890         return cm_SetLength(scp, &attrp->length, userp, reqp);
1891
1892     flags = CM_SCACHESYNC_STORESTATUS;
1893
1894     lock_ObtainMutex(&scp->mx);
1895     /* otherwise, we have to make an RPC to get the status */
1896     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1897
1898     /* make the attr structure */
1899     cm_StatusFromAttr(&afsInStatus, scp, attrp);
1900
1901     lock_ReleaseMutex(&scp->mx);
1902     if (code) 
1903         return code;
1904
1905     /* now make the RPC */
1906     osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1907     tfid.Volume = scp->fid.volume;
1908     tfid.Vnode = scp->fid.vnode;
1909     tfid.Unique = scp->fid.unique;
1910     do {
1911         code = cm_Conn(&scp->fid, userp, reqp, &connp);
1912         if (code) 
1913             continue;
1914
1915         lock_ObtainMutex(&connp->mx);
1916         code = RXAFS_StoreStatus(connp->callp, &tfid,
1917                                   &afsInStatus, &afsOutStatus, &volSync);
1918         lock_ReleaseMutex(&connp->mx);
1919     } while (cm_Analyze(connp, userp, reqp,
1920                          &scp->fid, &volSync, NULL, NULL, code));
1921     code = cm_MapRPCError(code, reqp);
1922
1923     osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1924
1925     lock_ObtainMutex(&scp->mx);
1926     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1927     if (code == 0)
1928         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1929                         CM_MERGEFLAG_FORCE);
1930         
1931     /* if we're changing the mode bits, discard the ACL cache, 
1932      * since we changed the mode bits.
1933      */
1934     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1935     lock_ReleaseMutex(&scp->mx);
1936     return code;
1937 }       
1938
1939 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1940                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1941 {       
1942     cm_conn_t *connp;
1943     long code;
1944     AFSFid dirAFSFid;
1945     cm_callbackRequest_t cbReq;
1946     AFSFid newAFSFid;
1947     cm_fid_t newFid;
1948     cm_scache_t *scp;
1949     int didEnd;
1950     AFSStoreStatus inStatus;
1951     AFSFetchStatus updatedDirStatus;
1952     AFSFetchStatus newFileStatus;
1953     AFSCallBack newFileCallback;
1954     AFSVolSync volSync;
1955
1956     /* can't create names with @sys in them; must expand it manually first.
1957      * return "invalid request" if they try.
1958      */
1959     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
1960         return CM_ERROR_ATSYS;
1961     }
1962
1963     /* before starting the RPC, mark that we're changing the file data, so
1964      * that someone who does a chmod will know to wait until our call
1965      * completes.
1966      */
1967     lock_ObtainMutex(&dscp->mx);
1968     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1969     if (code == 0) {
1970         cm_StartCallbackGrantingCall(NULL, &cbReq);
1971     }
1972     lock_ReleaseMutex(&dscp->mx);
1973     if (code) {
1974         return code;
1975     }
1976     didEnd = 0;
1977
1978     cm_StatusFromAttr(&inStatus, NULL, attrp);
1979
1980     /* try the RPC now */
1981     do {
1982         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1983         if (code) 
1984             continue;
1985
1986         dirAFSFid.Volume = dscp->fid.volume;
1987         dirAFSFid.Vnode = dscp->fid.vnode;
1988         dirAFSFid.Unique = dscp->fid.unique;
1989         lock_ObtainMutex(&connp->mx);
1990         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1991                                  &inStatus, &newAFSFid, &newFileStatus,
1992                                  &updatedDirStatus, &newFileCallback,
1993                                  &volSync);
1994         lock_ReleaseMutex(&connp->mx);
1995     } while (cm_Analyze(connp, userp, reqp,
1996                          &dscp->fid, &volSync, NULL, &cbReq, code));
1997     code = cm_MapRPCError(code, reqp);
1998         
1999     lock_ObtainMutex(&dscp->mx);
2000     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2001     if (code == 0) {
2002         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2003     }
2004     lock_ReleaseMutex(&dscp->mx);
2005
2006     /* now try to create the file's entry, too, but be careful to 
2007      * make sure that we don't merge in old info.  Since we weren't locking
2008      * out any requests during the file's creation, we may have pretty old
2009      * info.
2010      */
2011     if (code == 0) {
2012         newFid.cell = dscp->fid.cell;
2013         newFid.volume = dscp->fid.volume;
2014         newFid.vnode = newAFSFid.Vnode;
2015         newFid.unique = newAFSFid.Unique;
2016         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2017         if (code == 0) {
2018             lock_ObtainMutex(&scp->mx);
2019             if (!cm_HaveCallback(scp)) {
2020                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2021                                 userp, 0);
2022                 cm_EndCallbackGrantingCall(scp, &cbReq,
2023                                             &newFileCallback, 0);
2024                 didEnd = 1;     
2025             }       
2026             lock_ReleaseMutex(&scp->mx);
2027             *scpp = scp;
2028         }
2029     }
2030
2031     /* make sure we end things properly */
2032     if (!didEnd)
2033         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2034
2035     return code;
2036 }       
2037
2038 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2039 {
2040     long code;
2041
2042     lock_ObtainWrite(&scp->bufCreateLock);
2043     code = buf_CleanVnode(scp, userp, reqp);
2044     lock_ReleaseWrite(&scp->bufCreateLock);
2045     if (code == 0) {
2046         lock_ObtainMutex(&scp->mx);
2047         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2048                          | CM_SCACHEFLAG_OUTOFSPACE);
2049         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2050                           | CM_SCACHEMASK_CLIENTMODTIME
2051                           | CM_SCACHEMASK_LENGTH))
2052             code = cm_StoreMini(scp, userp, reqp);
2053         lock_ReleaseMutex(&scp->mx);
2054     }
2055     return code;
2056 }
2057
2058 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2059                  cm_user_t *userp, cm_req_t *reqp)
2060 {
2061     cm_conn_t *connp;
2062     long code;
2063     AFSFid dirAFSFid;
2064     cm_callbackRequest_t cbReq;
2065     AFSFid newAFSFid;
2066     cm_fid_t newFid;
2067     cm_scache_t *scp;
2068     int didEnd;
2069     AFSStoreStatus inStatus;
2070     AFSFetchStatus updatedDirStatus;
2071     AFSFetchStatus newDirStatus;
2072     AFSCallBack newDirCallback;
2073     AFSVolSync volSync;
2074
2075     /* can't create names with @sys in them; must expand it manually first.
2076      * return "invalid request" if they try.
2077      */
2078     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2079         return CM_ERROR_ATSYS;
2080     }
2081
2082     /* before starting the RPC, mark that we're changing the directory
2083      * data, so that someone who does a chmod on the dir will wait until
2084      * our call completes.
2085      */
2086     lock_ObtainMutex(&dscp->mx);
2087     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2088     if (code == 0) {
2089         cm_StartCallbackGrantingCall(NULL, &cbReq);
2090     }
2091     lock_ReleaseMutex(&dscp->mx);
2092     if (code) {
2093         return code;
2094     }
2095     didEnd = 0;
2096
2097     cm_StatusFromAttr(&inStatus, NULL, attrp);
2098
2099     /* try the RPC now */
2100     do {
2101         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2102         if (code) 
2103             continue;
2104
2105         dirAFSFid.Volume = dscp->fid.volume;
2106         dirAFSFid.Vnode = dscp->fid.vnode;
2107         dirAFSFid.Unique = dscp->fid.unique;
2108         lock_ObtainMutex(&connp->mx);
2109         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2110                               &inStatus, &newAFSFid, &newDirStatus,
2111                               &updatedDirStatus, &newDirCallback,
2112                               &volSync);
2113         lock_ReleaseMutex(&connp->mx);
2114     } while (cm_Analyze(connp, userp, reqp,
2115                          &dscp->fid, &volSync, NULL, &cbReq, code));
2116     code = cm_MapRPCError(code, reqp);
2117         
2118     lock_ObtainMutex(&dscp->mx);
2119     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2120     if (code == 0) {
2121         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2122     }
2123     lock_ReleaseMutex(&dscp->mx);
2124
2125     /* now try to create the new dir's entry, too, but be careful to 
2126      * make sure that we don't merge in old info.  Since we weren't locking
2127      * out any requests during the file's creation, we may have pretty old
2128      * info.
2129      */
2130     if (code == 0) {
2131         newFid.cell = dscp->fid.cell;
2132         newFid.volume = dscp->fid.volume;
2133         newFid.vnode = newAFSFid.Vnode;
2134         newFid.unique = newAFSFid.Unique;
2135         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2136         if (code == 0) {
2137             lock_ObtainMutex(&scp->mx);
2138             if (!cm_HaveCallback(scp)) {
2139                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2140                                 userp, 0);
2141                 cm_EndCallbackGrantingCall(scp, &cbReq,
2142                                             &newDirCallback, 0);
2143                 didEnd = 1;             
2144             }
2145             lock_ReleaseMutex(&scp->mx);
2146             cm_ReleaseSCache(scp);
2147         }
2148     }
2149
2150     /* make sure we end things properly */
2151     if (!didEnd)
2152         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2153
2154     /* and return error code */
2155     return code;
2156 }       
2157
2158 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2159              cm_user_t *userp, cm_req_t *reqp)
2160 {
2161     cm_conn_t *connp;
2162     long code = 0;
2163     AFSFid dirAFSFid;
2164     AFSFid existingAFSFid;
2165     AFSFetchStatus updatedDirStatus;
2166     AFSFetchStatus newLinkStatus;
2167     AFSVolSync volSync;
2168
2169     if (dscp->fid.cell != sscp->fid.cell ||
2170         dscp->fid.volume != sscp->fid.volume) {
2171         return CM_ERROR_CROSSDEVLINK;
2172     }
2173
2174     lock_ObtainMutex(&dscp->mx);
2175     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2176     lock_ReleaseMutex(&dscp->mx);
2177
2178     if (code)
2179         return code;
2180
2181     do {
2182         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2183         if (code) continue;
2184
2185         dirAFSFid.Volume = dscp->fid.volume;
2186         dirAFSFid.Vnode = dscp->fid.vnode;
2187         dirAFSFid.Unique = dscp->fid.unique;
2188
2189         existingAFSFid.Volume = sscp->fid.volume;
2190         existingAFSFid.Vnode = sscp->fid.vnode;
2191         existingAFSFid.Unique = sscp->fid.unique;
2192
2193         lock_ObtainMutex(&connp->mx);
2194         code = RXAFS_Link(connp->callp, &dirAFSFid, namep, &existingAFSFid,
2195             &newLinkStatus, &updatedDirStatus, &volSync);
2196         lock_ReleaseMutex(&connp->mx);
2197
2198         osi_Log1(smb_logp,"  RXAFS_Link returns %d", code);
2199     } while (cm_Analyze(connp, userp, reqp,
2200         &dscp->fid, &volSync, NULL, NULL, code));
2201
2202     code = cm_MapRPCError(code, reqp);
2203
2204     lock_ObtainMutex(&dscp->mx);
2205     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2206     if (code == 0) {
2207         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2208     }
2209     lock_ReleaseMutex(&dscp->mx);
2210
2211     return code;
2212 }
2213
2214 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2215                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2216 {
2217     cm_conn_t *connp;
2218     long code;
2219     AFSFid dirAFSFid;
2220     AFSFid newAFSFid;
2221     cm_fid_t newFid;
2222     cm_scache_t *scp;
2223     AFSStoreStatus inStatus;
2224     AFSFetchStatus updatedDirStatus;
2225     AFSFetchStatus newLinkStatus;
2226     AFSVolSync volSync;
2227
2228     /* before starting the RPC, mark that we're changing the directory data,
2229      * so that someone who does a chmod on the dir will wait until our
2230      * call completes.
2231      */
2232     lock_ObtainMutex(&dscp->mx);
2233     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2234     lock_ReleaseMutex(&dscp->mx);
2235     if (code) {
2236         return code;
2237     }
2238
2239     cm_StatusFromAttr(&inStatus, NULL, attrp);
2240
2241     /* try the RPC now */
2242     do {
2243         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2244         if (code) 
2245             continue;
2246
2247         dirAFSFid.Volume = dscp->fid.volume;
2248         dirAFSFid.Vnode = dscp->fid.vnode;
2249         dirAFSFid.Unique = dscp->fid.unique;
2250         lock_ObtainMutex(&connp->mx);
2251         code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2252                               &inStatus, &newAFSFid, &newLinkStatus,
2253                               &updatedDirStatus, &volSync);
2254         lock_ReleaseMutex(&connp->mx);
2255     } while (cm_Analyze(connp, userp, reqp,
2256                          &dscp->fid, &volSync, NULL, NULL, code));
2257     code = cm_MapRPCError(code, reqp);
2258         
2259     lock_ObtainMutex(&dscp->mx);
2260     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2261     if (code == 0) {
2262         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2263     }
2264     lock_ReleaseMutex(&dscp->mx);
2265
2266     /* now try to create the new dir's entry, too, but be careful to 
2267      * make sure that we don't merge in old info.  Since we weren't locking
2268      * out any requests during the file's creation, we may have pretty old
2269      * info.
2270      */
2271     if (code == 0) {
2272         newFid.cell = dscp->fid.cell;
2273         newFid.volume = dscp->fid.volume;
2274         newFid.vnode = newAFSFid.Vnode;
2275         newFid.unique = newAFSFid.Unique;
2276         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2277         if (code == 0) {
2278             lock_ObtainMutex(&scp->mx);
2279             if (!cm_HaveCallback(scp)) {
2280                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2281                                 userp, 0);
2282             }       
2283             lock_ReleaseMutex(&scp->mx);
2284             cm_ReleaseSCache(scp);
2285         }
2286     }
2287         
2288     /* and return error code */
2289     return code;
2290 }
2291
2292 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2293                    cm_req_t *reqp)
2294 {
2295     cm_conn_t *connp;
2296     long code;
2297     AFSFid dirAFSFid;
2298     int didEnd;
2299     AFSFetchStatus updatedDirStatus;
2300     AFSVolSync volSync;
2301
2302     /* before starting the RPC, mark that we're changing the directory data,
2303      * so that someone who does a chmod on the dir will wait until our
2304      * call completes.
2305      */
2306     lock_ObtainMutex(&dscp->mx);
2307     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2308     lock_ReleaseMutex(&dscp->mx);
2309     if (code) {
2310         return code;
2311     }
2312     didEnd = 0;
2313
2314     /* try the RPC now */
2315     do {
2316         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2317         if (code) 
2318             continue;
2319
2320         dirAFSFid.Volume = dscp->fid.volume;
2321         dirAFSFid.Vnode = dscp->fid.vnode;
2322         dirAFSFid.Unique = dscp->fid.unique;
2323         lock_ObtainMutex(&connp->mx);
2324         code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2325                                 &updatedDirStatus, &volSync);
2326         lock_ReleaseMutex(&connp->mx);
2327     } while (cm_Analyze(connp, userp, reqp,
2328                          &dscp->fid, &volSync, NULL, NULL, code));
2329     code = cm_MapRPCErrorRmdir(code, reqp);
2330         
2331     lock_ObtainMutex(&dscp->mx);
2332     cm_dnlcRemove(dscp, namep); 
2333     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2334     if (code == 0) {
2335         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2336     }
2337     lock_ReleaseMutex(&dscp->mx);
2338
2339     /* and return error code */
2340     return code;
2341 }
2342
2343 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2344 {
2345     /* grab mutex on contents */
2346     lock_ObtainMutex(&scp->mx);
2347
2348     /* reset the prefetch info */
2349     scp->prefetch.base.LowPart = 0;             /* base */
2350     scp->prefetch.base.HighPart = 0;
2351     scp->prefetch.end.LowPart = 0;              /* and end */
2352     scp->prefetch.end.HighPart = 0;
2353
2354     /* release mutex on contents */
2355     lock_ReleaseMutex(&scp->mx);
2356
2357     /* we're done */
2358     return 0;
2359 }       
2360
2361 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2362                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2363 {
2364     cm_conn_t *connp;
2365     long code;
2366     AFSFid oldDirAFSFid;
2367     AFSFid newDirAFSFid;
2368     int didEnd;
2369     AFSFetchStatus updatedOldDirStatus;
2370     AFSFetchStatus updatedNewDirStatus;
2371     AFSVolSync volSync;
2372     int oneDir;
2373
2374     /* before starting the RPC, mark that we're changing the directory data,
2375      * so that someone who does a chmod on the dir will wait until our call
2376      * completes.  We do this in vnode order so that we don't deadlock,
2377      * which makes the code a little verbose.
2378      */
2379     if (oldDscp == newDscp) {
2380         /* check for identical names */
2381         if (strcmp(oldNamep, newNamep) == 0)
2382             return CM_ERROR_RENAME_IDENTICAL;
2383
2384         oneDir = 1;
2385         lock_ObtainMutex(&oldDscp->mx);
2386         cm_dnlcRemove(oldDscp, oldNamep);
2387         cm_dnlcRemove(oldDscp, newNamep);
2388         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2389                           CM_SCACHESYNC_STOREDATA);
2390         lock_ReleaseMutex(&oldDscp->mx);
2391     }
2392     else {
2393         /* two distinct dir vnodes */
2394         oneDir = 0;
2395         if (oldDscp->fid.cell != newDscp->fid.cell ||
2396              oldDscp->fid.volume != newDscp->fid.volume)
2397             return CM_ERROR_CROSSDEVLINK;
2398
2399         /* shouldn't happen that we have distinct vnodes for two
2400          * different files, but could due to deliberate attack, or
2401          * stale info.  Avoid deadlocks and quit now.
2402          */
2403         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2404             return CM_ERROR_CROSSDEVLINK;
2405
2406         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2407             lock_ObtainMutex(&oldDscp->mx);
2408             cm_dnlcRemove(oldDscp, oldNamep);
2409             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2410                               CM_SCACHESYNC_STOREDATA);
2411             lock_ReleaseMutex(&oldDscp->mx);
2412             if (code == 0) {
2413                 lock_ObtainMutex(&newDscp->mx);
2414                 cm_dnlcRemove(newDscp, newNamep);
2415                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2416                                   CM_SCACHESYNC_STOREDATA);
2417                 lock_ReleaseMutex(&newDscp->mx);
2418                 if (code) {
2419                     /* cleanup first one */
2420                     cm_SyncOpDone(oldDscp, NULL,
2421                                    CM_SCACHESYNC_STOREDATA);
2422                 }       
2423             }
2424         }
2425         else {
2426             /* lock the new vnode entry first */
2427             lock_ObtainMutex(&newDscp->mx);
2428             cm_dnlcRemove(newDscp, newNamep);
2429             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2430                               CM_SCACHESYNC_STOREDATA);
2431             lock_ReleaseMutex(&newDscp->mx);
2432             if (code == 0) {
2433                 lock_ObtainMutex(&oldDscp->mx);
2434                 cm_dnlcRemove(oldDscp, oldNamep);
2435                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2436                                   CM_SCACHESYNC_STOREDATA);
2437                 lock_ReleaseMutex(&oldDscp->mx);
2438                 if (code) {
2439                     /* cleanup first one */
2440                     cm_SyncOpDone(newDscp, NULL,
2441                                    CM_SCACHESYNC_STOREDATA);
2442                 }       
2443             }
2444         }
2445     }   /* two distinct vnodes */
2446
2447     if (code) {
2448         return code;
2449     }
2450     didEnd = 0;
2451
2452     /* try the RPC now */
2453     do {
2454         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2455         if (code) 
2456             continue;
2457
2458         oldDirAFSFid.Volume = oldDscp->fid.volume;
2459         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2460         oldDirAFSFid.Unique = oldDscp->fid.unique;
2461         newDirAFSFid.Volume = newDscp->fid.volume;
2462         newDirAFSFid.Vnode = newDscp->fid.vnode;
2463         newDirAFSFid.Unique = newDscp->fid.unique;
2464         lock_ObtainMutex(&connp->mx);
2465         code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2466                              &newDirAFSFid, newNamep,
2467                              &updatedOldDirStatus, &updatedNewDirStatus,
2468                              &volSync);
2469         lock_ReleaseMutex(&connp->mx);
2470     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2471                          &volSync, NULL, NULL, code));
2472     code = cm_MapRPCError(code, reqp);
2473         
2474     /* update the individual stat cache entries for the directories */
2475     lock_ObtainMutex(&oldDscp->mx);
2476     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2477     if (code == 0) {
2478         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2479                         userp, 0);
2480     }
2481     lock_ReleaseMutex(&oldDscp->mx);
2482
2483     /* and update it for the new one, too, if necessary */
2484     if (!oneDir) {
2485         lock_ObtainMutex(&newDscp->mx);
2486         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2487         if (code == 0) {
2488             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2489                             userp, 0);
2490         }
2491         lock_ReleaseMutex(&newDscp->mx);
2492     }
2493
2494     /* and return error code */
2495     return code;
2496 }
2497
2498 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2499               LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2500               u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2501               void **lockpp)
2502 {
2503     long code;
2504     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2505     AFSFid tfid;
2506     AFSVolSync volSync;
2507     cm_conn_t *connp;
2508     cm_file_lock_t *fileLock;
2509     osi_queue_t *q;
2510     int found = 0;
2511
2512     /* Look for a conflict.  Also, if we are asking for a shared lock,
2513      * look for another shared lock, so we don't have to do an RPC.
2514      */
2515     q = scp->fileLocks;
2516     while (q) {
2517         fileLock = (cm_file_lock_t *)
2518             ((char *) q - offsetof(cm_file_lock_t, fileq));
2519         if ((fileLock->flags &
2520               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2521              == 0) {
2522             if ((LockType & 0x1) == 0
2523                  || (fileLock->LockType & 0x1) == 0)
2524                 return CM_ERROR_WOULDBLOCK;
2525             found = 1;
2526         }
2527         q = osi_QNext(q);
2528     }
2529
2530     if (found)
2531         code = 0;
2532     else {
2533         tfid.Volume = scp->fid.volume;
2534         tfid.Vnode = scp->fid.vnode;
2535         tfid.Unique = scp->fid.unique;
2536         lock_ReleaseMutex(&scp->mx);
2537         do {
2538             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2539             if (code) 
2540                 break;
2541             lock_ObtainMutex(&connp->mx);
2542             code = RXAFS_SetLock(connp->callp, &tfid, Which,
2543                                   &volSync);
2544             lock_ReleaseMutex(&connp->mx);
2545         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2546                              NULL, NULL, code));
2547         lock_ObtainMutex(&scp->mx);
2548         code = cm_MapRPCError(code, reqp);
2549     }
2550
2551     if (code == 0 || Timeout != 0) {
2552         fileLock = malloc(sizeof(cm_file_lock_t));
2553         fileLock->LockType = LockType;
2554         cm_HoldUser(userp);
2555         fileLock->userp = userp;
2556         fileLock->fid = scp->fid;
2557         fileLock->LOffset = LOffset;
2558         fileLock->LLength = LLength;
2559         fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2560         osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2561         lock_ObtainWrite(&cm_scacheLock);
2562         osi_QAdd(&cm_allFileLocks, &fileLock->q);
2563         lock_ReleaseWrite(&cm_scacheLock);
2564         if (code != 0) 
2565             *lockpp = fileLock;
2566     }
2567     return code;
2568 }
2569
2570 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2571                 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2572                 cm_user_t *userp, cm_req_t *reqp)
2573 {
2574     long code = 0;
2575     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2576     AFSFid tfid;
2577     AFSVolSync volSync;
2578     cm_conn_t *connp;
2579     cm_file_lock_t *fileLock, *ourLock;
2580     osi_queue_t *q, *qq;
2581     int anotherReader = 0;
2582     int smallLock = 0;
2583     int found = 0;
2584
2585     if (LargeIntegerLessThan(LLength, scp->length))
2586         smallLock = 1;
2587
2588     /* Look for our own lock on the list, so as to remove it.
2589      * Also, determine if we're the last reader; if not, avoid an RPC.
2590      */
2591     q = scp->fileLocks;
2592     while (q) {
2593         fileLock = (cm_file_lock_t *)
2594             ((char *) q - offsetof(cm_file_lock_t, fileq));
2595         if (!found
2596              && fileLock->userp == userp
2597              && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2598              && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2599             found = 1;
2600             ourLock = fileLock;
2601             qq = q;
2602         }
2603         else if (fileLock->LockType & 0x1)
2604             anotherReader = 1;
2605         q = osi_QNext(q);
2606     }
2607
2608     /* ignore byte ranges */
2609     if (smallLock && !found)
2610         return 0;
2611
2612     /* don't try to unlock other people's locks */
2613     if (!found)
2614         return CM_ERROR_WOULDBLOCK;
2615
2616     /* discard lock record */
2617     osi_QRemove(&scp->fileLocks, qq);
2618     /*
2619      * Don't delete it here; let the daemon delete it, to simplify
2620      * the daemon's traversal of the list.
2621      */
2622     lock_ObtainWrite(&cm_scacheLock);
2623     ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2624     cm_ReleaseUser(ourLock->userp);
2625     lock_ReleaseWrite(&cm_scacheLock);
2626
2627     if (!anotherReader) {
2628         tfid.Volume = scp->fid.volume;
2629         tfid.Vnode = scp->fid.vnode;
2630         tfid.Unique = scp->fid.unique;
2631         lock_ReleaseMutex(&scp->mx);
2632         do {
2633             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2634             if (code) 
2635                 break;
2636             lock_ObtainMutex(&connp->mx);
2637             code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2638             lock_ReleaseMutex(&connp->mx);
2639         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2640                              NULL, NULL, code));
2641         code = cm_MapRPCError(code, reqp);
2642         lock_ObtainMutex(&scp->mx);
2643     }
2644
2645     return code;
2646 }
2647
2648 void cm_CheckLocks()
2649 {
2650     osi_queue_t *q, *nq;
2651     cm_file_lock_t *fileLock;
2652     cm_req_t req;
2653     AFSFid tfid;
2654     AFSVolSync volSync;
2655     cm_conn_t *connp;
2656     long code;
2657
2658     cm_InitReq(&req);
2659
2660     lock_ObtainWrite(&cm_scacheLock);
2661     q = cm_allFileLocks;
2662     while (q) {
2663         fileLock = (cm_file_lock_t *) q;
2664         nq = osi_QNext(q);
2665         if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2666             osi_QRemove(&cm_allFileLocks, q);
2667             free(fileLock);
2668         }
2669         else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2670             tfid.Volume = fileLock->fid.volume;
2671             tfid.Vnode = fileLock->fid.vnode;
2672             tfid.Unique = fileLock->fid.unique;
2673             lock_ReleaseWrite(&cm_scacheLock);
2674             do {
2675                 code = cm_Conn(&fileLock->fid, fileLock->userp,
2676                                 &req, &connp);
2677                 if (code) 
2678                     break;
2679                 lock_ObtainMutex(&connp->mx);
2680                 code = RXAFS_ExtendLock(connp->callp, &tfid,
2681                                          &volSync);
2682                 lock_ReleaseMutex(&connp->mx);
2683             } while (cm_Analyze(connp, fileLock->userp, &req,
2684                                  &fileLock->fid, &volSync, NULL, NULL,
2685                                  code));
2686             code = cm_MapRPCError(code, &req);
2687             lock_ObtainWrite(&cm_scacheLock);
2688         }
2689         q = nq;
2690     }
2691     lock_ReleaseWrite(&cm_scacheLock);
2692 }       
2693
2694 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2695 {
2696     long code;
2697     int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2698     cm_scache_t *scp;
2699     AFSFid tfid;
2700     AFSVolSync volSync;
2701     cm_conn_t *connp;
2702     cm_file_lock_t *fileLock;
2703     osi_queue_t *q;
2704     cm_req_t req;
2705     int found = 0;
2706
2707     if (vcp_is_dead) {
2708         code = CM_ERROR_TIMEDOUT;
2709         goto handleCode;
2710     }
2711
2712     cm_InitReq(&req);
2713
2714     /* Look for a conflict.  Also, if we are asking for a shared lock,
2715      * look for another shared lock, so we don't have to do an RPC.
2716      */
2717     code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2718     if (code)
2719         return code;
2720
2721     q = scp->fileLocks;
2722     while (q) {
2723         fileLock = (cm_file_lock_t *)
2724             ((char *) q - offsetof(cm_file_lock_t, fileq));
2725         if ((fileLock->flags &
2726               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2727              == 0) {
2728             if ((oldFileLock->LockType & 0x1) == 0
2729                  || (fileLock->LockType & 0x1) == 0) {
2730                 cm_ReleaseSCache(scp);
2731                 return CM_ERROR_WOULDBLOCK;
2732             }
2733             found = 1;
2734         }
2735         q = osi_QNext(q);
2736     }
2737
2738     if (found)
2739         code = 0;
2740     else {
2741         tfid.Volume = oldFileLock->fid.volume;
2742         tfid.Vnode = oldFileLock->fid.vnode;
2743         tfid.Unique = oldFileLock->fid.unique;
2744         do {
2745             code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2746                             &req, &connp);
2747             if (code) 
2748                 break;
2749             lock_ObtainMutex(&connp->mx);
2750             code = RXAFS_SetLock(connp->callp, &tfid, Which,
2751                                   &volSync);
2752             lock_ReleaseMutex(&connp->mx);
2753         } while (cm_Analyze(connp, oldFileLock->userp, &req,
2754                              &oldFileLock->fid, &volSync,
2755                              NULL, NULL, code));
2756         code = cm_MapRPCError(code, &req);
2757     }
2758
2759   handleCode:
2760     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2761         lock_ObtainMutex(&scp->mx);
2762         osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2763         lock_ReleaseMutex(&scp->mx);
2764     }
2765     lock_ObtainWrite(&cm_scacheLock);
2766     if (code == 0)
2767         oldFileLock->flags = 0;
2768     else if (code != CM_ERROR_WOULDBLOCK) {
2769         oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2770         cm_ReleaseUser(oldFileLock->userp);
2771         oldFileLock->userp = NULL;
2772     }
2773     lock_ReleaseWrite(&cm_scacheLock);
2774
2775     return code;
2776 }