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