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