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