fd35295527592f58722019fe0ab4d36543cdd809
[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 #include <windows.h>
14 #include <winsock2.h>
15 #include <stddef.h>
16 #include <malloc.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20
21 #include <osi.h>
22
23 #include "afsd.h"
24 #include "cm_btree.h"
25
26 #ifdef DEBUG
27 extern void afsi_log(char *pattern, ...);
28 #endif
29
30 int cm_enableServerLocks = 1;
31
32 int cm_followBackupPath = 0;
33
34 /*
35  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
36  * I do not know anything more about it.
37  */
38 unsigned char cm_foldUpper[256] = {
39      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
40      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
41     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
42     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
43     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
44     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
45     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
46     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
47     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
48     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
49     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
50     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
51     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
52     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
53     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
54     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
55     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
56     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
57     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
58     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
59     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
60     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
61     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
62     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
63     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
64     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
65     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
66     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
67     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
68     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
69     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
70     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
71 };
72
73 /*
74  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
75  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
76  * upper-case u-umlaut).
77  */
78 int cm_stricmp(const char *str1, const char *str2)
79 {
80     char c1, c2;
81
82     while (1) {
83         if (*str1 == 0)
84             if (*str2 == 0)
85                 return 0;
86             else
87                 return -1;
88         if (*str2 == 0)
89             return 1;
90         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
91         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
92         if (c1 < c2)
93             return -1;
94         if (c1 > c2)
95             return 1;
96     }
97 }
98
99 /* characters that are legal in an 8.3 name */
100 /*
101  * We used to have 1's for all characters from 128 to 254.  But
102  * the NT client behaves better if we create an 8.3 name for any
103  * name that has a character with the high bit on, and if we
104  * delete those characters from 8.3 names.  In particular, see
105  * Sybase defect 10859.
106  */
107 char cm_LegalChars[256] = {
108  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110  0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0,
111  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
112  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
113  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
114  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
115  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
116  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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 };
125
126 /* return true iff component is a valid 8.3 name */
127 int cm_Is8Dot3(char *namep)
128 {
129     int sawDot = 0;
130     unsigned char tc;
131     int charCount = 0;
132         
133     /*
134      * can't have a leading dot;
135      * special case for . and ..
136      */
137     if (namep[0] == '.') {
138         if (namep[1] == 0)
139             return 1;
140         if (namep[1] == '.' && namep[2] == 0)
141             return 1;
142         return 0;
143     }
144     while (tc = *namep++) {
145         if (tc == '.') {
146             /* saw another dot */
147             if (sawDot) return 0;       /* second dot */
148             sawDot = 1;
149             charCount = 0;
150             continue;
151         }
152         if (cm_LegalChars[tc] == 0)
153             return 0;
154         charCount++;
155         if (!sawDot && charCount > 8)
156             /* more than 8 chars in name */
157             return 0;
158         if (sawDot && charCount > 3)
159             /* more than 3 chars in extension */
160             return 0;
161     }
162     return 1;
163 }
164
165 /*
166  * Number unparsing map for generating 8.3 names;
167  * The version taken from DFS was on drugs.  
168  * You can't include '&' and '@' in a file name.
169  */
170 char cm_8Dot3Mapping[42] =
171 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
172  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 
173  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 
174  'V', 'W', 'X', 'Y', 'Z', '_', '-', '$', '#', '!', '+', '='
175 };
176 int cm_8Dot3MapSize = sizeof(cm_8Dot3Mapping);
177
178 void cm_Gen8Dot3NameInt(const char * longname, cm_dirFid_t * pfid,
179                         char *shortName, char **shortNameEndp)
180 {
181     char number[12];
182     int i, nsize = 0;
183     int vnode = ntohl(pfid->vnode);
184     char *lastDot;
185     int validExtension = 0;
186     char tc, *temp;
187     const char *name;
188
189     /* Unparse the file's vnode number to get a "uniquifier" */
190     do {
191         number[nsize] = cm_8Dot3Mapping[vnode % cm_8Dot3MapSize];
192         nsize++;
193         vnode /= cm_8Dot3MapSize;
194     } while (vnode);
195
196     /*
197      * Look for valid extension.  There has to be a dot, and
198      * at least one of the characters following has to be legal.
199      */
200     lastDot = strrchr(longname, '.');
201     if (lastDot) {
202         temp = lastDot; temp++;
203         while (tc = *temp++)
204             if (cm_LegalChars[tc])
205                 break;
206         if (tc)
207             validExtension = 1;
208     }
209
210     /* Copy name characters */
211     for (i = 0, name = longname;
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) 
259         rights |= PRSFS_READ;
260     if (openMode == 1 || openMode == 2 || trunc) 
261         rights |= PRSFS_WRITE;
262         
263     lock_ObtainMutex(&scp->mx);
264
265     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
266                       CM_SCACHESYNC_GETSTATUS
267                      | CM_SCACHESYNC_NEEDCALLBACK
268                      | CM_SCACHESYNC_LOCK);
269
270     if (code == 0 && 
271         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
272         scp->fileType == CM_SCACHETYPE_FILE) {
273
274         cm_key_t key;
275         unsigned int sLockType;
276         LARGE_INTEGER LOffset, LLength;
277
278         /* Check if there's some sort of lock on the file at the
279            moment. */
280
281         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
282
283         if (rights & PRSFS_WRITE)
284             sLockType = 0;
285         else
286             sLockType = LOCKING_ANDX_SHARED_LOCK;
287
288         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
289         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
290         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
291         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
292
293         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
294
295         if (code == 0) {
296             cm_Unlock(scp, sLockType, LOffset, LLength, key, userp, reqp);
297         } else {
298             /* In this case, we allow the file open to go through even
299                though we can't enforce mandatory locking on the
300                file. */
301             if (code == CM_ERROR_NOACCESS &&
302                 !(rights & PRSFS_WRITE))
303                 code = 0;
304             else {
305                 switch (code) {
306                 case CM_ERROR_ALLOFFLINE:
307                 case CM_ERROR_ALLDOWN:
308                 case CM_ERROR_ALLBUSY:
309                 case CM_ERROR_TIMEDOUT:
310                 case CM_ERROR_RETRY:
311                 case CM_ERROR_WOULDBLOCK:
312                     break;
313                 default:
314                     code = CM_ERROR_SHARING_VIOLATION;
315                 }
316             }
317         }
318
319     } else if (code != 0) {
320         goto _done;
321     }
322
323     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
324
325  _done:
326
327     lock_ReleaseMutex(&scp->mx);
328
329     return code;
330 }
331
332 /* return success if we can open this file in this mode */
333 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
334                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp, 
335                     cm_lock_data_t **ldpp)
336 {
337     long rights;
338     long code;
339
340     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
341     *ldpp = NULL;
342
343     /* Always allow delete; the RPC will tell us if it's OK */
344     if (desiredAccess == DELETE)
345         return 0;
346
347     rights = 0;
348
349     if (desiredAccess & AFS_ACCESS_READ)
350         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
351
352     /* We used to require PRSFS_WRITE if createDisp was 4
353        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
354        However, we don't need to do that since the existence of the
355        scp implies that we don't need to create it. */
356     if (desiredAccess & AFS_ACCESS_WRITE)
357         rights |= PRSFS_WRITE;
358
359     lock_ObtainMutex(&scp->mx);
360
361     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
362                       CM_SCACHESYNC_GETSTATUS
363                      | CM_SCACHESYNC_NEEDCALLBACK
364                      | CM_SCACHESYNC_LOCK);
365
366     /*
367      * If the open will fail because the volume is readonly, then we will
368      * return an access denied error instead.  This is to help brain-dead
369      * apps run correctly on replicated volumes.
370      * See defect 10007 for more information.
371      */
372     if (code == CM_ERROR_READONLY)
373         code = CM_ERROR_NOACCESS;
374
375     if (code == 0 &&
376              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
377              scp->fileType == CM_SCACHETYPE_FILE) {
378         cm_key_t key;
379         unsigned int sLockType;
380         LARGE_INTEGER LOffset, LLength;
381
382         /* Check if there's some sort of lock on the file at the
383            moment. */
384
385         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
386         if (rights & PRSFS_WRITE)
387             sLockType = 0;
388         else
389             sLockType = LOCKING_ANDX_SHARED_LOCK;
390
391         /* single byte lock at offset 0x0100 0000 0000 0000 */
392         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
393         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
394         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
395         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
396
397         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
398
399         if (code == 0) {
400             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
401             if (!*ldpp) {
402                 code = ENOMEM;
403                 goto _done;
404             }
405
406             (*ldpp)->key = key;
407             (*ldpp)->sLockType = sLockType;
408             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
409             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
410             (*ldpp)->LLength.HighPart = LLength.HighPart;
411             (*ldpp)->LLength.LowPart = LLength.LowPart;
412         } else {
413             /* In this case, we allow the file open to go through even
414                though we can't enforce mandatory locking on the
415                file. */
416             if (code == CM_ERROR_NOACCESS &&
417                 !(rights & PRSFS_WRITE))
418                 code = 0;
419             else {
420                 switch (code) {
421                 case CM_ERROR_ALLOFFLINE:
422                 case CM_ERROR_ALLDOWN:
423                 case CM_ERROR_ALLBUSY:
424                 case CM_ERROR_TIMEDOUT:
425                 case CM_ERROR_RETRY:
426                 case CM_ERROR_WOULDBLOCK:
427                     break;
428                 default:
429                     code = CM_ERROR_SHARING_VIOLATION;
430                 }
431             }
432         }
433     } else if (code != 0) {
434         goto _done;
435     }
436
437     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
438
439  _done:
440     lock_ReleaseMutex(&scp->mx);
441
442     return code;
443 }
444
445 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
446                                cm_lock_data_t ** ldpp)
447 {
448     if (*ldpp) {
449         lock_ObtainMutex(&scp->mx);
450         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength, 
451                   (*ldpp)->key, userp, reqp);
452         lock_ReleaseMutex(&scp->mx);
453         free(*ldpp);
454         *ldpp = NULL;
455     }
456     return 0;
457 }
458 /*
459  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
460  * done in three steps:
461  * (1) open for deletion (NT_CREATE_AND_X)
462  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
463  * (3) close (CLOSE)
464  * We must not do the RPC until step 3.  But if we are going to return an error
465  * code (e.g. directory not empty), we must return it by step 2, otherwise most
466  * clients will not notice it.  So we do a preliminary check.  For deleting
467  * files, this is almost free, since we have already done the RPC to get the
468  * parent directory's status bits.  But for deleting directories, we must do an
469  * additional RPC to get the directory's data to check if it is empty.  Sigh.
470  */
471 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
472         cm_req_t *reqp)
473 {
474     long code;
475     osi_hyper_t thyper;
476     cm_buf_t *bufferp;
477     cm_dirEntry_t *dep;
478     unsigned short *hashTable;
479     unsigned int i, idx;
480     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
481
482     /* First check permissions */
483     lock_ObtainMutex(&scp->mx);
484     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
485                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
486     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
487     lock_ReleaseMutex(&scp->mx);
488     if (code)
489         return code;
490
491     /* If deleting directory, must be empty */
492
493     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
494         return code;
495
496     thyper.HighPart = 0; thyper.LowPart = 0;
497     lock_ObtainRead(&scp->bufCreateLock);
498     code = buf_Get(scp, &thyper, &bufferp);
499     lock_ReleaseRead(&scp->bufCreateLock);
500     if (code)
501         return code;
502
503     lock_ObtainMutex(&bufferp->mx);
504     lock_ObtainMutex(&scp->mx);
505     while (1) {
506         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
507                           CM_SCACHESYNC_NEEDCALLBACK
508                           | CM_SCACHESYNC_READ
509                           | CM_SCACHESYNC_BUFLOCKED);
510         if (code)
511             break;
512
513         if (cm_HaveBuffer(scp, bufferp, 1))
514             break;
515
516         /* otherwise, load the buffer and try again */
517         lock_ReleaseMutex(&bufferp->mx);
518         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
519         lock_ReleaseMutex(&scp->mx);
520         lock_ObtainMutex(&bufferp->mx);
521         lock_ObtainMutex(&scp->mx);
522         cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
523         if (code)
524             break;
525     }
526
527     /* We try to determine emptiness without looking beyond the first page,
528      * and without assuming "." and ".." are present and are on the first
529      * page (though these assumptions might, after all, be reasonable).
530      */
531     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
532     for (i=0; i<128; i++) {
533         idx = ntohs(hashTable[i]);
534         while (idx) {
535             if (idx >= 64) {
536                 BeyondPage = 1;
537                 break;
538             }
539             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
540             if (strcmp(dep->name, ".") == 0)
541                 HaveDot = 1;
542             else if (strcmp(dep->name, "..") == 0)
543                 HaveDotDot = 1;
544             else {
545                 code = CM_ERROR_NOTEMPTY;
546                 goto done;
547             }
548             idx = ntohs(dep->next);
549         }
550     }
551     if (BeyondPage && HaveDot && HaveDotDot)
552         code = CM_ERROR_NOTEMPTY;
553     else
554         code = 0;
555   done:   
556     lock_ReleaseMutex(&bufferp->mx);
557     buf_Release(bufferp);
558     lock_ReleaseMutex(&scp->mx);
559     return code;
560 }       
561
562 /*
563  * Iterate through all entries in a directory.
564  * When the function funcp is called, the buffer is locked but the
565  * directory vnode is not.
566  *
567  * If the retscp parameter is not NULL, the parmp must be a 
568  * cm_lookupSearch_t object.  
569  */
570 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
571                   osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
572                   cm_scache_t **retscp)
573 {
574     char *tp;
575     long code;
576     cm_dirEntry_t *dep;
577     cm_buf_t *bufferp;
578     long temp;
579     osi_hyper_t dirLength;
580     osi_hyper_t bufferOffset;
581     osi_hyper_t curOffset;
582     osi_hyper_t thyper;
583     long entryInDir;
584     long entryInBuffer;
585     cm_pageHeader_t *pageHeaderp;
586     int slotInPage;
587     long nextEntryCookie;
588     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
589         
590     /* get the directory size */
591     lock_ObtainMutex(&scp->mx);
592     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
593                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
594     lock_ReleaseMutex(&scp->mx);
595     if (code)
596         return code;
597         
598     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
599         return CM_ERROR_NOTDIR;
600
601     if (retscp)                         /* if this is a lookup call */
602     {
603         cm_lookupSearch_t*      sp = parmp;
604
605         if (
606 #ifdef AFS_FREELANCE_CLIENT
607         /* Freelance entries never end up in the DNLC because they
608          * do not have an associated cm_server_t
609          */
610             !(cm_freelanceEnabled &&
611             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
612               sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
613 #else /* !AFS_FREELANCE_CLIENT */
614             TRUE
615 #endif
616             ) 
617         {
618             int casefold = sp->caseFold;
619             sp->caseFold = 0; /* we have a strong preference for exact matches */
620             if ( *retscp = cm_dnlcLookup(scp, sp))      /* dnlc hit */
621             {
622                 sp->caseFold = casefold;
623                 return 0;
624             }
625             sp->caseFold = casefold;
626
627             /* see if we can find it using the directory hash tables.
628                we can only do exact matches, since the hash is case
629                sensitive. */
630             {
631                 cm_dirOp_t dirop;
632 #ifdef USE_BPLUS
633                 int usedBplus = 0;
634 #endif
635
636                 code = ENOENT;
637
638                 code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ, &dirop);
639                 if (code == 0) {
640
641 #ifdef USE_BPLUS
642                     code = cm_BPlusDirLookup(&dirop, sp->searchNamep, &sp->fid);
643                     if (code != EINVAL)
644                         usedBplus = 1;
645                     else 
646 #endif
647                         code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
648
649                     cm_EndDirOp(&dirop);
650                 }
651
652                 if (code == 0) {
653                     /* found it */
654                     sp->found = TRUE;
655                     sp->ExactFound = TRUE;
656                     *retscp = NULL; /* force caller to call cm_GetSCache() */
657                     return 0;
658                 }
659 #ifdef USE_BPLUS
660                 if (usedBplus) {
661                     if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
662                         /* found it */
663                         sp->found = TRUE;
664                         sp->ExactFound = FALSE;
665                         *retscp = NULL; /* force caller to call cm_GetSCache() */
666                         return 0;
667                     }
668                     
669                     return CM_ERROR_BPLUS_NOMATCH;
670                 }
671 #endif 
672             }
673         }
674     }   
675
676     /*
677      * XXX We only get the length once.  It might change when we drop the
678      * lock.
679      */
680     dirLength = scp->length;
681
682     bufferp = NULL;
683     bufferOffset.LowPart = bufferOffset.HighPart = 0;
684     if (startOffsetp)
685         curOffset = *startOffsetp;
686     else {
687         curOffset.HighPart = 0;
688         curOffset.LowPart = 0;
689     }   
690
691     while (1) {
692         /* make sure that curOffset.LowPart doesn't point to the first
693          * 32 bytes in the 2nd through last dir page, and that it
694          * doesn't point at the first 13 32-byte chunks in the first
695          * dir page, since those are dir and page headers, and don't
696          * contain useful information.
697          */
698         temp = curOffset.LowPart & (2048-1);
699         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
700             /* we're in the first page */
701             if (temp < 13*32) temp = 13*32;
702         }
703         else {
704             /* we're in a later dir page */
705             if (temp < 32) temp = 32;
706         }       
707                 
708         /* make sure the low order 5 bits are zero */
709         temp &= ~(32-1);
710                 
711         /* now put temp bits back ito curOffset.LowPart */
712         curOffset.LowPart &= ~(2048-1);
713         curOffset.LowPart |= temp;
714
715         /* check if we've passed the dir's EOF */
716         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
717             break;
718                 
719         /* see if we can use the bufferp we have now; compute in which
720          * page the current offset would be, and check whether that's
721          * the offset of the buffer we have.  If not, get the buffer.
722          */
723         thyper.HighPart = curOffset.HighPart;
724         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
725         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
726             /* wrong buffer */
727             if (bufferp) {
728                 lock_ReleaseMutex(&bufferp->mx);
729                 buf_Release(bufferp);
730                 bufferp = NULL;
731             }
732
733             lock_ObtainRead(&scp->bufCreateLock);
734             code = buf_Get(scp, &thyper, &bufferp);
735             lock_ReleaseRead(&scp->bufCreateLock);
736             if (code) {
737                 /* if buf_Get() fails we do not have a buffer object to lock */
738                 bufferp = NULL;
739                 break;
740             }
741
742 #ifdef AFSIFS
743             /* for the IFS version, we bulkstat the dirents because this
744                routine is used in place of smb_ReceiveCoreSearchDir.  our
745                other option is to modify smb_ReceiveCoreSearchDir itself, 
746                but this seems to be the proper use for cm_ApplyDir. */
747             lock_ObtainMutex(&scp->mx);
748             if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
749                  && (scp->bulkStatProgress.QuadPart <= thyper.QuadPart))
750             {
751                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
752                 code = cm_TryBulkStat(scp, &thyper, userp, reqp);
753                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
754                 scp->bulkStatProgress = thyper;
755             }
756             lock_ReleaseMutex(&scp->mx);
757 #endif
758
759             lock_ObtainMutex(&bufferp->mx);
760             bufferOffset = thyper;
761
762             /* now get the data in the cache */
763             while (1) {
764                 lock_ObtainMutex(&scp->mx);
765                 code = cm_SyncOp(scp, bufferp, userp, reqp,
766                                   PRSFS_LOOKUP,
767                                   CM_SCACHESYNC_NEEDCALLBACK
768                                   | CM_SCACHESYNC_READ
769                                   | CM_SCACHESYNC_BUFLOCKED);
770                 if (code) {
771                     lock_ReleaseMutex(&scp->mx);
772                     break;
773                 }
774                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
775                                 
776                 if (cm_HaveBuffer(scp, bufferp, 1)) {
777                     lock_ReleaseMutex(&scp->mx);
778                     break;
779                 }
780
781                 /* otherwise, load the buffer and try again */
782                 lock_ReleaseMutex(&bufferp->mx);
783                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
784                                     reqp);
785                 lock_ReleaseMutex(&scp->mx);
786                 lock_ObtainMutex(&bufferp->mx);
787                 if (code) 
788                     break;
789             }
790             if (code) {
791                 lock_ReleaseMutex(&bufferp->mx);
792                 buf_Release(bufferp);
793                 bufferp = NULL;
794                 break;
795             }
796         }       /* if (wrong buffer) ... */
797            
798         /* now we have the buffer containing the entry we're interested
799          * in; copy it out if it represents a non-deleted entry.
800          */
801         entryInDir = curOffset.LowPart & (2048-1);
802         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
803
804         /* page header will help tell us which entries are free.  Page
805          * header can change more often than once per buffer, since
806          * AFS 3 dir page size may be less than (but not more than) a
807          * buffer package buffer.
808          */
809         /* only look intra-buffer */
810         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
811         temp &= ~(2048 - 1);    /* turn off intra-page bits */
812         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
813
814         /* now determine which entry we're looking at in the page.  If
815          * it is free (there's a free bitmap at the start of the dir),
816          * we should skip these 32 bytes.
817          */
818         slotInPage = (entryInDir & 0x7e0) >> 5;
819         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
820                & (1 << (slotInPage & 0x7)))) {
821             /* this entry is free */
822             numDirChunks = 1;   /* only skip this guy */
823             goto nextEntry;
824         }
825
826         tp = bufferp->datap + entryInBuffer;
827         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
828
829         /* while we're here, compute the next entry's location, too,
830          * since we'll need it when writing out the cookie into the
831          * dir listing stream.
832          */
833         numDirChunks = cm_NameEntries(dep->name, NULL);
834                 
835         /* compute the offset of the cookie representing the next entry */
836         nextEntryCookie = curOffset.LowPart
837             + (CM_DIR_CHUNKSIZE * numDirChunks);
838
839         if (dep->fid.vnode != 0) {
840             /* this is one of the entries to use: it is not deleted */
841             code = (*funcp)(scp, dep, parmp, &curOffset);
842             if (code) 
843                 break;
844         }       /* if we're including this name */
845                 
846       nextEntry:
847         /* and adjust curOffset to be where the new cookie is */
848         thyper.HighPart = 0;
849         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
850         curOffset = LargeIntegerAdd(thyper, curOffset);
851     }           /* while copying data for dir listing */
852
853     /* release the mutex */
854     if (bufferp) {
855         lock_ReleaseMutex(&bufferp->mx);
856         buf_Release(bufferp);
857     }
858     return code;
859 }
860
861 int cm_NoneUpper(char *s)
862 {
863     char c;
864     while (c = *s++)
865         if (c >= 'A' && c <= 'Z')
866             return 0;
867     return 1;
868 }
869
870 int cm_NoneLower(char *s)
871 {
872     char c;
873     while (c = *s++)
874         if (c >= 'a' && c <= 'z')
875             return 0;
876     return 1;
877 }
878
879 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
880                           osi_hyper_t *offp)
881 {
882     cm_lookupSearch_t *sp;
883     int match;
884     char shortName[13];
885     char *matchName;
886
887     sp = (cm_lookupSearch_t *) rockp;
888
889     matchName = dep->name;
890     if (sp->caseFold)
891         match = cm_stricmp(matchName, sp->searchNamep);
892     else
893         match = strcmp(matchName, sp->searchNamep);
894
895     if (match != 0
896          && sp->hasTilde
897          && !cm_Is8Dot3(dep->name)) {
898         matchName = shortName;
899         cm_Gen8Dot3Name(dep, shortName, NULL);
900         if (sp->caseFold)
901             match = cm_stricmp(matchName, sp->searchNamep);
902         else
903             match = strcmp(matchName, sp->searchNamep);
904     }
905
906     if (match != 0)
907         return 0;
908
909     sp->found = 1;
910     if (!sp->caseFold) 
911         sp->ExactFound = 1;
912
913     if (!sp->caseFold || matchName == shortName) {
914         sp->fid.vnode = ntohl(dep->fid.vnode);
915         sp->fid.unique = ntohl(dep->fid.unique);
916         return CM_ERROR_STOPNOW;
917     }
918
919     /*
920      * If we get here, we are doing a case-insensitive search, and we
921      * have found a match.  Now we determine what kind of match it is:
922      * exact, lower-case, upper-case, or none of the above.  This is done
923      * in order to choose among matches, if there are more than one.
924      */
925
926     /* Exact matches are the best. */
927     match = strcmp(matchName, sp->searchNamep);
928     if (match == 0) {
929         sp->ExactFound = 1;
930         sp->fid.vnode = ntohl(dep->fid.vnode);
931         sp->fid.unique = ntohl(dep->fid.unique);
932         return CM_ERROR_STOPNOW;
933     }
934
935     /* Lower-case matches are next. */
936     if (sp->LCfound)
937         return 0;
938     if (cm_NoneUpper(matchName)) {
939         sp->LCfound = 1;
940         goto inexact;
941     }
942
943     /* Upper-case matches are next. */
944     if (sp->UCfound)
945         return 0;
946     if (cm_NoneLower(matchName)) {
947         sp->UCfound = 1;
948         goto inexact;
949     }
950
951     /* General matches are last. */
952     if (sp->NCfound)
953         return 0;
954     sp->NCfound = 1;
955
956   inexact:
957     sp->fid.vnode = ntohl(dep->fid.vnode);
958     sp->fid.unique = ntohl(dep->fid.unique);
959     return 0;
960 }       
961
962 /* read the contents of a mount point into the appropriate string.
963  * called with locked scp, and returns with locked scp.
964  */
965 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
966 {
967     long code;
968     cm_buf_t *bufp;
969     osi_hyper_t thyper;
970     int tlen;
971
972     if (scp->mountPointStringp[0]) 
973         return 0;
974         
975     /* otherwise, we have to read it in */
976     lock_ReleaseMutex(&scp->mx);
977
978     lock_ObtainRead(&scp->bufCreateLock);
979     thyper.LowPart = thyper.HighPart = 0;
980     code = buf_Get(scp, &thyper, &bufp);
981     lock_ReleaseRead(&scp->bufCreateLock);
982
983     lock_ObtainMutex(&scp->mx);
984     if (code)
985         return code;
986
987     while (1) {
988         code = cm_SyncOp(scp, bufp, userp, reqp, 0,
989                           CM_SCACHESYNC_READ | CM_SCACHESYNC_NEEDCALLBACK);
990         if (code)
991             goto done;
992
993         cm_SyncOpDone(scp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
994
995         if (cm_HaveBuffer(scp, bufp, 0)) 
996             break;
997
998         /* otherwise load buffer */
999         code = cm_GetBuffer(scp, bufp, NULL, userp, reqp);
1000         if (code)
1001             goto done;
1002     }
1003     /* locked, has callback, has valid data in buffer */
1004     if ((tlen = scp->length.LowPart) > MOUNTPOINTLEN - 1) 
1005         return CM_ERROR_TOOBIG;
1006     if (tlen <= 0) {
1007         code = CM_ERROR_INVAL;
1008         goto done;
1009     }
1010
1011     /* someone else did the work while we were out */
1012     if (scp->mountPointStringp[0]) {
1013         code = 0;
1014         goto done;
1015     }
1016
1017     /* otherwise, copy out the link */
1018     memcpy(scp->mountPointStringp, bufp->datap, tlen);
1019
1020     /* now make it null-terminated.  Note that the original contents of a
1021      * link that is a mount point is "#volname." where "." is there just to
1022      * be turned into a null.  That is, we can trash the last char of the
1023      * link without damaging the vol name.  This is a stupid convention,
1024      * but that's the protocol.
1025      */
1026     scp->mountPointStringp[tlen-1] = 0;
1027     code = 0;
1028
1029   done:
1030     if (bufp) 
1031         buf_Release(bufp);
1032     return code;
1033 }
1034
1035
1036 /* called with a locked scp and chases the mount point, yielding outScpp.
1037  * scp remains locked, just for simplicity of describing the interface.
1038  */
1039 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
1040                          cm_req_t *reqp, cm_scache_t **outScpp)
1041 {
1042     char *cellNamep;
1043     char *volNamep;
1044     int tlen;
1045     long code;
1046     char *cp;
1047     char *mpNamep;
1048     cm_volume_t *volp = NULL;
1049     cm_cell_t *cellp;
1050     char mtType;
1051     cm_fid_t tfid;
1052     size_t vnLength;
1053     int targetType;
1054
1055     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
1056         tfid = scp->mountRootFid;
1057         lock_ReleaseMutex(&scp->mx);
1058         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1059         lock_ObtainMutex(&scp->mx);
1060         return code;
1061     }
1062
1063     /* parse the volume name */
1064     mpNamep = scp->mountPointStringp;
1065     if (!mpNamep[0])
1066         return CM_ERROR_NOSUCHPATH;
1067     tlen = (int)strlen(scp->mountPointStringp);
1068     mtType = *scp->mountPointStringp;
1069     cellNamep = malloc(tlen);
1070     volNamep = malloc(tlen);
1071
1072     cp = strrchr(mpNamep, ':');
1073     if (cp) {
1074         /* cellular mount point */
1075         memset(cellNamep, 0, tlen);
1076         strncpy(cellNamep, mpNamep+1, cp - mpNamep - 1);
1077         strcpy(volNamep, cp+1);
1078         /* now look up the cell */
1079         lock_ReleaseMutex(&scp->mx);
1080         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
1081         lock_ObtainMutex(&scp->mx);
1082     }
1083     else {
1084         /* normal mt pt */
1085         strcpy(volNamep, mpNamep+1);
1086
1087         cellp = cm_FindCellByID(scp->fid.cell);
1088     }
1089
1090     if (!cellp) {
1091         code = CM_ERROR_NOSUCHCELL;
1092         goto done;
1093     }
1094
1095     vnLength = strlen(volNamep);
1096     if (vnLength >= 8 && strcmp(volNamep + vnLength - 7, ".backup") == 0)
1097         targetType = BACKVOL;
1098     else if (vnLength >= 10
1099               && strcmp(volNamep + vnLength - 9, ".readonly") == 0)
1100         targetType = ROVOL;
1101     else
1102         targetType = RWVOL;
1103
1104     /* check for backups within backups */
1105     if (targetType == BACKVOL
1106          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
1107          == CM_SCACHEFLAG_RO) {
1108         code = CM_ERROR_NOSUCHVOLUME;
1109         goto done;
1110     }
1111
1112     /* now we need to get the volume */
1113     lock_ReleaseMutex(&scp->mx);
1114     if (cm_VolNameIsID(volNamep)) {
1115         code = cm_GetVolumeByID(cellp, atoi(volNamep), userp, reqp, 
1116                                 CM_GETVOL_FLAG_CREATE, &volp);
1117     } else {
1118         code = cm_GetVolumeByName(cellp, volNamep, userp, reqp, 
1119                                   CM_GETVOL_FLAG_CREATE, &volp);
1120     }
1121     lock_ObtainMutex(&scp->mx);
1122         
1123     if (code == 0) {
1124         /* save the parent of the volume root for this is the 
1125          * place where the volume is mounted and we must remember 
1126          * this in the volume structure rather than just in the 
1127          * scache entry lest the scache entry gets recycled 
1128          * (defect 11489)
1129          */
1130         lock_ObtainMutex(&volp->mx);
1131         volp->dotdotFid = dscp->fid;
1132         lock_ReleaseMutex(&volp->mx);
1133
1134         scp->mountRootFid.cell = cellp->cellID;
1135         
1136         /* if the mt pt originates in a .backup volume (not a .readonly)
1137          * and FollowBackupPath is active, and if there is a .backup
1138          * volume for the target, then use the .backup of the target
1139          * instead of the read-write.
1140          */
1141         if (cm_followBackupPath && targetType == RWVOL &&
1142             (scp->flags & CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO) == CM_SCACHEFLAG_RO &&
1143             volp->bk.ID != 0) {
1144             targetType = BACKVOL;
1145         } 
1146         /* if the mt pt is in a read-only volume (not just a
1147          * backup), and if there is a read-only volume for the
1148          * target, and if this is a targetType '#' mount point, use
1149          * the read-only, otherwise use the one specified.
1150          */
1151         else if (mtType == '#' && targetType == RWVOL && 
1152                  (scp->flags & CM_SCACHEFLAG_PURERO) && 
1153                  volp->ro.ID != 0) {
1154             targetType = ROVOL;
1155         }
1156         if (targetType == ROVOL)
1157             scp->mountRootFid.volume = volp->ro.ID;
1158         else if (targetType == BACKVOL)
1159             scp->mountRootFid.volume = volp->bk.ID;
1160         else
1161             scp->mountRootFid.volume = volp->rw.ID;
1162
1163         /* the rest of the fid is a magic number */
1164         scp->mountRootFid.vnode = 1;
1165         scp->mountRootFid.unique = 1;
1166         scp->mountRootGen = cm_data.mountRootGen;
1167
1168         tfid = scp->mountRootFid;
1169         lock_ReleaseMutex(&scp->mx);
1170         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1171         lock_ObtainMutex(&scp->mx);
1172     }
1173
1174   done:
1175     if (volp)
1176         cm_PutVolume(volp);
1177     free(cellNamep);
1178     free(volNamep);
1179     return code;
1180 }       
1181
1182 long cm_LookupInternal(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1183                        cm_req_t *reqp, cm_scache_t **outpScpp)
1184 {
1185     long code;
1186     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1187     cm_scache_t *tscp = NULL;
1188     cm_scache_t *mountedScp;
1189     cm_lookupSearch_t rock;
1190     int getroot;
1191
1192     memset(&rock, 0, sizeof(rock));
1193
1194     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1195          && strcmp(namep, "..") == 0) {
1196         if (dscp->dotdotFid.volume == 0)
1197             return CM_ERROR_NOSUCHVOLUME;
1198         rock.fid = dscp->dotdotFid;
1199         goto haveFid;
1200     } else if (strcmp(namep, ".") == 0) {
1201         rock.fid = dscp->fid;
1202         goto haveFid;
1203     }
1204
1205     if (flags & CM_FLAG_NOMOUNTCHASE) {
1206         /* In this case, we should go and call cm_Dir* functions
1207            directly since the following cm_ApplyDir() function will
1208            not. */
1209
1210         cm_dirOp_t dirop;
1211 #ifdef USE_BPLUS
1212         int usedBplus = 0;
1213 #endif
1214
1215         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ, &dirop);
1216         if (code == 0) {
1217 #ifdef USE_BPLUS
1218             code = cm_BPlusDirLookup(&dirop, namep, &rock.fid);
1219             if (code != EINVAL)
1220                 usedBplus = 1;
1221             else
1222 #endif
1223                 code = cm_DirLookup(&dirop, namep, &rock.fid);
1224
1225             cm_EndDirOp(&dirop);
1226         }
1227
1228         if (code == 0) {
1229             /* found it */
1230             rock.found = TRUE;
1231             goto haveFid;
1232         }
1233 #ifdef USE_BPLUS
1234         if (usedBplus) {
1235             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1236                 /* found it */
1237                 code = 0;
1238                 rock.found = TRUE;
1239                 goto haveFid;
1240             }
1241             
1242             return CM_ERROR_BPLUS_NOMATCH;
1243         }
1244 #endif
1245     }
1246
1247     rock.fid.cell = dscp->fid.cell;
1248     rock.fid.volume = dscp->fid.volume;
1249     rock.searchNamep = namep;
1250     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1251     rock.hasTilde = ((strchr(namep, '~') != NULL) ? 1 : 0);
1252
1253     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1254     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1255                         (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1256
1257     /* code == 0 means we fell off the end of the dir, while stopnow means
1258      * that we stopped early, probably because we found the entry we're
1259      * looking for.  Any other non-zero code is an error.
1260      */
1261     if (code && code != CM_ERROR_STOPNOW) {
1262         /* if the cm_scache_t we are searching in is not a directory 
1263          * we must return path not found because the error 
1264          * is to describe the final component not an intermediary
1265          */
1266         if (code == CM_ERROR_NOTDIR) {
1267             if (flags & CM_FLAG_CHECKPATH)
1268                 return CM_ERROR_NOSUCHPATH;
1269             else
1270                 return CM_ERROR_NOSUCHFILE;
1271         }
1272         return code;
1273     }
1274
1275     getroot = (dscp==cm_data.rootSCachep) ;
1276     if (!rock.found) {
1277         if (!cm_freelanceEnabled || !getroot) {
1278             if (flags & CM_FLAG_CHECKPATH)
1279                 return CM_ERROR_NOSUCHPATH;
1280             else
1281                 return CM_ERROR_NOSUCHFILE;
1282         }
1283         else {  /* nonexistent dir on freelance root, so add it */
1284             char fullname[200] = ".";
1285             int  found = 0;
1286
1287             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %s", 
1288                       osi_LogSaveString(afsd_logp,namep));
1289             if (namep[0] == '.') {
1290                 if (cm_GetCell_Gen(&namep[1], &fullname[1], CM_FLAG_CREATE)) {
1291                     found = 1;
1292                     if ( stricmp(&namep[1], &fullname[1]) )
1293                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1294                     else
1295                         code = cm_FreelanceAddMount(namep, &fullname[1], "root.cell.", 1, &rock.fid);
1296                 }
1297             } else {
1298                 if (cm_GetCell_Gen(namep, fullname, CM_FLAG_CREATE)) {
1299                     found = 1;
1300                     if ( stricmp(namep, fullname) )
1301                         code = cm_FreelanceAddSymlink(namep, fullname, &rock.fid);
1302                     else
1303                         code = cm_FreelanceAddMount(namep, fullname, "root.cell.", 0, &rock.fid);
1304                 }
1305             }
1306             if (!found || code < 0) {   /* add mount point failed, so give up */
1307                 if (flags & CM_FLAG_CHECKPATH)
1308                     return CM_ERROR_NOSUCHPATH;
1309                 else
1310                     return CM_ERROR_NOSUCHFILE;
1311             }
1312             tscp = NULL;   /* to force call of cm_GetSCache */
1313         }
1314     }
1315
1316   haveFid:       
1317     if ( !tscp )    /* we did not find it in the dnlc */
1318     {
1319         dnlcHit = 0;    
1320         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1321         if (code) 
1322             return code;
1323     }       
1324     /* tscp is now held */
1325
1326     lock_ObtainMutex(&tscp->mx);
1327     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1328                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1329     if (code) { 
1330         lock_ReleaseMutex(&tscp->mx);
1331         cm_ReleaseSCache(tscp);
1332         return code;
1333     }
1334     cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1335     /* tscp is now locked */
1336
1337     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1338          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1339         /* mount points are funny: they have a volume name to mount
1340          * the root of.
1341          */
1342         code = cm_ReadMountPoint(tscp, userp, reqp);
1343         if (code == 0)
1344             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1345                                         &mountedScp);
1346         lock_ReleaseMutex(&tscp->mx);
1347         cm_ReleaseSCache(tscp);
1348         if (code) {
1349             return code;
1350         }
1351         tscp = mountedScp;
1352     }
1353     else {
1354         lock_ReleaseMutex(&tscp->mx);
1355     }
1356
1357     /* copy back pointer */
1358     *outpScpp = tscp;
1359
1360     /* insert scache in dnlc */
1361     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1362         /* lock the directory entry to prevent racing callback revokes */
1363         lock_ObtainMutex(&dscp->mx);
1364         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 )
1365             cm_dnlcEnter(dscp, namep, tscp);
1366         lock_ReleaseMutex(&dscp->mx);
1367     }
1368
1369     /* and return */
1370     return 0;
1371 }
1372
1373 int cm_ExpandSysName(char *inp, char *outp, long outSize, unsigned int index)
1374 {
1375     char *tp;
1376     int prefixCount;
1377
1378     tp = strrchr(inp, '@');
1379     if (tp == NULL) 
1380         return 0;               /* no @sys */
1381
1382     if (strcmp(tp, "@sys") != 0) 
1383         return 0;       /* no @sys */
1384
1385     /* caller just wants to know if this is a valid @sys type of name */
1386     if (outp == NULL) 
1387         return 1;
1388
1389     if (index >= MAXNUMSYSNAMES)
1390         return -1;
1391
1392     /* otherwise generate the properly expanded @sys name */
1393     prefixCount = (int)(tp - inp);
1394
1395     strncpy(outp, inp, prefixCount);    /* copy out "a." from "a.@sys" */
1396     outp[prefixCount] = 0;              /* null terminate the "a." */
1397     strcat(outp, cm_sysNameList[index]);/* append i386_nt40 */
1398     return 1;
1399 }   
1400
1401 long cm_EvaluateVolumeReference(char * namep, long flags, cm_user_t * userp,
1402                                 cm_req_t *reqp, cm_scache_t ** outpScpp)
1403 {
1404     long          code = 0;
1405     char          cellName[CELL_MAXNAMELEN];
1406     char          volumeName[VL_MAXNAMELEN];
1407     size_t        len;
1408     char *        cp;
1409     char *        tp;
1410
1411     cm_cell_t *   cellp = NULL;
1412     cm_volume_t * volp = NULL;
1413     cm_fid_t      fid;
1414     int           volType;
1415     int           mountType = RWVOL;
1416
1417     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%s]",
1418              osi_LogSaveString(afsd_logp, namep));
1419
1420     if (strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) != 0) {
1421         goto _exit_invalid_path;
1422     }
1423
1424     /* namep is assumed to look like the following:
1425
1426        @vol:<cellname>%<volume>\0
1427        or
1428        @vol:<cellname>#<volume>\0
1429
1430      */
1431
1432     cp = namep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1433     tp = strchr(cp, '%');
1434     if (tp == NULL)
1435         tp = strchr(cp, '#');
1436     if (tp == NULL ||
1437         (len = tp - cp) == 0 ||
1438         len > CELL_MAXNAMELEN)
1439         goto _exit_invalid_path;
1440     strncpy(cellName, cp, len);
1441     cellName[len] = '\0';
1442
1443     if (*tp == '#')
1444         mountType = ROVOL;
1445
1446     cp = tp+1;                  /* cp now points to volume, supposedly */
1447     strncpy(volumeName, cp, VL_MAXNAMELEN-1);
1448     volumeName[VL_MAXNAMELEN - 1] = 0;
1449
1450     /* OK, now we have the cell and the volume */
1451     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1452              osi_LogSaveString(afsd_logp, cellName),
1453              osi_LogSaveString(afsd_logp, volumeName));
1454
1455     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1456     if (cellp == NULL) {
1457         goto _exit_invalid_path;
1458     }
1459
1460     len = strlen(volumeName);
1461     if (len >= 8 && strcmp(volumeName + len - 7, ".backup") == 0)
1462         volType = BACKVOL;
1463     else if (len >= 10 &&
1464              strcmp(volumeName + len - 9, ".readonly") == 0)
1465         volType = ROVOL;
1466     else
1467         volType = RWVOL;
1468
1469     if (cm_VolNameIsID(volumeName)) {
1470         code = cm_GetVolumeByID(cellp, atoi(volumeName), userp, reqp,
1471                                 CM_GETVOL_FLAG_CREATE, &volp);
1472     } else {
1473         code = cm_GetVolumeByName(cellp, volumeName, userp, reqp,
1474                                   CM_GETVOL_FLAG_CREATE, &volp);
1475     }
1476
1477     if (code != 0)
1478         goto _exit_cleanup;
1479
1480     fid.cell = cellp->cellID;
1481
1482     if (volType == BACKVOL)
1483         fid.volume = volp->bk.ID;
1484     else if (volType == ROVOL ||
1485              (volType == RWVOL && mountType == ROVOL && volp->ro.ID != 0))
1486         fid.volume = volp->ro.ID;
1487     else
1488         fid.volume = volp->rw.ID;
1489
1490     fid.vnode = 1;
1491     fid.unique = 1;
1492
1493     code = cm_GetSCache(&fid, outpScpp, userp, reqp);
1494
1495  _exit_cleanup:
1496     if (volp)
1497         cm_PutVolume(volp);
1498
1499     if (code == 0)
1500         return code;
1501
1502  _exit_invalid_path:
1503     if (flags & CM_FLAG_CHECKPATH)
1504         return CM_ERROR_NOSUCHPATH;
1505     else
1506         return CM_ERROR_NOSUCHFILE;
1507 }
1508
1509 #ifdef DEBUG_REFCOUNT
1510 long cm_LookupDbg(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1511                cm_req_t *reqp, cm_scache_t **outpScpp, char * file, long line)
1512 #else
1513 long cm_Lookup(cm_scache_t *dscp, char *namep, long flags, cm_user_t *userp,
1514                cm_req_t *reqp, cm_scache_t **outpScpp)
1515 #endif
1516 {
1517     long code;
1518     char tname[AFSPATHMAX];
1519     int sysNameIndex = 0;
1520     cm_scache_t *scp = NULL;
1521
1522 #ifdef DEBUG_REFCOUNT
1523     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1524     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1525 #endif
1526
1527     if ( stricmp(namep,SMB_IOCTL_FILENAME_NOSLASH) == 0 ) {
1528         if (flags & CM_FLAG_CHECKPATH)
1529             return CM_ERROR_NOSUCHPATH;
1530         else
1531             return CM_ERROR_NOSUCHFILE;
1532     }
1533
1534     if (dscp == cm_data.rootSCachep &&
1535         strnicmp(namep, CM_PREFIX_VOL, CM_PREFIX_VOL_CCH) == 0) {
1536         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outpScpp);
1537     }
1538
1539     if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1540         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1541             code = cm_ExpandSysName(namep, tname, sizeof(tname), sysNameIndex);
1542             if (code > 0) {
1543                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1544 #ifdef DEBUG_REFCOUNT
1545                 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1546                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1547 #endif
1548
1549                 if (code == 0) {
1550                     *outpScpp = scp;
1551                     return 0;
1552                 }
1553                 if (scp) {
1554                     cm_ReleaseSCache(scp);
1555                     scp = NULL;
1556                 }
1557             } else {
1558                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1559 #ifdef DEBUG_REFCOUNT
1560                 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1561                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1562 #endif
1563                 *outpScpp = scp;
1564                 return code;
1565             }
1566         }
1567     } else {
1568         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1569 #ifdef DEBUG_REFCOUNT
1570         afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1571         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1572 #endif
1573         *outpScpp = scp;
1574         return code;
1575     }
1576
1577     /* None of the possible sysName expansions could be found */
1578     if (flags & CM_FLAG_CHECKPATH)
1579         return CM_ERROR_NOSUCHPATH;
1580     else
1581         return CM_ERROR_NOSUCHFILE;
1582 }
1583
1584 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1585 {
1586     long code;
1587     cm_conn_t *connp;
1588     AFSFid afsFid;
1589     int sflags;
1590     AFSFetchStatus newDirStatus;
1591     AFSVolSync volSync;
1592     struct rx_connection * callp;
1593     cm_dirOp_t dirop;
1594
1595 #ifdef AFS_FREELANCE_CLIENT
1596     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1597         /* deleting a mount point from the root dir. */
1598         code = cm_FreelanceRemoveMount(namep);
1599         return code;
1600     }
1601 #endif  
1602
1603     /* make sure we don't screw up the dir status during the merge */
1604     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
1605
1606     lock_ObtainMutex(&dscp->mx);
1607     sflags = CM_SCACHESYNC_STOREDATA;
1608     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1609     lock_ReleaseMutex(&dscp->mx);
1610     if (code) {
1611         cm_EndDirOp(&dirop);
1612         return code;
1613     }
1614
1615     /* make the RPC */
1616     afsFid.Volume = dscp->fid.volume;
1617     afsFid.Vnode = dscp->fid.vnode;
1618     afsFid.Unique = dscp->fid.unique;
1619
1620     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1621     do {
1622         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1623         if (code) 
1624             continue;
1625
1626         callp = cm_GetRxConn(connp);
1627         code = RXAFS_RemoveFile(callp, &afsFid, namep,
1628                                  &newDirStatus, &volSync);
1629         rx_PutConnection(callp);
1630
1631     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1632     code = cm_MapRPCError(code, reqp);
1633
1634     if (code)
1635         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1636     else
1637         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1638
1639     if (dirop.scp) {
1640         lock_ObtainWrite(&dirop.scp->dirlock);
1641         dirop.lockType = CM_DIRLOCK_WRITE;
1642     }
1643     lock_ObtainMutex(&dscp->mx);
1644     cm_dnlcRemove(dscp, namep);
1645     cm_SyncOpDone(dscp, NULL, sflags);
1646     if (code == 0) {
1647         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
1648     } else if (code == CM_ERROR_NOSUCHFILE) {
1649         /* windows would not have allowed the request to delete the file 
1650          * if it did not believe the file existed.  therefore, we must 
1651          * have an inconsistent view of the world.
1652          */
1653         dscp->cbServerp = NULL;
1654     }
1655     lock_ReleaseMutex(&dscp->mx);
1656
1657     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop)) {
1658         cm_DirDeleteEntry(&dirop, namep);
1659 #ifdef USE_BPLUS
1660         cm_BPlusDirDeleteEntry(&dirop, namep);
1661 #endif
1662     }
1663     cm_EndDirOp(&dirop);
1664
1665     return code;
1666 }
1667
1668 /* called with a locked vnode, and fills in the link info.
1669  * returns this the vnode still locked.
1670  */
1671 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1672 {
1673     long code;
1674     cm_buf_t *bufp;
1675     long temp;
1676     osi_hyper_t thyper;
1677
1678     lock_AssertMutex(&linkScp->mx);
1679     if (!linkScp->mountPointStringp[0]) {
1680         /* read the link data */
1681         lock_ReleaseMutex(&linkScp->mx);
1682         thyper.LowPart = thyper.HighPart = 0;
1683         code = buf_Get(linkScp, &thyper, &bufp);
1684         lock_ObtainMutex(&linkScp->mx);
1685         if (code) 
1686             return code;
1687         while (1) {
1688             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1689                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1690             if (code) {
1691                 buf_Release(bufp);
1692                 return code;
1693             }
1694             cm_SyncOpDone(linkScp, bufp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1695
1696             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1697                 break;
1698
1699             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1700             if (code) {
1701                 buf_Release(bufp);
1702                 return code;
1703             }
1704         } /* while loop to get the data */
1705                 
1706         /* now if we still have no link read in,
1707          * copy the data from the buffer */
1708         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1709             buf_Release(bufp);
1710             return CM_ERROR_TOOBIG;
1711         }
1712
1713         /* otherwise, it fits; make sure it is still null (could have
1714          * lost race with someone else referencing this link above),
1715          * and if so, copy in the data.
1716          */
1717         if (!linkScp->mountPointStringp[0]) {
1718             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1719             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1720         }
1721         buf_Release(bufp);
1722     }   /* don't have sym link contents cached */
1723
1724     return 0;
1725 }       
1726
1727 /* called with a held vnode and a path suffix, with the held vnode being a
1728  * symbolic link.  Our goal is to generate a new path to interpret, and return
1729  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1730  * other than the directory containing the symbolic link, then the new root is
1731  * returned in *newRootScpp, otherwise a null is returned there.
1732  */
1733 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1734                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1735                       cm_user_t *userp, cm_req_t *reqp)
1736 {
1737     long code = 0;
1738     long len;
1739     char *linkp;
1740     cm_space_t *tsp;
1741
1742     lock_ObtainMutex(&linkScp->mx);
1743     code = cm_HandleLink(linkScp, userp, reqp);
1744     if (code) 
1745         goto done;
1746
1747     /* if we may overflow the buffer, bail out; buffer is signficantly
1748      * bigger than max path length, so we don't really have to worry about
1749      * being a little conservative here.
1750      */
1751     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1752          >= CM_UTILS_SPACESIZE)
1753         return CM_ERROR_TOOBIG;
1754
1755     tsp = cm_GetSpace();
1756     linkp = linkScp->mountPointStringp;
1757     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1758         if (strlen(linkp) > cm_mountRootLen)
1759             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1760         else
1761             tsp->data[0] = 0;
1762         *newRootScpp = cm_data.rootSCachep;
1763         cm_HoldSCache(cm_data.rootSCachep);
1764     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1765         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName)))) 
1766         {
1767             char * p = &linkp[len + 3];
1768             if (strnicmp(p, "all", 3) == 0)
1769                 p += 4;
1770
1771             strcpy(tsp->data, p);
1772             for (p = tsp->data; *p; p++) {
1773                 if (*p == '\\')
1774                     *p = '/';
1775             }
1776             *newRootScpp = cm_data.rootSCachep;
1777             cm_HoldSCache(cm_data.rootSCachep);
1778         } else {
1779             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1780             strcpy(tsp->data, linkp);
1781             *newRootScpp = NULL;
1782             code = CM_ERROR_PATH_NOT_COVERED;
1783         }
1784     } else if ( !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1785         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1786         strcpy(tsp->data, linkp);
1787         *newRootScpp = NULL;
1788         code = CM_ERROR_PATH_NOT_COVERED;
1789     } else if (*linkp == '\\' || *linkp == '/') {
1790 #if 0   
1791         /* formerly, this was considered to be from the AFS root,
1792          * but this seems to create problems.  instead, we will just
1793          * reject the link */
1794         strcpy(tsp->data, linkp+1);
1795         *newRootScpp = cm_data.rootSCachep;
1796         cm_HoldSCache(cm_data.rootSCachep);
1797 #else
1798         /* we still copy the link data into the response so that 
1799          * the user can see what the link points to
1800          */
1801         linkScp->fileType = CM_SCACHETYPE_INVALID;
1802         strcpy(tsp->data, linkp);
1803         *newRootScpp = NULL;
1804         code = CM_ERROR_NOSUCHPATH;
1805 #endif  
1806     } else {
1807         /* a relative link */
1808         strcpy(tsp->data, linkp);
1809         *newRootScpp = NULL;
1810     }
1811     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1812         strcat(tsp->data, "\\");
1813         strcat(tsp->data, pathSuffixp);
1814     }
1815     *newSpaceBufferp = tsp;
1816
1817   done:
1818     lock_ReleaseMutex(&linkScp->mx);
1819     return code;
1820 }
1821 #ifdef DEBUG_REFCOUNT
1822 long cm_NameIDbg(cm_scache_t *rootSCachep, char *pathp, long flags,
1823                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp, 
1824                char * file, long line)
1825 #else
1826 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1827                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1828 #endif
1829 {
1830     long code;
1831     char *tp;                   /* ptr moving through input buffer */
1832     char tc;                    /* temp char */
1833     int haveComponent;          /* has new component started? */
1834     char component[AFSPATHMAX]; /* this is the new component */
1835     char *cp;                   /* component name being assembled */
1836     cm_scache_t *tscp;          /* current location in the hierarchy */
1837     cm_scache_t *nscp;          /* next dude down */
1838     cm_scache_t *dirScp;        /* last dir we searched */
1839     cm_scache_t *linkScp;       /* new root for the symlink we just
1840     * looked up */
1841     cm_space_t *psp;            /* space for current path, if we've hit
1842     * any symlinks */
1843     cm_space_t *tempsp;         /* temp vbl */
1844     char *restp;                /* rest of the pathname to interpret */
1845     int symlinkCount;           /* count of # of symlinks traversed */
1846     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1847     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1848 #define MAX_FID_COUNT 512
1849     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1850     int fid_count = 0;          /* number of fids processed in this path walk */
1851     int i;
1852
1853 #ifdef DEBUG_REFCOUNT
1854     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1855     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %s tidpath %s flags 0x%x",
1856               rootSCachep, pathp ? pathp : "<NULL>", tidPathp ? tidPathp : "<NULL>", 
1857               flags);
1858 #endif
1859
1860     tp = tidPathp;
1861     if (tp == NULL) {
1862         tp = pathp;
1863         phase = 2;
1864     }
1865     if (tp == NULL) {
1866         tp = "";
1867     }
1868     haveComponent = 0;
1869     psp = NULL;
1870     tscp = rootSCachep;
1871     cm_HoldSCache(tscp);
1872     symlinkCount = 0;
1873     dirScp = NULL;
1874
1875
1876     while (1) {
1877         tc = *tp++;
1878
1879         /* map Unix slashes into DOS ones so we can interpret Unix
1880          * symlinks properly
1881          */
1882         if (tc == '/') 
1883             tc = '\\';
1884
1885         if (!haveComponent) {
1886             if (tc == '\\') {
1887                 continue;
1888             } else if (tc == 0) {
1889                 if (phase == 1) {
1890                     phase = 2;
1891                     tp = pathp;
1892                     continue;
1893                 }
1894                 code = 0;
1895                 break;
1896             } else {
1897                 haveComponent = 1;
1898                 cp = component;
1899                 *cp++ = tc;
1900             }
1901         } else {
1902             /* we have a component here */
1903             if (tc == 0 || tc == '\\') {
1904                 /* end of the component; we're at the last
1905                  * component if tc == 0.  However, if the last
1906                  * is a symlink, we have more to do.
1907                  */
1908                 *cp++ = 0;      /* add null termination */
1909                 extraFlag = 0;
1910                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1911                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1912                 code = cm_Lookup(tscp, component,
1913                                   flags | extraFlag,
1914                                   userp, reqp, &nscp);
1915
1916                 if (code == 0) {
1917                     if (!strcmp(component,"..") || !strcmp(component,".")) {
1918                         /* 
1919                          * roll back the fid list until we find the fid 
1920                          * that matches where we are now.  Its not necessarily
1921                          * one or two fids because they might have been 
1922                          * symlinks or mount points or both that were crossed.  
1923                          */
1924                         for ( i=fid_count-1; i>=0; i--) {
1925                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
1926                                 break;
1927                         }
1928                     } else {
1929                         /* add the new fid to the list */
1930                         for ( i=0; i<fid_count; i++) {
1931                             if ( !cm_FidCmp(&nscp->fid, &fids[i]) ) {
1932                                 code = CM_ERROR_TOO_MANY_SYMLINKS;
1933                                 cm_ReleaseSCache(nscp);
1934                                 nscp = NULL;
1935                                 break;
1936                             }
1937                         }
1938                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
1939                             fids[fid_count++] = nscp->fid;
1940                         }
1941                     }
1942                 }
1943
1944                 if (code) {
1945                     cm_ReleaseSCache(tscp);
1946                     if (dirScp)
1947                         cm_ReleaseSCache(dirScp);
1948                     if (psp) 
1949                         cm_FreeSpace(psp);
1950                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) && 
1951                          tscp->fileType == CM_SCACHETYPE_SYMLINK) 
1952                     {
1953                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
1954                         return CM_ERROR_NOSUCHPATH;
1955                     } else {
1956                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
1957                         return code;
1958                     }
1959                 }
1960
1961                 haveComponent = 0;      /* component done */
1962                 if (dirScp)
1963                     cm_ReleaseSCache(dirScp);
1964                 dirScp = tscp;          /* for some symlinks */
1965                 tscp = nscp;            /* already held */
1966                 nscp = NULL;
1967                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1968                     code = 0;
1969                     if (dirScp) {
1970                         cm_ReleaseSCache(dirScp);
1971                         dirScp = NULL;
1972                     }
1973                     break;
1974                 }
1975
1976                 /* now, if tscp is a symlink, we should follow
1977                  * it and assemble the path again.
1978                  */
1979                 lock_ObtainMutex(&tscp->mx);
1980                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1981                                   CM_SCACHESYNC_GETSTATUS
1982                                   | CM_SCACHESYNC_NEEDCALLBACK);
1983                 if (code) {
1984                     lock_ReleaseMutex(&tscp->mx);
1985                     cm_ReleaseSCache(tscp);
1986                     tscp = NULL;
1987                     if (dirScp) {
1988                         cm_ReleaseSCache(dirScp);
1989                         dirScp = NULL;
1990                     }
1991                     break;
1992                 }
1993                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1994
1995                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1996                     /* this is a symlink; assemble a new buffer */
1997                     lock_ReleaseMutex(&tscp->mx);
1998                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1999                         cm_ReleaseSCache(tscp);
2000                         tscp = NULL;
2001                         if (dirScp) {
2002                             cm_ReleaseSCache(dirScp);
2003                             dirScp = NULL;
2004                         }
2005                         if (psp) 
2006                             cm_FreeSpace(psp);
2007                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2008                         return CM_ERROR_TOO_MANY_SYMLINKS;
2009                     }
2010                     if (tc == 0) 
2011                         restp = "";
2012                     else 
2013                         restp = tp;
2014                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
2015
2016                     if (code == 0 && linkScp != NULL) {
2017                         if (linkScp == cm_data.rootSCachep) 
2018                             fid_count = 0;
2019                         else {
2020                             for ( i=0; i<fid_count; i++) {
2021                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2022                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2023                                     cm_ReleaseSCache(linkScp);
2024                                     nscp = NULL;
2025                                     break;
2026                                 }
2027                             }
2028                         }
2029                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2030                             fids[fid_count++] = linkScp->fid;
2031                         }
2032                     }
2033
2034                     if (code) {
2035                         /* something went wrong */
2036                         cm_ReleaseSCache(tscp);
2037                         tscp = NULL;
2038                         if (dirScp) {
2039                             cm_ReleaseSCache(dirScp);
2040                             dirScp = NULL;
2041                         }
2042                         break;
2043                     }
2044
2045                     /* otherwise, tempsp has the new path,
2046                      * and linkScp is the new root from
2047                      * which to interpret that path.
2048                      * Continue with the namei processing,
2049                      * also doing the bookkeeping for the
2050                      * space allocation and tracking the
2051                      * vnode reference counts.
2052                      */
2053                     if (psp) 
2054                         cm_FreeSpace(psp);
2055                     psp = tempsp;
2056                     tp = psp->data;
2057                     cm_ReleaseSCache(tscp);
2058                     tscp = linkScp;
2059                     linkScp = NULL;
2060                     /* already held
2061                      * by AssembleLink
2062                      * now, if linkScp is null, that's
2063                      * AssembleLink's way of telling us that
2064                      * the sym link is relative to the dir
2065                      * containing the link.  We have a ref
2066                      * to it in dirScp, and we hold it now
2067                      * and reuse it as the new spot in the
2068                      * dir hierarchy.
2069                      */
2070                     if (tscp == NULL) {
2071                         tscp = dirScp;
2072                         dirScp = NULL;
2073                     }
2074                 } else {
2075                     /* not a symlink, we may be done */
2076                     lock_ReleaseMutex(&tscp->mx);
2077                     if (tc == 0) {
2078                         if (phase == 1) {
2079                             phase = 2;
2080                             tp = pathp;
2081                             continue;
2082                         }
2083                         if (dirScp) {
2084                             cm_ReleaseSCache(dirScp);
2085                             dirScp = NULL;
2086                         }
2087                         code = 0;
2088                         break;
2089                     }
2090                 }
2091                 if (dirScp) {
2092                     cm_ReleaseSCache(dirScp);
2093                     dirScp = NULL;
2094                 }
2095             } /* end of a component */
2096             else 
2097                 *cp++ = tc;
2098         } /* we have a component */
2099     } /* big while loop over all components */
2100
2101     /* already held */
2102     if (dirScp)
2103         cm_ReleaseSCache(dirScp);
2104     if (psp) 
2105         cm_FreeSpace(psp);
2106     if (code == 0) 
2107         *outScpp = tscp;
2108     else if (tscp)
2109         cm_ReleaseSCache(tscp);
2110
2111 #ifdef DEBUG_REFCOUNT
2112     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp)->refCount);
2113 #endif
2114     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2115     return code;
2116 }
2117
2118 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2119  * We chase the link, and return a held pointer to the target, if it exists,
2120  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2121  * and do not hold or return a target vnode.
2122  *
2123  * This is very similar to calling cm_NameI with the last component of a name,
2124  * which happens to be a symlink, except that we've already passed by the name.
2125  *
2126  * This function is typically called by the directory listing functions, which
2127  * encounter symlinks but need to return the proper file length so programs
2128  * like "more" work properly when they make use of the attributes retrieved from
2129  * the dir listing.
2130  *
2131  * The input vnode should not be locked when this function is called.
2132  */
2133 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2134                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2135 {
2136     long code;
2137     cm_space_t *spacep;
2138     cm_scache_t *newRootScp;
2139
2140     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2141
2142     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2143     if (code) 
2144         return code;
2145
2146     /* now, if newRootScp is NULL, we're really being told that the symlink
2147      * is relative to the current directory (dscp).
2148      */
2149     if (newRootScp == NULL) {
2150         newRootScp = dscp;
2151         cm_HoldSCache(dscp);
2152     }
2153
2154     code = cm_NameI(newRootScp, spacep->data,
2155                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2156                      userp, NULL, reqp, outScpp);
2157
2158     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2159         code = CM_ERROR_NOSUCHPATH;
2160
2161     /* this stuff is allocated no matter what happened on the namei call,
2162      * so free it */
2163     cm_FreeSpace(spacep);
2164     cm_ReleaseSCache(newRootScp);
2165
2166     if (linkScp == *outScpp) {
2167         cm_ReleaseSCache(*outScpp);
2168         *outScpp = NULL;
2169         code = CM_ERROR_NOSUCHPATH;
2170     }
2171
2172     return code;
2173 }
2174
2175 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
2176  * check anyway, but we want to minimize the chance that we have to leave stuff
2177  * unstat'd.
2178  */
2179 #define CM_BULKMAX              (3 * AFSCBMAX)
2180
2181 /* rock for bulk stat calls */
2182 typedef struct cm_bulkStat {
2183     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
2184
2185     /* info for the actual call */
2186     int counter;                        /* next free slot */
2187     AFSFid fids[CM_BULKMAX];
2188     AFSFetchStatus stats[CM_BULKMAX];
2189     AFSCallBack callbacks[CM_BULKMAX];
2190 } cm_bulkStat_t;
2191
2192 /* for a given entry, make sure that it isn't in the stat cache, and then
2193  * add it to the list of file IDs to be obtained.
2194  *
2195  * Don't bother adding it if we already have a vnode.  Note that the dir
2196  * is locked, so we have to be careful checking the vnode we're thinking of
2197  * processing, to avoid deadlocks.
2198  */
2199 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2200                      osi_hyper_t *offp)
2201 {
2202     osi_hyper_t thyper;
2203     cm_bulkStat_t *bsp;
2204     int i;
2205     cm_scache_t *tscp;
2206     cm_fid_t tfid;
2207
2208     bsp = rockp;
2209
2210     /* Don't overflow bsp. */
2211     if (bsp->counter >= CM_BULKMAX)
2212         return CM_ERROR_STOPNOW;
2213
2214     thyper.LowPart = cm_data.buf_blockSize;
2215     thyper.HighPart = 0;
2216     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2217
2218     /* thyper is now the first byte past the end of the record we're
2219      * interested in, and bsp->bufOffset is the first byte of the record
2220      * we're interested in.
2221      * Skip data in the others.
2222      * Skip '.' and '..'
2223      */
2224     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2225         return 0;
2226     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2227         return CM_ERROR_STOPNOW;
2228     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2229         return 0;
2230
2231     tfid.cell = scp->fid.cell;
2232     tfid.volume = scp->fid.volume;
2233     tfid.vnode = ntohl(dep->fid.vnode);
2234     tfid.unique = ntohl(dep->fid.unique);
2235     tscp = cm_FindSCache(&tfid);
2236     if (tscp) {
2237         if (lock_TryMutex(&tscp->mx)) {
2238             /* we have an entry that we can look at */
2239             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2240                 /* we have a callback on it.  Don't bother
2241                  * fetching this stat entry, since we're happy
2242                  * with the info we have.
2243                  */
2244                 lock_ReleaseMutex(&tscp->mx);
2245                 cm_ReleaseSCache(tscp);
2246                 return 0;
2247             }
2248             lock_ReleaseMutex(&tscp->mx);
2249         }       /* got lock */
2250         cm_ReleaseSCache(tscp);
2251     }   /* found entry */
2252
2253 #ifdef AFS_FREELANCE_CLIENT
2254     // yj: if this is a mountpoint under root.afs then we don't want it
2255     // to be bulkstat-ed, instead, we call getSCache directly and under
2256     // getSCache, it is handled specially.
2257     if  ( cm_freelanceEnabled &&
2258           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
2259           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2260           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2261     {       
2262         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2263         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2264     }
2265 #endif /* AFS_FREELANCE_CLIENT */
2266
2267     i = bsp->counter++;
2268     bsp->fids[i].Volume = scp->fid.volume;
2269     bsp->fids[i].Vnode = tfid.vnode;
2270     bsp->fids[i].Unique = tfid.unique;
2271     return 0;
2272 }       
2273
2274 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
2275  * calls on all undeleted files in the page of the directory specified.
2276  */
2277 afs_int32
2278 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2279                cm_req_t *reqp)
2280 {
2281     long code;
2282     cm_bulkStat_t bb;   /* this is *BIG*, probably 16K or so;
2283                          * watch for stack problems */
2284     AFSCBFids fidStruct;
2285     AFSBulkStats statStruct;
2286     cm_conn_t *connp;
2287     AFSCBs callbackStruct;
2288     long filex;
2289     AFSVolSync volSync;
2290     cm_callbackRequest_t cbReq;
2291     long filesThisCall;
2292     long i;
2293     long j;
2294     cm_scache_t *scp;
2295     cm_fid_t tfid;
2296     struct rx_connection * callp;
2297     int inlinebulk = 0;         /* Did we use InlineBulkStatus RPC or not? */
2298
2299     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2300
2301     /* should be on a buffer boundary */
2302     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2303
2304     memset(&bb, 0, sizeof(bb));
2305     bb.bufOffset = *offsetp;
2306
2307     lock_ReleaseMutex(&dscp->mx);
2308     /* first, assemble the file IDs we need to stat */
2309     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
2310
2311     /* if we failed, bail out early */
2312     if (code && code != CM_ERROR_STOPNOW) {
2313         lock_ObtainMutex(&dscp->mx);
2314         return code;
2315     }
2316
2317     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2318      * make the calls to create the entries.  Handle AFSCBMAX files at a
2319      * time.
2320      */
2321     filex = 0;
2322     while (filex < bb.counter) {
2323         filesThisCall = bb.counter - filex;
2324         if (filesThisCall > AFSCBMAX) 
2325             filesThisCall = AFSCBMAX;
2326
2327         fidStruct.AFSCBFids_len = filesThisCall;
2328         fidStruct.AFSCBFids_val = &bb.fids[filex];
2329         statStruct.AFSBulkStats_len = filesThisCall;
2330         statStruct.AFSBulkStats_val = &bb.stats[filex];
2331         callbackStruct.AFSCBs_len = filesThisCall;
2332         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
2333         cm_StartCallbackGrantingCall(NULL, &cbReq);
2334         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2335         do {
2336             code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2337             if (code) 
2338                 continue;
2339
2340             callp = cm_GetRxConn(connp);
2341             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2342                 code = RXAFS_InlineBulkStatus(callp, &fidStruct,
2343                                      &statStruct, &callbackStruct, &volSync);
2344                 if (code == RXGEN_OPCODE) {
2345                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2346                 } else {
2347                     inlinebulk = 1;
2348                 }
2349             }
2350             if (!inlinebulk) {
2351                 code = RXAFS_BulkStatus(callp, &fidStruct,
2352                                         &statStruct, &callbackStruct, &volSync);
2353             }
2354             rx_PutConnection(callp);
2355
2356         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
2357                              &volSync, NULL, &cbReq, code));
2358         code = cm_MapRPCError(code, reqp);
2359         if (code)
2360             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x", 
2361                       inlinebulk ? "Inline" : "", code);
2362         else
2363             osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2364
2365         /* may as well quit on an error, since we're not going to do
2366          * much better on the next immediate call, either.
2367          */
2368         if (code) {
2369             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2370             break;
2371         }
2372
2373         /* otherwise, we should do the merges */
2374         for (i = 0; i<filesThisCall; i++) {
2375             j = filex + i;
2376             tfid.cell = dscp->fid.cell;
2377             tfid.volume = bb.fids[j].Volume;
2378             tfid.vnode = bb.fids[j].Vnode;
2379             tfid.unique = bb.fids[j].Unique;
2380             code = cm_GetSCache(&tfid, &scp, userp, reqp);
2381             if (code != 0) 
2382                 continue;
2383
2384             /* otherwise, if this entry has no callback info, 
2385              * merge in this.
2386              */
2387             lock_ObtainMutex(&scp->mx);
2388             /* now, we have to be extra paranoid on merging in this
2389              * information, since we didn't use cm_SyncOp before
2390              * starting the fetch to make sure that no bad races
2391              * were occurring.  Specifically, we need to make sure
2392              * we don't obliterate any newer information in the
2393              * vnode than have here.
2394              *
2395              * Right now, be pretty conservative: if there's a
2396              * callback or a pending call, skip it.
2397              */
2398             if ((scp->cbServerp == NULL || (scp->flags & CM_SCACHEFLAG_EACCESS))
2399                  && !(scp->flags &
2400                        (CM_SCACHEFLAG_FETCHING
2401                          | CM_SCACHEFLAG_STORING
2402                          | CM_SCACHEFLAG_SIZESTORING))) {
2403                 cm_EndCallbackGrantingCall(scp, &cbReq,
2404                                             &bb.callbacks[j],
2405                                             CM_CALLBACK_MAINTAINCOUNT);
2406                 cm_MergeStatus(dscp, scp, &bb.stats[j], &volSync, userp, 0);
2407             }       
2408             lock_ReleaseMutex(&scp->mx);
2409             cm_ReleaseSCache(scp);
2410         } /* all files in the response */
2411         /* now tell it to drop the count,
2412          * after doing the vnode processing above */
2413         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2414
2415         filex += filesThisCall;
2416     }   /* while there are still more files to process */
2417     lock_ObtainMutex(&dscp->mx);
2418
2419     /* If we did the InlineBulk RPC pull out the return code and log it */
2420     if (inlinebulk) {
2421         if ((&bb.stats[0])->errorCode) {
2422             osi_Log1(afsd_logp, "cm_TryBulkStat bulk stat error: %d", 
2423                      (&bb.stats[0])->errorCode);
2424         }
2425     }
2426
2427     osi_Log0(afsd_logp, "END cm_TryBulkStat");
2428     return 0;
2429 }       
2430
2431 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2432 {
2433     long mask;
2434
2435     /* initialize store back mask as inexpensive local variable */
2436     mask = 0;
2437     memset(statusp, 0, sizeof(AFSStoreStatus));
2438
2439     /* copy out queued info from scache first, if scp passed in */
2440     if (scp) {
2441         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2442             statusp->ClientModTime = scp->clientModTime;
2443             mask |= AFS_SETMODTIME;
2444             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2445         }
2446     }
2447
2448     if (attrp) {
2449         /* now add in our locally generated request */
2450         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2451             statusp->ClientModTime = attrp->clientModTime;
2452             mask |= AFS_SETMODTIME;
2453         }
2454         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2455             statusp->UnixModeBits = attrp->unixModeBits;
2456             mask |= AFS_SETMODE;
2457         }
2458         if (attrp->mask & CM_ATTRMASK_OWNER) {
2459             statusp->Owner = attrp->owner;
2460             mask |= AFS_SETOWNER;
2461         }
2462         if (attrp->mask & CM_ATTRMASK_GROUP) {
2463             statusp->Group = attrp->group;
2464             mask |= AFS_SETGROUP;
2465         }
2466     }
2467     statusp->Mask = mask;
2468 }       
2469
2470 /* set the file size, and make sure that all relevant buffers have been
2471  * truncated.  Ensure that any partially truncated buffers have been zeroed
2472  * to the end of the buffer.
2473  */
2474 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2475                    cm_req_t *reqp)
2476 {
2477     long code;
2478     int shrinking;
2479
2480     /* start by locking out buffer creation */
2481     lock_ObtainWrite(&scp->bufCreateLock);
2482
2483     /* verify that this is a file, not a dir or a symlink */
2484     lock_ObtainMutex(&scp->mx);
2485     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2486                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2487     if (code) 
2488         goto done;
2489     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2490
2491     if (scp->fileType != CM_SCACHETYPE_FILE) {
2492         code = CM_ERROR_ISDIR;
2493         goto done;
2494     }
2495
2496   startover:
2497     if (LargeIntegerLessThan(*sizep, scp->length))
2498         shrinking = 1;
2499     else
2500         shrinking = 0;
2501
2502     lock_ReleaseMutex(&scp->mx);
2503
2504     /* can't hold scp->mx lock here, since we may wait for a storeback to
2505      * finish if the buffer package is cleaning a buffer by storing it to
2506      * the server.
2507      */
2508     if (shrinking)
2509         buf_Truncate(scp, userp, reqp, sizep);
2510
2511     /* now ensure that file length is short enough, and update truncPos */
2512     lock_ObtainMutex(&scp->mx);
2513
2514     /* make sure we have a callback (so we have the right value for the
2515      * length), and wait for it to be safe to do a truncate.
2516      */
2517     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2518                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2519                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2520
2521     /* If we only have 'i' bits, then we should still be able to set
2522        the size of a file we created. */
2523     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2524         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2525                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2526                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2527     }
2528
2529     if (code) 
2530         goto done;
2531
2532     if (LargeIntegerLessThan(*sizep, scp->length)) {
2533         /* a real truncation.  If truncPos is not set yet, or is bigger
2534          * than where we're truncating the file, set truncPos to this
2535          * new value.
2536          */
2537         if (!shrinking)
2538             goto startover;
2539         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2540              || LargeIntegerLessThan(*sizep, scp->length)) {
2541             /* set trunc pos */
2542             scp->truncPos = *sizep;
2543             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2544         }
2545         /* in either case, the new file size has been changed */
2546         scp->length = *sizep;
2547         scp->mask |= CM_SCACHEMASK_LENGTH;
2548     }
2549     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2550         /* really extending the file */
2551         scp->length = *sizep;
2552         scp->mask |= CM_SCACHEMASK_LENGTH;
2553     }
2554
2555     /* done successfully */
2556     code = 0;
2557
2558     cm_SyncOpDone(scp, NULL, 
2559                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2560                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2561
2562   done:
2563     lock_ReleaseMutex(&scp->mx);
2564     lock_ReleaseWrite(&scp->bufCreateLock);
2565
2566     return code;
2567 }
2568
2569 /* set the file size or other attributes (but not both at once) */
2570 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2571                 cm_req_t *reqp)
2572 {
2573     long code;
2574     AFSFetchStatus afsOutStatus;
2575     AFSVolSync volSync;
2576     cm_conn_t *connp;
2577     AFSFid tfid;
2578     AFSStoreStatus afsInStatus;
2579     struct rx_connection * callp;
2580
2581     /* handle file length setting */
2582     if (attrp->mask & CM_ATTRMASK_LENGTH)
2583         return cm_SetLength(scp, &attrp->length, userp, reqp);
2584
2585     lock_ObtainMutex(&scp->mx);
2586     /* otherwise, we have to make an RPC to get the status */
2587     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2588     if (code) {
2589         lock_ReleaseMutex(&scp->mx);
2590         return code;
2591     }
2592
2593     /* make the attr structure */
2594     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2595
2596     tfid.Volume = scp->fid.volume;
2597     tfid.Vnode = scp->fid.vnode;
2598     tfid.Unique = scp->fid.unique;
2599         lock_ReleaseMutex(&scp->mx);
2600
2601     /* now make the RPC */
2602     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2603     do {
2604         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2605         if (code) 
2606             continue;
2607
2608         callp = cm_GetRxConn(connp);
2609         code = RXAFS_StoreStatus(callp, &tfid,
2610                                   &afsInStatus, &afsOutStatus, &volSync);
2611         rx_PutConnection(callp);
2612
2613     } while (cm_Analyze(connp, userp, reqp,
2614                          &scp->fid, &volSync, NULL, NULL, code));
2615     code = cm_MapRPCError(code, reqp);
2616
2617     if (code)
2618         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2619     else
2620         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2621
2622     lock_ObtainMutex(&scp->mx);
2623     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2624     if (code == 0)
2625         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp,
2626                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2627         
2628     /* if we're changing the mode bits, discard the ACL cache, 
2629      * since we changed the mode bits.
2630      */
2631     if (afsInStatus.Mask & AFS_SETMODE) 
2632         cm_FreeAllACLEnts(scp);
2633     lock_ReleaseMutex(&scp->mx);
2634     return code;
2635 }       
2636
2637 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2638                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2639 {       
2640     cm_conn_t *connp;
2641     long code;
2642     AFSFid dirAFSFid;
2643     cm_callbackRequest_t cbReq;
2644     AFSFid newAFSFid;
2645     cm_fid_t newFid;
2646     cm_scache_t *scp = NULL;
2647     int didEnd;
2648     AFSStoreStatus inStatus;
2649     AFSFetchStatus updatedDirStatus;
2650     AFSFetchStatus newFileStatus;
2651     AFSCallBack newFileCallback;
2652     AFSVolSync volSync;
2653     struct rx_connection * callp;
2654     cm_dirOp_t dirop;
2655
2656     /* can't create names with @sys in them; must expand it manually first.
2657      * return "invalid request" if they try.
2658      */
2659     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2660         return CM_ERROR_ATSYS;
2661     }
2662
2663     /* before starting the RPC, mark that we're changing the file data, so
2664      * that someone who does a chmod will know to wait until our call
2665      * completes.
2666      */
2667     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2668     lock_ObtainMutex(&dscp->mx);
2669     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2670     lock_ReleaseMutex(&dscp->mx);
2671     if (code == 0) {
2672         cm_StartCallbackGrantingCall(NULL, &cbReq);
2673     } else {
2674         cm_EndDirOp(&dirop);
2675     }
2676     if (code) {
2677         return code;
2678     }
2679     didEnd = 0;
2680
2681     cm_StatusFromAttr(&inStatus, NULL, attrp);
2682
2683     /* try the RPC now */
2684     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2685     do {
2686         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2687         if (code) 
2688             continue;
2689
2690         dirAFSFid.Volume = dscp->fid.volume;
2691         dirAFSFid.Vnode = dscp->fid.vnode;
2692         dirAFSFid.Unique = dscp->fid.unique;
2693
2694         callp = cm_GetRxConn(connp);
2695         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2696                                  &inStatus, &newAFSFid, &newFileStatus,
2697                                  &updatedDirStatus, &newFileCallback,
2698                                  &volSync);
2699         rx_PutConnection(callp);
2700
2701     } while (cm_Analyze(connp, userp, reqp,
2702                          &dscp->fid, &volSync, NULL, &cbReq, code));
2703     code = cm_MapRPCError(code, reqp);
2704         
2705     if (code)
2706         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2707     else
2708         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2709
2710     if (dirop.scp) {
2711         lock_ObtainWrite(&dirop.scp->dirlock);
2712         dirop.lockType = CM_DIRLOCK_WRITE;
2713     }
2714     lock_ObtainMutex(&dscp->mx);
2715     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2716     if (code == 0) {
2717         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2718     }
2719     lock_ReleaseMutex(&dscp->mx);
2720
2721     /* now try to create the file's entry, too, but be careful to 
2722      * make sure that we don't merge in old info.  Since we weren't locking
2723      * out any requests during the file's creation, we may have pretty old
2724      * info.
2725      */
2726     if (code == 0) {
2727         newFid.cell = dscp->fid.cell;
2728         newFid.volume = dscp->fid.volume;
2729         newFid.vnode = newAFSFid.Vnode;
2730         newFid.unique = newAFSFid.Unique;
2731         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2732         if (code == 0) {
2733             lock_ObtainMutex(&scp->mx);
2734             scp->creator = userp;               /* remember who created it */
2735             if (!cm_HaveCallback(scp)) {
2736                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2737                                 userp, 0);
2738                 cm_EndCallbackGrantingCall(scp, &cbReq,
2739                                             &newFileCallback, 0);
2740                 didEnd = 1;     
2741             }       
2742             lock_ReleaseMutex(&scp->mx);
2743             *scpp = scp;
2744         }
2745     }
2746
2747     /* make sure we end things properly */
2748     if (!didEnd)
2749         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2750
2751     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2752         cm_DirCreateEntry(&dirop, namep, &newFid);
2753 #ifdef USE_BPLUS
2754         cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2755 #endif
2756     }
2757     cm_EndDirOp(&dirop);
2758
2759     return code;
2760 }       
2761
2762 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2763 {
2764     long code;
2765
2766     lock_ObtainWrite(&scp->bufCreateLock);
2767     code = buf_CleanVnode(scp, userp, reqp);
2768     lock_ReleaseWrite(&scp->bufCreateLock);
2769     if (code == 0) {
2770         lock_ObtainMutex(&scp->mx);
2771
2772         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2773                           | CM_SCACHEMASK_CLIENTMODTIME
2774                           | CM_SCACHEMASK_LENGTH))
2775             code = cm_StoreMini(scp, userp, reqp);
2776
2777         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2778             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2779             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2780         }
2781
2782         lock_ReleaseMutex(&scp->mx);
2783     }
2784     return code;
2785 }
2786
2787 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2788                  cm_user_t *userp, cm_req_t *reqp)
2789 {
2790     cm_conn_t *connp;
2791     long code;
2792     AFSFid dirAFSFid;
2793     cm_callbackRequest_t cbReq;
2794     AFSFid newAFSFid;
2795     cm_fid_t newFid;
2796     cm_scache_t *scp = NULL;
2797     int didEnd;
2798     AFSStoreStatus inStatus;
2799     AFSFetchStatus updatedDirStatus;
2800     AFSFetchStatus newDirStatus;
2801     AFSCallBack newDirCallback;
2802     AFSVolSync volSync;
2803     struct rx_connection * callp;
2804     cm_dirOp_t dirop;
2805
2806     /* can't create names with @sys in them; must expand it manually first.
2807      * return "invalid request" if they try.
2808      */
2809     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2810         return CM_ERROR_ATSYS;
2811     }
2812
2813     /* before starting the RPC, mark that we're changing the directory
2814      * data, so that someone who does a chmod on the dir will wait until
2815      * our call completes.
2816      */
2817     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2818     lock_ObtainMutex(&dscp->mx);
2819     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2820     lock_ReleaseMutex(&dscp->mx);
2821     if (code == 0) {
2822         cm_StartCallbackGrantingCall(NULL, &cbReq);
2823     } else {
2824         cm_EndDirOp(&dirop);
2825     }
2826     if (code) {
2827         return code;
2828     }
2829     didEnd = 0;
2830
2831     cm_StatusFromAttr(&inStatus, NULL, attrp);
2832
2833     /* try the RPC now */
2834     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2835     do {
2836         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2837         if (code) 
2838             continue;
2839
2840         dirAFSFid.Volume = dscp->fid.volume;
2841         dirAFSFid.Vnode = dscp->fid.vnode;
2842         dirAFSFid.Unique = dscp->fid.unique;
2843
2844         callp = cm_GetRxConn(connp);
2845         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2846                               &inStatus, &newAFSFid, &newDirStatus,
2847                               &updatedDirStatus, &newDirCallback,
2848                               &volSync);
2849         rx_PutConnection(callp);
2850
2851     } while (cm_Analyze(connp, userp, reqp,
2852                          &dscp->fid, &volSync, NULL, &cbReq, code));
2853     code = cm_MapRPCError(code, reqp);
2854         
2855     if (code)
2856         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2857     else
2858         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2859
2860     if (dirop.scp) {
2861         lock_ObtainWrite(&dirop.scp->dirlock);
2862         dirop.lockType = CM_DIRLOCK_WRITE;
2863     }
2864     lock_ObtainMutex(&dscp->mx);
2865     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2866     if (code == 0) {
2867         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2868     }
2869     lock_ReleaseMutex(&dscp->mx);
2870
2871     /* now try to create the new dir's entry, too, but be careful to 
2872      * make sure that we don't merge in old info.  Since we weren't locking
2873      * out any requests during the file's creation, we may have pretty old
2874      * info.
2875      */
2876     if (code == 0) {
2877         newFid.cell = dscp->fid.cell;
2878         newFid.volume = dscp->fid.volume;
2879         newFid.vnode = newAFSFid.Vnode;
2880         newFid.unique = newAFSFid.Unique;
2881         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2882         if (code == 0) {
2883             lock_ObtainMutex(&scp->mx);
2884             if (!cm_HaveCallback(scp)) {
2885                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
2886                                 userp, 0);
2887                 cm_EndCallbackGrantingCall(scp, &cbReq,
2888                                             &newDirCallback, 0);
2889                 didEnd = 1;             
2890             }
2891             lock_ReleaseMutex(&scp->mx);
2892             cm_ReleaseSCache(scp);
2893         }
2894     }
2895
2896     /* make sure we end things properly */
2897     if (!didEnd)
2898         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2899
2900     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2901         cm_DirCreateEntry(&dirop, namep, &newFid);
2902 #ifdef USE_BPLUS
2903         cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
2904 #endif
2905     }
2906     cm_EndDirOp(&dirop);
2907
2908     /* and return error code */
2909     return code;
2910 }       
2911
2912 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2913              cm_user_t *userp, cm_req_t *reqp)
2914 {
2915     cm_conn_t *connp;
2916     long code = 0;
2917     AFSFid dirAFSFid;
2918     AFSFid existingAFSFid;
2919     AFSFetchStatus updatedDirStatus;
2920     AFSFetchStatus newLinkStatus;
2921     AFSVolSync volSync;
2922     struct rx_connection * callp;
2923     cm_dirOp_t dirop;
2924
2925     if (dscp->fid.cell != sscp->fid.cell ||
2926         dscp->fid.volume != sscp->fid.volume) {
2927         return CM_ERROR_CROSSDEVLINK;
2928     }
2929
2930     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
2931     lock_ObtainMutex(&dscp->mx);
2932     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2933     lock_ReleaseMutex(&dscp->mx);
2934     if (code != 0)
2935         cm_EndDirOp(&dirop);
2936
2937     if (code)
2938         return code;
2939
2940     /* try the RPC now */
2941     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2942     do {
2943         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2944         if (code) continue;
2945
2946         dirAFSFid.Volume = dscp->fid.volume;
2947         dirAFSFid.Vnode = dscp->fid.vnode;
2948         dirAFSFid.Unique = dscp->fid.unique;
2949
2950         existingAFSFid.Volume = sscp->fid.volume;
2951         existingAFSFid.Vnode = sscp->fid.vnode;
2952         existingAFSFid.Unique = sscp->fid.unique;
2953
2954         callp = cm_GetRxConn(connp);
2955         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2956             &newLinkStatus, &updatedDirStatus, &volSync);
2957         rx_PutConnection(callp);
2958         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2959
2960     } while (cm_Analyze(connp, userp, reqp,
2961         &dscp->fid, &volSync, NULL, NULL, code));
2962
2963     code = cm_MapRPCError(code, reqp);
2964
2965     if (code)
2966         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2967     else
2968         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2969
2970     if (dirop.scp) {
2971         lock_ObtainWrite(&dirop.scp->dirlock);
2972         dirop.lockType = CM_DIRLOCK_WRITE;
2973     }
2974     lock_ObtainMutex(&dscp->mx);
2975     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2976     if (code == 0) {
2977         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
2978     }
2979     lock_ReleaseMutex(&dscp->mx);
2980
2981     if (code == 0) {
2982         if (cm_CheckDirOpForSingleChange(&dirop)) {
2983             cm_DirCreateEntry(&dirop, namep, &sscp->fid);
2984 #ifdef USE_BPLUS
2985             cm_BPlusDirCreateEntry(&dirop, namep, &sscp->fid);
2986 #endif
2987         }
2988     }
2989     cm_EndDirOp(&dirop);
2990
2991     return code;
2992 }
2993
2994 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2995                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2996 {
2997     cm_conn_t *connp;
2998     long code;
2999     AFSFid dirAFSFid;
3000     AFSFid newAFSFid;
3001     cm_fid_t newFid;
3002     cm_scache_t *scp;
3003     AFSStoreStatus inStatus;
3004     AFSFetchStatus updatedDirStatus;
3005     AFSFetchStatus newLinkStatus;
3006     AFSVolSync volSync;
3007     struct rx_connection * callp;
3008     cm_dirOp_t dirop;
3009
3010     /* before starting the RPC, mark that we're changing the directory data,
3011      * so that someone who does a chmod on the dir will wait until our
3012      * call completes.
3013      */
3014     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3015     lock_ObtainMutex(&dscp->mx);
3016     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3017     lock_ReleaseMutex(&dscp->mx);
3018     if (code != 0)
3019         cm_EndDirOp(&dirop);
3020     if (code) {
3021         return code;
3022     }
3023
3024     cm_StatusFromAttr(&inStatus, NULL, attrp);
3025
3026     /* try the RPC now */
3027     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3028     do {
3029         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3030         if (code) 
3031             continue;
3032
3033         dirAFSFid.Volume = dscp->fid.volume;
3034         dirAFSFid.Vnode = dscp->fid.vnode;
3035         dirAFSFid.Unique = dscp->fid.unique;
3036
3037         callp = cm_GetRxConn(connp);
3038         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
3039                               &inStatus, &newAFSFid, &newLinkStatus,
3040                               &updatedDirStatus, &volSync);
3041         rx_PutConnection(callp);
3042
3043     } while (cm_Analyze(connp, userp, reqp,
3044                          &dscp->fid, &volSync, NULL, NULL, code));
3045     code = cm_MapRPCError(code, reqp);
3046         
3047     if (code)
3048         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3049     else
3050         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3051
3052     if (dirop.scp) {
3053         lock_ObtainWrite(&dirop.scp->dirlock);
3054         dirop.lockType = CM_DIRLOCK_WRITE;
3055     }
3056     lock_ObtainMutex(&dscp->mx);
3057     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3058     if (code == 0) {
3059         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3060     }
3061     lock_ReleaseMutex(&dscp->mx);
3062
3063     if (code == 0) {
3064         if (cm_CheckDirOpForSingleChange(&dirop)) {
3065             newFid.cell = dscp->fid.cell;
3066             newFid.volume = dscp->fid.volume;
3067             newFid.vnode = newAFSFid.Vnode;
3068             newFid.unique = newAFSFid.Unique;
3069
3070             cm_DirCreateEntry(&dirop, namep, &newFid);
3071 #ifdef USE_BPLUS
3072             cm_BPlusDirCreateEntry(&dirop, namep, &newFid);
3073 #endif
3074         }
3075     }
3076     cm_EndDirOp(&dirop);
3077
3078     /* now try to create the new dir's entry, too, but be careful to 
3079      * make sure that we don't merge in old info.  Since we weren't locking
3080      * out any requests during the file's creation, we may have pretty old
3081      * info.
3082      */
3083     if (code == 0) {
3084         newFid.cell = dscp->fid.cell;
3085         newFid.volume = dscp->fid.volume;
3086         newFid.vnode = newAFSFid.Vnode;
3087         newFid.unique = newAFSFid.Unique;
3088         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3089         if (code == 0) {
3090             lock_ObtainMutex(&scp->mx);
3091             if (!cm_HaveCallback(scp)) {
3092                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3093                                 userp, 0);
3094             }       
3095             lock_ReleaseMutex(&scp->mx);
3096             cm_ReleaseSCache(scp);
3097         }
3098     }
3099         
3100     /* and return error code */
3101     return code;
3102 }
3103
3104 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
3105                    cm_req_t *reqp)
3106 {
3107     cm_conn_t *connp;
3108     long code;
3109     AFSFid dirAFSFid;
3110     int didEnd;
3111     AFSFetchStatus updatedDirStatus;
3112     AFSVolSync volSync;
3113     struct rx_connection * callp;
3114     cm_dirOp_t dirop;
3115
3116     /* before starting the RPC, mark that we're changing the directory data,
3117      * so that someone who does a chmod on the dir will wait until our
3118      * call completes.
3119      */
3120     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, &dirop);
3121     lock_ObtainMutex(&dscp->mx);
3122     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3123     lock_ReleaseMutex(&dscp->mx);
3124     if (code) {
3125         cm_EndDirOp(&dirop);
3126         return code;
3127     }
3128     didEnd = 0;
3129
3130     /* try the RPC now */
3131     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3132     do {
3133         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3134         if (code) 
3135             continue;
3136
3137         dirAFSFid.Volume = dscp->fid.volume;
3138         dirAFSFid.Vnode = dscp->fid.vnode;
3139         dirAFSFid.Unique = dscp->fid.unique;
3140
3141         callp = cm_GetRxConn(connp);
3142         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
3143                                 &updatedDirStatus, &volSync);
3144         rx_PutConnection(callp);
3145
3146     } while (cm_Analyze(connp, userp, reqp,
3147                          &dscp->fid, &volSync, NULL, NULL, code));
3148     code = cm_MapRPCErrorRmdir(code, reqp);
3149
3150     if (code)
3151         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3152     else
3153         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3154
3155     if (dirop.scp) {
3156         lock_ObtainWrite(&dirop.scp->dirlock);
3157         dirop.lockType = CM_DIRLOCK_WRITE;
3158     }
3159     lock_ObtainMutex(&dscp->mx);
3160     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3161     if (code == 0) {
3162         cm_dnlcRemove(dscp, namep); 
3163         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, CM_MERGEFLAG_DIROP);
3164     }
3165     lock_ReleaseMutex(&dscp->mx);
3166
3167     if (code == 0) {
3168         if (cm_CheckDirOpForSingleChange(&dirop)) {
3169             cm_DirDeleteEntry(&dirop, namep);
3170 #ifdef USE_BPLUS
3171             cm_BPlusDirDeleteEntry(&dirop, namep);
3172 #endif
3173         }
3174     }
3175     cm_EndDirOp(&dirop);
3176
3177     /* and return error code */
3178     return code;
3179 }
3180
3181 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3182 {
3183     /* grab mutex on contents */
3184     lock_ObtainMutex(&scp->mx);
3185
3186     /* reset the prefetch info */
3187     scp->prefetch.base.LowPart = 0;             /* base */
3188     scp->prefetch.base.HighPart = 0;
3189     scp->prefetch.end.LowPart = 0;              /* and end */
3190     scp->prefetch.end.HighPart = 0;
3191
3192     /* release mutex on contents */
3193     lock_ReleaseMutex(&scp->mx);
3194
3195     /* we're done */
3196     return 0;
3197 }       
3198
3199 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
3200                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
3201 {
3202     cm_conn_t *connp;
3203     long code;
3204     AFSFid oldDirAFSFid;
3205     AFSFid newDirAFSFid;
3206     int didEnd;
3207     AFSFetchStatus updatedOldDirStatus;
3208     AFSFetchStatus updatedNewDirStatus;
3209     AFSVolSync volSync;
3210     int oneDir;
3211     struct rx_connection * callp;
3212     cm_dirOp_t oldDirOp;
3213     cm_fid_t   fileFid;
3214     int        diropCode = -1;
3215     cm_dirOp_t newDirOp;
3216
3217     /* before starting the RPC, mark that we're changing the directory data,
3218      * so that someone who does a chmod on the dir will wait until our call
3219      * completes.  We do this in vnode order so that we don't deadlock,
3220      * which makes the code a little verbose.
3221      */
3222     if (oldDscp == newDscp) {
3223         /* check for identical names */
3224         if (strcmp(oldNamep, newNamep) == 0)
3225             return CM_ERROR_RENAME_IDENTICAL;
3226
3227         oneDir = 1;
3228         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3229         lock_ObtainMutex(&oldDscp->mx);
3230         cm_dnlcRemove(oldDscp, oldNamep);
3231         cm_dnlcRemove(oldDscp, newNamep);
3232         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3233                           CM_SCACHESYNC_STOREDATA);
3234         lock_ReleaseMutex(&oldDscp->mx);
3235         if (code != 0) {
3236             cm_EndDirOp(&oldDirOp);
3237         }
3238     }
3239     else {
3240         /* two distinct dir vnodes */
3241         oneDir = 0;
3242         if (oldDscp->fid.cell != newDscp->fid.cell ||
3243              oldDscp->fid.volume != newDscp->fid.volume)
3244             return CM_ERROR_CROSSDEVLINK;
3245
3246         /* shouldn't happen that we have distinct vnodes for two
3247          * different files, but could due to deliberate attack, or
3248          * stale info.  Avoid deadlocks and quit now.
3249          */
3250         if (oldDscp->fid.vnode == newDscp->fid.vnode)
3251             return CM_ERROR_CROSSDEVLINK;
3252
3253         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3254             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3255             lock_ObtainMutex(&oldDscp->mx);
3256             cm_dnlcRemove(oldDscp, oldNamep);
3257             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3258                               CM_SCACHESYNC_STOREDATA);
3259             lock_ReleaseMutex(&oldDscp->mx);
3260             if (code != 0)
3261                 cm_EndDirOp(&oldDirOp);
3262             if (code == 0) {
3263                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3264                 lock_ObtainMutex(&newDscp->mx);
3265                 cm_dnlcRemove(newDscp, newNamep);
3266                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3267                                   CM_SCACHESYNC_STOREDATA);
3268                 lock_ReleaseMutex(&newDscp->mx);
3269                 if (code) {
3270                     cm_EndDirOp(&newDirOp);
3271
3272                     /* cleanup first one */
3273                     lock_ObtainMutex(&oldDscp->mx);
3274                     cm_SyncOpDone(oldDscp, NULL,
3275                                    CM_SCACHESYNC_STOREDATA);
3276                     lock_ReleaseMutex(&oldDscp->mx);
3277                     cm_EndDirOp(&oldDirOp);
3278                 }       
3279             }
3280         }
3281         else {
3282             /* lock the new vnode entry first */
3283             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE, &newDirOp);
3284             lock_ObtainMutex(&newDscp->mx);
3285             cm_dnlcRemove(newDscp, newNamep);
3286             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3287                               CM_SCACHESYNC_STOREDATA);
3288             lock_ReleaseMutex(&newDscp->mx);
3289             if (code != 0)
3290                 cm_EndDirOp(&newDirOp);
3291             if (code == 0) {
3292                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE, &oldDirOp);
3293                 lock_ObtainMutex(&oldDscp->mx);
3294                 cm_dnlcRemove(oldDscp, oldNamep);
3295                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3296                                   CM_SCACHESYNC_STOREDATA);
3297                 lock_ReleaseMutex(&oldDscp->mx);
3298                 if (code != 0)
3299                     cm_EndDirOp(&oldDirOp);
3300                 if (code) {
3301                     /* cleanup first one */
3302                     lock_ObtainMutex(&newDscp->mx);
3303                     cm_SyncOpDone(newDscp, NULL,
3304                                    CM_SCACHESYNC_STOREDATA);
3305                     lock_ReleaseMutex(&newDscp->mx);
3306                     cm_EndDirOp(&newDirOp);
3307                 }       
3308             }
3309         }
3310     }   /* two distinct vnodes */
3311
3312     if (code) {
3313         return code;
3314     }
3315     didEnd = 0;
3316
3317     /* try the RPC now */
3318     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
3319               oldDscp, newDscp);
3320     do {
3321         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3322         if (code) 
3323             continue;
3324
3325         oldDirAFSFid.Volume = oldDscp->fid.volume;
3326         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3327         oldDirAFSFid.Unique = oldDscp->fid.unique;
3328         newDirAFSFid.Volume = newDscp->fid.volume;
3329         newDirAFSFid.Vnode = newDscp->fid.vnode;
3330         newDirAFSFid.Unique = newDscp->fid.unique;
3331
3332         callp = cm_GetRxConn(connp);
3333         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
3334                              &newDirAFSFid, newNamep,
3335                              &updatedOldDirStatus, &updatedNewDirStatus,
3336                              &volSync);
3337         rx_PutConnection(callp);
3338
3339     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3340                          &volSync, NULL, NULL, code));
3341     code = cm_MapRPCError(code, reqp);
3342         
3343     if (code)
3344         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3345     else
3346         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3347
3348     /* update the individual stat cache entries for the directories */
3349     if (oldDirOp.scp) {
3350         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3351         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3352     }
3353     lock_ObtainMutex(&oldDscp->mx);
3354     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3355
3356     if (code == 0)
3357         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3358                         userp, CM_MERGEFLAG_DIROP);
3359     lock_ReleaseMutex(&oldDscp->mx);
3360
3361     if (code == 0) {
3362         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3363
3364 #ifdef USE_BPLUS
3365             diropCode = cm_BPlusDirLookup(&oldDirOp, oldNamep, &fileFid);
3366             if (diropCode == CM_ERROR_INEXACT_MATCH)
3367                 diropCode = 0;
3368             else if (diropCode == EINVAL)
3369 #endif
3370                 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3371
3372             if (diropCode == 0) {
3373                 if (oneDir) {
3374                     diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3375 #ifdef USE_BPLUS
3376                     cm_BPlusDirCreateEntry(&oldDirOp, newNamep, &fileFid);
3377 #endif
3378                 }
3379
3380                 if (diropCode == 0) { 
3381                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3382 #ifdef USE_BPLUS
3383                     cm_BPlusDirDeleteEntry(&oldDirOp, oldNamep);
3384 #endif
3385                 }
3386             }
3387         }
3388     }
3389     cm_EndDirOp(&oldDirOp);
3390
3391     /* and update it for the new one, too, if necessary */
3392     if (!oneDir) {
3393         if (newDirOp.scp) {
3394             lock_ObtainWrite(&newDirOp.scp->dirlock);
3395             newDirOp.lockType = CM_DIRLOCK_WRITE;
3396         }
3397         lock_ObtainMutex(&newDscp->mx);
3398         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3399         if (code == 0)
3400             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3401                             userp, CM_MERGEFLAG_DIROP);
3402         lock_ReleaseMutex(&newDscp->mx);
3403
3404         if (code == 0) {
3405             /* we only make the local change if we successfully made
3406                the change in the old directory AND there was only one
3407                change in the new directory */
3408             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3409                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3410 #ifdef USE_BPLUS
3411                 cm_BPlusDirCreateEntry(&newDirOp, newNamep, &fileFid);
3412 #endif
3413             }
3414         }
3415         cm_EndDirOp(&newDirOp);
3416     }
3417
3418     /* and return error code */
3419     return code;
3420 }
3421
3422 /* Byte range locks:
3423
3424    The OpenAFS Windows client has to fake byte range locks given no
3425    server side support for such locks.  This is implemented as keyed
3426    byte range locks on the cache manager.
3427
3428    Keyed byte range locks:
3429
3430    Each cm_scache_t structure keeps track of a list of keyed locks.
3431    The key for a lock identifies an owner of a set of locks (referred
3432    to as a client).  Each key is represented by a value.  The set of
3433    key values used within a specific cm_scache_t structure form a
3434    namespace that has a scope of just that cm_scache_t structure.  The
3435    same key value can be used with another cm_scache_t structure and
3436    correspond to a completely different client.  However it is
3437    advantageous for the SMB or IFS layer to make sure that there is a
3438    1-1 mapping between client and keys over all cm_scache_t objects.
3439
3440    Assume a client C has key Key(C) (although, since the scope of the
3441    key is a cm_scache_t, the key can be Key(C,S), where S is the
3442    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3443    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3444    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3445    through cm_generateKey() function for both SMB and IFS.
3446
3447    The list of locks for a cm_scache_t object S is maintained in
3448    S->fileLocks.  The cache manager will set a lock on the AFS file
3449    server in order to assert the locks in S->fileLocks.  If only
3450    shared locks are in place for S, then the cache manager will obtain
3451    a LockRead lock, while if there are any exclusive locks, it will
3452    obtain a LockWrite lock.  If the exclusive locks are all released
3453    while the shared locks remain, then the cache manager will
3454    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3455    exclusive lock is obtained when only shared locks exist, then the
3456    cache manager will try to upgrade the lock from LockRead to
3457    LockWrite.
3458
3459    Each lock L owned by client C maintains a key L->key such that
3460    L->key == Key(C), the effective range defined by L->LOffset and
3461    L->LLength such that the range of bytes affected by the lock is
3462    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3463    is either exclusive or shared.
3464
3465    Lock states:
3466
3467    A lock exists iff it is in S->fileLocks for some cm_scache_t
3468    S. Existing locks are in one of the following states: ACTIVE,
3469    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3470
3471    The following sections describe each lock and the associated
3472    transitions.
3473
3474    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3475       the lock with the AFS file server.  This type of lock can be
3476       exercised by a client to read or write to the locked region (as
3477       the lock allows).
3478
3479       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3480         server lock that was required to assert the lock.  Before
3481         marking the lock as lost, the cache manager checks if the file
3482         has changed on the server.  If the file has not changed, then
3483         the cache manager will attempt to obtain a new server lock
3484         that is sufficient to assert the client side locks for the
3485         file.  If any of these fail, the lock is marked as LOST.
3486         Otherwise, it is left as ACTIVE.
3487
3488       1.2 ACTIVE->DELETED: Lock is released.
3489
3490    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3491       grants the lock but the lock is yet to be asserted with the AFS
3492       file server.  Once the file server grants the lock, the state
3493       will transition to an ACTIVE lock.
3494
3495       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3496
3497       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3498         waiting.
3499
3500       2.3 WAITLOCK->LOST: One or more locks from this client were
3501         marked as LOST.  No further locks will be granted to this
3502         client until all lost locks are removed.
3503
3504    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3505       receives a request for a lock that conflicts with an existing
3506       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3507       and will be granted at such time the conflicting locks are
3508       removed, at which point the state will transition to either
3509       WAITLOCK or ACTIVE.
3510
3511       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3512         current serverLock is sufficient to assert this lock, or a
3513         sufficient serverLock is obtained.
3514
3515       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3516         however the required serverLock is yet to be asserted with the
3517         server.
3518
3519       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3520         released.
3521
3522       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3523         marked as LOST.  No further locks will be granted to this
3524         client until all lost locks are removed.
3525
3526    4. LOST: A lock L is LOST if the server lock that was required to
3527       assert the lock could not be obtained or if it could not be
3528       extended, or if other locks by the same client were LOST.
3529       Essentially, once a lock is LOST, the contract between the cache
3530       manager and that specific client is no longer valid.
3531
3532       The cache manager rechecks the server lock once every minute and
3533       extends it as appropriate.  If this is not done for 5 minutes,
3534       the AFS file server will release the lock (the 5 minute timeout
3535       is based on current file server code and is fairly arbitrary).
3536       Once released, the lock cannot be re-obtained without verifying
3537       that the contents of the file hasn't been modified since the
3538       time the lock was released.  Re-obtaining the lock without
3539       verifying this may lead to data corruption.  If the lock can not
3540       be obtained safely, then all active locks for the cm_scache_t
3541       are marked as LOST.
3542
3543       4.1 LOST->DELETED: The lock is released.
3544
3545    5. DELETED: The lock is no longer relevant.  Eventually, it will
3546       get removed from the cm_scache_t. In the meantime, it will be
3547       treated as if it does not exist.
3548
3549       5.1 DELETED->not exist: The lock is removed from the
3550         cm_scache_t.
3551
3552    The following are classifications of locks based on their state.
3553
3554    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3555       have been accepted by the cache manager, but may or may not have
3556       been granted back to the client.
3557
3558    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3559
3560    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3561
3562    Lock operation:
3563
3564    A client C can READ range (Offset,+Length) of a file represented by
3565    cm_scache_t S iff (1):
3566
3567    1. for all _a_ in (Offset,+Length), all of the following is true:
3568
3569        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3570          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3571          shared.
3572
3573        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3574          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3575          Key(C)
3576
3577        (When locks are lost on an cm_scache_t, all locks are lost.  By
3578        4.2 (below), if there is an exclusive LOST lock, then there
3579        can't be any overlapping ACTIVE locks.)
3580
3581    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3582
3583    2. for all _a_ in (Offset,+Length), one of the following is true:
3584
3585        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
3586          does not exist a LOST lock L such that _a_ in
3587          (L->LOffset,+L->LLength).
3588
3589        2.2 Byte _a_ of S is owned by C under lock L (as specified in
3590          1.2) AND L->LockType is exclusive.
3591
3592    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
3593
3594    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
3595       true:
3596
3597        3.1 If L->LockType is exclusive then there does NOT exist a
3598          ACCEPTED lock M in S->fileLocks such that _a_ in
3599          (M->LOffset,+M->LLength).
3600
3601          (If we count all QUEUED locks then we hit cases such as
3602          cascading waiting locks where the locks later on in the queue
3603          can be granted without compromising file integrity.  On the
3604          other hand if only ACCEPTED locks are considered, then locks
3605          that were received earlier may end up waiting for locks that
3606          were received later to be unlocked. The choice of ACCEPTED
3607          locks was made to mimic the Windows byte range lock
3608          semantics.)
3609
3610        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3611          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3612          M->LockType is shared.
3613
3614    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3615
3616        4.1 M->key != Key(C)
3617
3618        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3619          and (M->LOffset,+M->LLength) do not intersect.
3620
3621          (Note: If a client loses a lock, it loses all locks.
3622          Subsequently, it will not be allowed to obtain any more locks
3623          until all existing LOST locks that belong to the client are
3624          released.  Once all locks are released by a single client,
3625          there exists no further contract between the client and AFS
3626          about the contents of the file, hence the client can then
3627          proceed to obtain new locks and establish a new contract.
3628
3629          This doesn't quite work as you think it should, because most
3630          applications aren't built to deal with losing locks they
3631          thought they once had.  For now, we don't have a good
3632          solution to lost locks.
3633
3634          Also, for consistency reasons, we have to hold off on
3635          granting locks that overlap exclusive LOST locks.)
3636
3637    A client C can only unlock locks L in S->fileLocks which have
3638    L->key == Key(C).
3639
3640    The representation and invariants are as follows:
3641
3642    - Each cm_scache_t structure keeps:
3643
3644        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3645          are of type cm_file_lock_t.
3646
3647        - A record of the highest server-side lock that has been
3648          obtained for this object (cm_scache_t::serverLock), which is
3649          one of (-1), LockRead, LockWrite.
3650
3651        - A count of ACCEPTED exclusive and shared locks that are in the
3652          queue (cm_scache_t::sharedLocks and
3653          cm_scache_t::exclusiveLocks)
3654
3655    - Each cm_file_lock_t structure keeps:
3656
3657        - The type of lock (cm_file_lock_t::LockType)
3658
3659        - The key associated with the lock (cm_file_lock_t::key)
3660
3661        - The offset and length of the lock (cm_file_lock_t::LOffset
3662          and cm_file_lock_t::LLength)
3663
3664        - The state of the lock.
3665
3666        - Time of issuance or last successful extension
3667
3668    Semantic invariants:
3669
3670        I1. The number of ACCEPTED locks in S->fileLocks are
3671            (S->sharedLocks + S->exclusiveLocks)
3672
3673    External invariants:
3674
3675        I3. S->serverLock is the lock that we have asserted with the
3676            AFS file server for this cm_scache_t.
3677
3678        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3679            shared lock, but no ACTIVE exclusive locks.
3680
3681        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3682            exclusive lock.
3683
3684        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3685            M->key == L->key IMPLIES M is LOST or DELETED.
3686
3687    --asanka
3688  */
3689
3690 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3691
3692 #define IS_LOCK_WAITLOCK(lockp)   (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
3693
3694 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
3695
3696 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3697
3698 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3699
3700 /* unsafe */
3701 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3702
3703 /* unsafe */
3704 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3705
3706 /* unsafe */
3707 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3708
3709 /* unsafe */
3710 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3711
3712 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3713 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3714 #else
3715 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3716 #endif
3717
3718 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3719
3720 #if defined(VICED_CAPABILITY_WRITELOCKACL)
3721 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
3722 #else
3723 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
3724
3725 /* This should really be defined in any build that this code is being
3726    compiled. */
3727 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
3728 #endif
3729
3730 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3731 {
3732     afs_int64 int_begin;
3733     afs_int64 int_end;
3734
3735     int_begin = MAX(pos->offset, neg->offset);
3736     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3737
3738     if (int_begin < int_end) {
3739         if (int_begin == pos->offset) {
3740             pos->length = pos->offset + pos->length - int_end;
3741             pos->offset = int_end;
3742         } else if (int_end == pos->offset + pos->length) {
3743             pos->length = int_begin - pos->offset;
3744         }
3745
3746         /* We only subtract ranges if the resulting range is
3747            contiguous.  If we try to support non-contigous ranges, we
3748            aren't actually improving performance. */
3749     }
3750 }
3751
3752 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3753    specified range by the client identified by key.
3754  */
3755 long cm_LockCheckRead(cm_scache_t *scp, 
3756                       LARGE_INTEGER LOffset, 
3757                       LARGE_INTEGER LLength, 
3758                       cm_key_t key)
3759 {
3760 #ifndef ADVISORY_LOCKS
3761
3762     cm_file_lock_t *fileLock;
3763     osi_queue_t *q;
3764     long code = 0;
3765     cm_range_t range;
3766     int substract_ranges = FALSE;
3767
3768     range.offset = LOffset.QuadPart;
3769     range.length = LLength.QuadPart;
3770
3771     /*
3772
3773      1. for all _a_ in (Offset,+Length), all of the following is true:
3774
3775        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3776          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3777          shared.
3778
3779        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3780          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3781          Key(C)
3782
3783     */
3784
3785     lock_ObtainRead(&cm_scacheLock);
3786
3787     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3788         fileLock = 
3789             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3790
3791         if (INTERSECT_RANGE(range, fileLock->range)) {
3792             if (IS_LOCK_ACTIVE(fileLock)) {
3793                 if (fileLock->key == key) {
3794
3795                     /* If there is an active lock for this client, it
3796                        is safe to substract ranges.*/
3797                     cm_LockRangeSubtract(&range, &fileLock->range);
3798                     substract_ranges = TRUE;
3799                 } else {
3800                     if (fileLock->lockType != LockRead) {
3801                         code = CM_ERROR_LOCK_CONFLICT;
3802                         break;
3803                     }
3804
3805                     /* even if the entire range is locked for reading,
3806                        we still can't grant the lock at this point
3807                        because the client may have lost locks. That
3808                        is, unless we have already seen an active lock
3809                        belonging to the client, in which case there
3810                        can't be any lost locks for this client. */
3811                     if (substract_ranges)
3812                         cm_LockRangeSubtract(&range, &fileLock->range);
3813                 }
3814             } else if (IS_LOCK_LOST(fileLock) &&
3815                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3816                 code = CM_ERROR_BADFD;
3817                 break;
3818             }
3819         }
3820     }
3821
3822     lock_ReleaseRead(&cm_scacheLock);
3823
3824     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3825               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3826
3827     return code;
3828
3829 #else
3830
3831     return 0;
3832
3833 #endif
3834 }
3835
3836 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3837    specified range by the client identified by key.
3838  */
3839 long cm_LockCheckWrite(cm_scache_t *scp,
3840                        LARGE_INTEGER LOffset,
3841                        LARGE_INTEGER LLength,
3842                        cm_key_t key)
3843 {
3844 #ifndef ADVISORY_LOCKS
3845
3846     cm_file_lock_t *fileLock;
3847     osi_queue_t *q;
3848     long code = 0;
3849     cm_range_t range;
3850
3851     range.offset = LOffset.QuadPart;
3852     range.length = LLength.QuadPart;
3853
3854     /*
3855    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3856
3857    2. for all _a_ in (Offset,+Length), one of the following is true:
3858
3859        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3860          lock L such that _a_ in (L->LOffset,+L->LLength).
3861
3862        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3863          exclusive.
3864     */
3865
3866     lock_ObtainRead(&cm_scacheLock);
3867
3868     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3869         fileLock = 
3870             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3871
3872         if (INTERSECT_RANGE(range, fileLock->range)) {
3873             if (IS_LOCK_ACTIVE(fileLock)) {
3874                 if (fileLock->key == key) {
3875                     if (fileLock->lockType == LockWrite) {
3876
3877                         /* if there is an active lock for this client, it
3878                            is safe to substract ranges */
3879                         cm_LockRangeSubtract(&range, &fileLock->range);
3880                     } else {
3881                         code = CM_ERROR_LOCK_CONFLICT;
3882                         break;
3883                     }
3884                 } else {
3885                     code = CM_ERROR_LOCK_CONFLICT;
3886                     break;
3887                 }
3888             } else if (IS_LOCK_LOST(fileLock)) {
3889                 code = CM_ERROR_BADFD;
3890                 break;
3891             }
3892         }
3893     }
3894
3895     lock_ReleaseRead(&cm_scacheLock);
3896
3897     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3898               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3899
3900     return code;
3901
3902 #else
3903
3904     return 0;
3905
3906 #endif
3907 }
3908
3909 /* Forward dcl. */
3910 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3911
3912 /* Called with cm_scacheLock write locked */
3913 static cm_file_lock_t * cm_GetFileLock(void) {
3914     cm_file_lock_t * l;
3915
3916     l = (cm_file_lock_t *) cm_freeFileLocks;
3917     if (l) {
3918         osi_QRemove(&cm_freeFileLocks, &l->q);
3919     } else {
3920         l = malloc(sizeof(cm_file_lock_t));
3921         osi_assertx(l, "null cm_file_lock_t");
3922     }
3923
3924     memset(l, 0, sizeof(cm_file_lock_t));
3925
3926     return l;
3927 }
3928
3929 /* Called with cm_scacheLock write locked */
3930 static void cm_PutFileLock(cm_file_lock_t *l) {
3931     osi_QAdd(&cm_freeFileLocks, &l->q);
3932 }
3933
3934 /* called with scp->mx held.  May release it during processing, but
3935    leaves it held on exit. */
3936 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3937                    cm_req_t * reqp) {
3938     long code = 0;
3939     AFSFid tfid;
3940     cm_fid_t cfid;
3941     cm_conn_t * connp;
3942     struct rx_connection * callp;
3943     AFSVolSync volSync;
3944
3945     tfid.Volume = scp->fid.volume;
3946     tfid.Vnode = scp->fid.vnode;
3947     tfid.Unique = scp->fid.unique;
3948     cfid = scp->fid;
3949
3950     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3951
3952     lock_ReleaseMutex(&scp->mx);
3953
3954     do {
3955         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
3956         if (code) 
3957             break;
3958
3959         callp = cm_GetRxConn(connp);
3960         code = RXAFS_SetLock(callp, &tfid, lockType,
3961                              &volSync);
3962         rx_PutConnection(callp);
3963
3964     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3965                         NULL, NULL, code));
3966
3967     code = cm_MapRPCError(code, reqp);
3968     if (code) {
3969         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3970     } else {
3971         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3972     }
3973
3974     lock_ObtainMutex(&scp->mx);
3975
3976     return code;
3977 }
3978
3979 /* called with scp->mx held.  Releases it during processing */
3980 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3981                        cm_req_t * reqp) {
3982     long code = 0;
3983     AFSFid tfid;
3984     cm_fid_t cfid;
3985     cm_conn_t * connp;
3986     struct rx_connection * callp;
3987     AFSVolSync volSync;
3988
3989     tfid.Volume = scp->fid.volume;
3990     tfid.Vnode = scp->fid.vnode;
3991     tfid.Unique = scp->fid.unique;
3992     cfid = scp->fid;
3993
3994     lock_ReleaseMutex(&scp->mx);
3995
3996     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3997
3998     do {
3999         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4000         if (code) 
4001             break;
4002
4003         callp = cm_GetRxConn(connp);
4004         code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
4005         rx_PutConnection(callp);
4006
4007     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4008                         NULL, NULL, code));
4009     code = cm_MapRPCError(code, reqp);
4010     if (code)
4011         osi_Log1(afsd_logp,
4012                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4013     else
4014         osi_Log0(afsd_logp,
4015                  "CALL ReleaseLock SUCCESS");
4016         
4017     lock_ObtainMutex(&scp->mx);
4018
4019     return code;
4020 }
4021
4022 /* called with scp->mx held.  May release it during processing, but
4023    will exit with lock held.
4024
4025    This will return:
4026
4027    - 0 if the user has permission to get the specified lock for the scp
4028
4029    - CM_ERROR_NOACCESS if not
4030
4031    Any other error from cm_SyncOp will be sent down untranslated.
4032
4033    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4034    phas_insert (if non-NULL) will receive a boolean value indicating
4035    whether the user has INSERT permission or not.
4036 */
4037 long cm_LockCheckPerms(cm_scache_t * scp,
4038                        int lock_type,
4039                        cm_user_t * userp,
4040                        cm_req_t * reqp,
4041                        int * phas_insert)
4042 {
4043     long rights = 0;
4044     long code = 0, code2 = 0;
4045
4046     /* lock permissions are slightly tricky because of the 'i' bit.
4047        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4048        user has PRSFS_WRITE, she can write-lock the file.  However, if
4049        the user has PRSFS_INSERT, then she can write-lock new files,
4050        but not old ones.  Since we don't have information about
4051        whether a file is new or not, we assume that if the user owns
4052        the scp, then she has the permissions that are granted by
4053        PRSFS_INSERT. */
4054
4055     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4056              scp, lock_type, userp);
4057
4058     if (lock_type == LockRead)
4059         rights |= PRSFS_LOCK;
4060     else if (lock_type == LockWrite)