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