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