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