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