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