DEVEL15-windows-largefile-support-20060623
[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                 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              128
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 (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 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1882                      cm_req_t *reqp)
1883 {
1884     long code;
1885     cm_bulkStat_t bb;   /* this is *BIG*, probably 12K or so;
1886                          * watch for stack problems */
1887     AFSCBFids fidStruct;
1888     AFSBulkStats statStruct;
1889     cm_conn_t *connp;
1890     AFSCBs callbackStruct;
1891     long filex;
1892     AFSVolSync volSync;
1893     cm_callbackRequest_t cbReq;
1894     long filesThisCall;
1895     long i;
1896     long j;
1897     cm_scache_t *scp;
1898     cm_fid_t tfid;
1899     struct rx_connection * callp;
1900
1901     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
1902
1903     /* should be on a buffer boundary */
1904     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1905
1906     bb.counter = 0;
1907     bb.bufOffset = *offsetp;
1908
1909     lock_ReleaseMutex(&dscp->mx);
1910     /* first, assemble the file IDs we need to stat */
1911     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1912
1913     /* if we failed, bail out early */
1914     if (code && code != CM_ERROR_STOPNOW) {
1915         lock_ObtainMutex(&dscp->mx);
1916         return;
1917     }
1918
1919     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1920      * make the calls to create the entries.  Handle AFSCBMAX files at a
1921      * time.
1922      */
1923     filex = 0;
1924     while (filex < bb.counter) {
1925         filesThisCall = bb.counter - filex;
1926         if (filesThisCall > AFSCBMAX) 
1927             filesThisCall = AFSCBMAX;
1928
1929         fidStruct.AFSCBFids_len = filesThisCall;
1930         fidStruct.AFSCBFids_val = &bb.fids[filex];
1931         statStruct.AFSBulkStats_len = filesThisCall;
1932         statStruct.AFSBulkStats_val = &bb.stats[filex];
1933         callbackStruct.AFSCBs_len = filesThisCall;
1934         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1935         cm_StartCallbackGrantingCall(NULL, &cbReq);
1936         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1937         do {
1938             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1939             if (code) 
1940                 continue;
1941
1942             callp = cm_GetRxConn(connp);
1943             code = RXAFS_BulkStatus(callp, &fidStruct,
1944                                      &statStruct, &callbackStruct, &volSync);
1945             rx_PutConnection(callp);
1946
1947         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1948                              &volSync, NULL, &cbReq, code));
1949         code = cm_MapRPCError(code, reqp);
1950
1951         if (code)
1952             osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1953         else
1954             osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1955
1956         /* may as well quit on an error, since we're not going to do
1957          * much better on the next immediate call, either.
1958          */
1959         if (code) {
1960             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1961             break;
1962         }
1963
1964         /* otherwise, we should do the merges */
1965         for (i = 0; i<filesThisCall; i++) {
1966             j = filex + i;
1967             tfid.cell = dscp->fid.cell;
1968             tfid.volume = bb.fids[j].Volume;
1969             tfid.vnode = bb.fids[j].Vnode;
1970             tfid.unique = bb.fids[j].Unique;
1971             code = cm_GetSCache(&tfid, &scp, userp, reqp);
1972             if (code != 0) 
1973                 continue;
1974
1975             /* otherwise, if this entry has no callback info, 
1976              * merge in this.
1977              */
1978             lock_ObtainMutex(&scp->mx);
1979             /* now, we have to be extra paranoid on merging in this
1980              * information, since we didn't use cm_SyncOp before
1981              * starting the fetch to make sure that no bad races
1982              * were occurring.  Specifically, we need to make sure
1983              * we don't obliterate any newer information in the
1984              * vnode than have here.
1985              *
1986              * Right now, be pretty conservative: if there's a
1987              * callback or a pending call, skip it.
1988              */
1989             if (scp->cbServerp == NULL
1990                  && !(scp->flags &
1991                        (CM_SCACHEFLAG_FETCHING
1992                          | CM_SCACHEFLAG_STORING
1993                          | CM_SCACHEFLAG_SIZESTORING))) {
1994                 cm_EndCallbackGrantingCall(scp, &cbReq,
1995                                             &bb.callbacks[j],
1996                                             CM_CALLBACK_MAINTAINCOUNT);
1997                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1998                                 userp, 0);
1999             }       
2000             lock_ReleaseMutex(&scp->mx);
2001             cm_ReleaseSCache(scp);
2002         } /* all files in the response */
2003         /* now tell it to drop the count,
2004          * after doing the vnode processing above */
2005         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2006
2007         filex += filesThisCall;
2008     }   /* while there are still more files to process */
2009     lock_ObtainMutex(&dscp->mx);
2010     osi_Log0(afsd_logp, "END cm_TryBulkStat");
2011 }       
2012
2013 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2014 {
2015     long mask;
2016
2017     /* initialize store back mask as inexpensive local variable */
2018     mask = 0;
2019     memset(statusp, 0, sizeof(AFSStoreStatus));
2020
2021     /* copy out queued info from scache first, if scp passed in */
2022     if (scp) {
2023         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2024             statusp->ClientModTime = scp->clientModTime;
2025             mask |= AFS_SETMODTIME;
2026             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2027         }
2028     }
2029
2030     if (attrp) {
2031         /* now add in our locally generated request */
2032         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2033             statusp->ClientModTime = attrp->clientModTime;
2034             mask |= AFS_SETMODTIME;
2035         }
2036         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2037             statusp->UnixModeBits = attrp->unixModeBits;
2038             mask |= AFS_SETMODE;
2039         }
2040         if (attrp->mask & CM_ATTRMASK_OWNER) {
2041             statusp->Owner = attrp->owner;
2042             mask |= AFS_SETOWNER;
2043         }
2044         if (attrp->mask & CM_ATTRMASK_GROUP) {
2045             statusp->Group = attrp->group;
2046             mask |= AFS_SETGROUP;
2047         }
2048     }
2049     statusp->Mask = mask;
2050 }       
2051
2052 /* set the file size, and make sure that all relevant buffers have been
2053  * truncated.  Ensure that any partially truncated buffers have been zeroed
2054  * to the end of the buffer.
2055  */
2056 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2057                    cm_req_t *reqp)
2058 {
2059     long code;
2060     int shrinking;
2061
2062     /* start by locking out buffer creation */
2063     lock_ObtainWrite(&scp->bufCreateLock);
2064
2065     /* verify that this is a file, not a dir or a symlink */
2066     lock_ObtainMutex(&scp->mx);
2067     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2068                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2069     if (code) 
2070         goto done;
2071         
2072     if (scp->fileType != CM_SCACHETYPE_FILE) {
2073         code = CM_ERROR_ISDIR;
2074         goto done;
2075     }
2076
2077   startover:
2078     if (LargeIntegerLessThan(*sizep, scp->length))
2079         shrinking = 1;
2080     else
2081         shrinking = 0;
2082
2083     lock_ReleaseMutex(&scp->mx);
2084
2085     /* can't hold scp->mx lock here, since we may wait for a storeback to
2086      * finish if the buffer package is cleaning a buffer by storing it to
2087      * the server.
2088      */
2089     if (shrinking)
2090         buf_Truncate(scp, userp, reqp, sizep);
2091
2092     /* now ensure that file length is short enough, and update truncPos */
2093     lock_ObtainMutex(&scp->mx);
2094
2095     /* make sure we have a callback (so we have the right value for the
2096      * length), and wait for it to be safe to do a truncate.
2097      */
2098     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2099                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2100                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2101     if (code) 
2102         goto done;
2103
2104     if (LargeIntegerLessThan(*sizep, scp->length)) {
2105         /* a real truncation.  If truncPos is not set yet, or is bigger
2106          * than where we're truncating the file, set truncPos to this
2107          * new value.
2108          */
2109         if (!shrinking)
2110             goto startover;
2111         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2112              || LargeIntegerLessThan(*sizep, scp->length)) {
2113             /* set trunc pos */
2114             scp->truncPos = *sizep;
2115             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2116         }
2117         /* in either case, the new file size has been changed */
2118         scp->length = *sizep;
2119         scp->mask |= CM_SCACHEMASK_LENGTH;
2120     }
2121     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2122         /* really extending the file */
2123         scp->length = *sizep;
2124         scp->mask |= CM_SCACHEMASK_LENGTH;
2125     }
2126
2127     /* done successfully */
2128     code = 0;
2129
2130   done:
2131     lock_ReleaseMutex(&scp->mx);
2132     lock_ReleaseWrite(&scp->bufCreateLock);
2133
2134     return code;
2135 }
2136
2137 /* set the file size or other attributes (but not both at once) */
2138 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2139                 cm_req_t *reqp)
2140 {
2141     long code;
2142     int flags;
2143     AFSFetchStatus afsOutStatus;
2144     AFSVolSync volSync;
2145     cm_conn_t *connp;
2146     AFSFid tfid;
2147     AFSStoreStatus afsInStatus;
2148     struct rx_connection * callp;
2149
2150     /* handle file length setting */
2151     if (attrp->mask & CM_ATTRMASK_LENGTH)
2152         return cm_SetLength(scp, &attrp->length, userp, reqp);
2153
2154     flags = CM_SCACHESYNC_STORESTATUS;
2155
2156     lock_ObtainMutex(&scp->mx);
2157     /* otherwise, we have to make an RPC to get the status */
2158     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2159
2160     /* make the attr structure */
2161     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2162
2163     tfid.Volume = scp->fid.volume;
2164     tfid.Vnode = scp->fid.vnode;
2165     tfid.Unique = scp->fid.unique;
2166
2167     lock_ReleaseMutex(&scp->mx);
2168     if (code) 
2169         return code;
2170
2171     /* now make the RPC */
2172     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2173     do {
2174         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2175         if (code) 
2176             continue;
2177
2178         callp = cm_GetRxConn(connp);
2179         code = RXAFS_StoreStatus(callp, &tfid,
2180                                   &afsInStatus, &afsOutStatus, &volSync);
2181         rx_PutConnection(callp);
2182
2183     } while (cm_Analyze(connp, userp, reqp,
2184                          &scp->fid, &volSync, NULL, NULL, code));
2185     code = cm_MapRPCError(code, reqp);
2186
2187     if (code)
2188         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2189     else
2190         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2191
2192     lock_ObtainMutex(&scp->mx);
2193     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2194     if (code == 0)
2195         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2196                         CM_MERGEFLAG_FORCE);
2197         
2198     /* if we're changing the mode bits, discard the ACL cache, 
2199      * since we changed the mode bits.
2200      */
2201     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2202     lock_ReleaseMutex(&scp->mx);
2203     return code;
2204 }       
2205
2206 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2207                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2208 {       
2209     cm_conn_t *connp;
2210     long code;
2211     AFSFid dirAFSFid;
2212     cm_callbackRequest_t cbReq;
2213     AFSFid newAFSFid;
2214     cm_fid_t newFid;
2215     cm_scache_t *scp;
2216     int didEnd;
2217     AFSStoreStatus inStatus;
2218     AFSFetchStatus updatedDirStatus;
2219     AFSFetchStatus newFileStatus;
2220     AFSCallBack newFileCallback;
2221     AFSVolSync volSync;
2222     struct rx_connection * callp;
2223
2224     /* can't create names with @sys in them; must expand it manually first.
2225      * return "invalid request" if they try.
2226      */
2227     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2228         return CM_ERROR_ATSYS;
2229     }
2230
2231     /* before starting the RPC, mark that we're changing the file data, so
2232      * that someone who does a chmod will know to wait until our call
2233      * completes.
2234      */
2235     lock_ObtainMutex(&dscp->mx);
2236     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2237     if (code == 0) {
2238         cm_StartCallbackGrantingCall(NULL, &cbReq);
2239     }
2240     lock_ReleaseMutex(&dscp->mx);
2241     if (code) {
2242         return code;
2243     }
2244     didEnd = 0;
2245
2246     cm_StatusFromAttr(&inStatus, NULL, attrp);
2247
2248     /* try the RPC now */
2249     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2250     do {
2251         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2252         if (code) 
2253             continue;
2254
2255         dirAFSFid.Volume = dscp->fid.volume;
2256         dirAFSFid.Vnode = dscp->fid.vnode;
2257         dirAFSFid.Unique = dscp->fid.unique;
2258
2259         callp = cm_GetRxConn(connp);
2260         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2261                                  &inStatus, &newAFSFid, &newFileStatus,
2262                                  &updatedDirStatus, &newFileCallback,
2263                                  &volSync);
2264         rx_PutConnection(callp);
2265
2266     } while (cm_Analyze(connp, userp, reqp,
2267                          &dscp->fid, &volSync, NULL, &cbReq, code));
2268     code = cm_MapRPCError(code, reqp);
2269         
2270     if (code)
2271         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2272     else
2273         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2274
2275     lock_ObtainMutex(&dscp->mx);
2276     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2277     if (code == 0) {
2278         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2279     }
2280     lock_ReleaseMutex(&dscp->mx);
2281
2282     /* now try to create the file's entry, too, but be careful to 
2283      * make sure that we don't merge in old info.  Since we weren't locking
2284      * out any requests during the file's creation, we may have pretty old
2285      * info.
2286      */
2287     if (code == 0) {
2288         newFid.cell = dscp->fid.cell;
2289         newFid.volume = dscp->fid.volume;
2290         newFid.vnode = newAFSFid.Vnode;
2291         newFid.unique = newAFSFid.Unique;
2292         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2293         if (code == 0) {
2294             lock_ObtainMutex(&scp->mx);
2295             scp->creator = userp;               /* remember who created it */
2296             if (!cm_HaveCallback(scp)) {
2297                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2298                                 userp, 0);
2299                 cm_EndCallbackGrantingCall(scp, &cbReq,
2300                                             &newFileCallback, 0);
2301                 didEnd = 1;     
2302             }       
2303             lock_ReleaseMutex(&scp->mx);
2304             *scpp = scp;
2305         }
2306     }
2307
2308     /* make sure we end things properly */
2309     if (!didEnd)
2310         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2311
2312     return code;
2313 }       
2314
2315 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2316 {
2317     long code;
2318
2319     lock_ObtainWrite(&scp->bufCreateLock);
2320     code = buf_CleanVnode(scp, userp, reqp);
2321     lock_ReleaseWrite(&scp->bufCreateLock);
2322     if (code == 0) {
2323         lock_ObtainMutex(&scp->mx);
2324         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2325                          | CM_SCACHEFLAG_OUTOFSPACE);
2326         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2327                           | CM_SCACHEMASK_CLIENTMODTIME
2328                           | CM_SCACHEMASK_LENGTH))
2329             code = cm_StoreMini(scp, userp, reqp);
2330         lock_ReleaseMutex(&scp->mx);
2331     }
2332     return code;
2333 }
2334
2335 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2336                  cm_user_t *userp, cm_req_t *reqp)
2337 {
2338     cm_conn_t *connp;
2339     long code;
2340     AFSFid dirAFSFid;
2341     cm_callbackRequest_t cbReq;
2342     AFSFid newAFSFid;
2343     cm_fid_t newFid;
2344     cm_scache_t *scp;
2345     int didEnd;
2346     AFSStoreStatus inStatus;
2347     AFSFetchStatus updatedDirStatus;
2348     AFSFetchStatus newDirStatus;
2349     AFSCallBack newDirCallback;
2350     AFSVolSync volSync;
2351     struct rx_connection * callp;
2352
2353     /* can't create names with @sys in them; must expand it manually first.
2354      * return "invalid request" if they try.
2355      */
2356     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2357         return CM_ERROR_ATSYS;
2358     }
2359
2360     /* before starting the RPC, mark that we're changing the directory
2361      * data, so that someone who does a chmod on the dir will wait until
2362      * our call completes.
2363      */
2364     lock_ObtainMutex(&dscp->mx);
2365     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2366     if (code == 0) {
2367         cm_StartCallbackGrantingCall(NULL, &cbReq);
2368     }
2369     lock_ReleaseMutex(&dscp->mx);
2370     if (code) {
2371         return code;
2372     }
2373     didEnd = 0;
2374
2375     cm_StatusFromAttr(&inStatus, NULL, attrp);
2376
2377     /* try the RPC now */
2378     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
2379     do {
2380         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2381         if (code) 
2382             continue;
2383
2384         dirAFSFid.Volume = dscp->fid.volume;
2385         dirAFSFid.Vnode = dscp->fid.vnode;
2386         dirAFSFid.Unique = dscp->fid.unique;
2387
2388         callp = cm_GetRxConn(connp);
2389         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2390                               &inStatus, &newAFSFid, &newDirStatus,
2391                               &updatedDirStatus, &newDirCallback,
2392                               &volSync);
2393         rx_PutConnection(callp);
2394
2395     } while (cm_Analyze(connp, userp, reqp,
2396                          &dscp->fid, &volSync, NULL, &cbReq, code));
2397     code = cm_MapRPCError(code, reqp);
2398         
2399     if (code)
2400         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2401     else
2402         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2403
2404     lock_ObtainMutex(&dscp->mx);
2405     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2406     if (code == 0) {
2407         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2408     }
2409     lock_ReleaseMutex(&dscp->mx);
2410
2411     /* now try to create the new dir's entry, too, but be careful to 
2412      * make sure that we don't merge in old info.  Since we weren't locking
2413      * out any requests during the file's creation, we may have pretty old
2414      * info.
2415      */
2416     if (code == 0) {
2417         newFid.cell = dscp->fid.cell;
2418         newFid.volume = dscp->fid.volume;
2419         newFid.vnode = newAFSFid.Vnode;
2420         newFid.unique = newAFSFid.Unique;
2421         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2422         if (code == 0) {
2423             lock_ObtainMutex(&scp->mx);
2424             if (!cm_HaveCallback(scp)) {
2425                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2426                                 userp, 0);
2427                 cm_EndCallbackGrantingCall(scp, &cbReq,
2428                                             &newDirCallback, 0);
2429                 didEnd = 1;             
2430             }
2431             lock_ReleaseMutex(&scp->mx);
2432             cm_ReleaseSCache(scp);
2433         }
2434     }
2435
2436     /* make sure we end things properly */
2437     if (!didEnd)
2438         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2439
2440     /* and return error code */
2441     return code;
2442 }       
2443
2444 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2445              cm_user_t *userp, cm_req_t *reqp)
2446 {
2447     cm_conn_t *connp;
2448     long code = 0;
2449     AFSFid dirAFSFid;
2450     AFSFid existingAFSFid;
2451     AFSFetchStatus updatedDirStatus;
2452     AFSFetchStatus newLinkStatus;
2453     AFSVolSync volSync;
2454     struct rx_connection * callp;
2455
2456     if (dscp->fid.cell != sscp->fid.cell ||
2457         dscp->fid.volume != sscp->fid.volume) {
2458         return CM_ERROR_CROSSDEVLINK;
2459     }
2460
2461     lock_ObtainMutex(&dscp->mx);
2462     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2463     lock_ReleaseMutex(&dscp->mx);
2464
2465     if (code)
2466         return code;
2467
2468     /* try the RPC now */
2469     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
2470     do {
2471         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2472         if (code) continue;
2473
2474         dirAFSFid.Volume = dscp->fid.volume;
2475         dirAFSFid.Vnode = dscp->fid.vnode;
2476         dirAFSFid.Unique = dscp->fid.unique;
2477
2478         existingAFSFid.Volume = sscp->fid.volume;
2479         existingAFSFid.Vnode = sscp->fid.vnode;
2480         existingAFSFid.Unique = sscp->fid.unique;
2481
2482         callp = cm_GetRxConn(connp);
2483         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2484             &newLinkStatus, &updatedDirStatus, &volSync);
2485         rx_PutConnection(callp);
2486         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2487
2488     } while (cm_Analyze(connp, userp, reqp,
2489         &dscp->fid, &volSync, NULL, NULL, code));
2490
2491     code = cm_MapRPCError(code, reqp);
2492
2493     if (code)
2494         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2495     else
2496         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2497
2498     lock_ObtainMutex(&dscp->mx);
2499     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2500     if (code == 0) {
2501         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2502     }
2503     lock_ReleaseMutex(&dscp->mx);
2504
2505     return code;
2506 }
2507
2508 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2509                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2510 {
2511     cm_conn_t *connp;
2512     long code;
2513     AFSFid dirAFSFid;
2514     AFSFid newAFSFid;
2515     cm_fid_t newFid;
2516     cm_scache_t *scp;
2517     AFSStoreStatus inStatus;
2518     AFSFetchStatus updatedDirStatus;
2519     AFSFetchStatus newLinkStatus;
2520     AFSVolSync volSync;
2521     struct rx_connection * callp;
2522
2523     /* before starting the RPC, mark that we're changing the directory data,
2524      * so that someone who does a chmod on the dir will wait until our
2525      * call completes.
2526      */
2527     lock_ObtainMutex(&dscp->mx);
2528     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2529     lock_ReleaseMutex(&dscp->mx);
2530     if (code) {
2531         return code;
2532     }
2533
2534     cm_StatusFromAttr(&inStatus, NULL, attrp);
2535
2536     /* try the RPC now */
2537     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
2538     do {
2539         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2540         if (code) 
2541             continue;
2542
2543         dirAFSFid.Volume = dscp->fid.volume;
2544         dirAFSFid.Vnode = dscp->fid.vnode;
2545         dirAFSFid.Unique = dscp->fid.unique;
2546
2547         callp = cm_GetRxConn(connp);
2548         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2549                               &inStatus, &newAFSFid, &newLinkStatus,
2550                               &updatedDirStatus, &volSync);
2551         rx_PutConnection(callp);
2552
2553     } while (cm_Analyze(connp, userp, reqp,
2554                          &dscp->fid, &volSync, NULL, NULL, code));
2555     code = cm_MapRPCError(code, reqp);
2556         
2557     if (code)
2558         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2559     else
2560         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2561
2562     lock_ObtainMutex(&dscp->mx);
2563     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2564     if (code == 0) {
2565         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2566     }
2567     lock_ReleaseMutex(&dscp->mx);
2568
2569     /* now try to create the new dir's entry, too, but be careful to 
2570      * make sure that we don't merge in old info.  Since we weren't locking
2571      * out any requests during the file's creation, we may have pretty old
2572      * info.
2573      */
2574     if (code == 0) {
2575         newFid.cell = dscp->fid.cell;
2576         newFid.volume = dscp->fid.volume;
2577         newFid.vnode = newAFSFid.Vnode;
2578         newFid.unique = newAFSFid.Unique;
2579         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2580         if (code == 0) {
2581             lock_ObtainMutex(&scp->mx);
2582             if (!cm_HaveCallback(scp)) {
2583                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2584                                 userp, 0);
2585             }       
2586             lock_ReleaseMutex(&scp->mx);
2587             cm_ReleaseSCache(scp);
2588         }
2589     }
2590         
2591     /* and return error code */
2592     return code;
2593 }
2594
2595 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2596                    cm_req_t *reqp)
2597 {
2598     cm_conn_t *connp;
2599     long code;
2600     AFSFid dirAFSFid;
2601     int didEnd;
2602     AFSFetchStatus updatedDirStatus;
2603     AFSVolSync volSync;
2604     struct rx_connection * callp;
2605
2606     /* before starting the RPC, mark that we're changing the directory data,
2607      * so that someone who does a chmod on the dir will wait until our
2608      * call completes.
2609      */
2610     lock_ObtainMutex(&dscp->mx);
2611     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2612     lock_ReleaseMutex(&dscp->mx);
2613     if (code) {
2614         return code;
2615     }
2616     didEnd = 0;
2617
2618     /* try the RPC now */
2619     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
2620     do {
2621         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2622         if (code) 
2623             continue;
2624
2625         dirAFSFid.Volume = dscp->fid.volume;
2626         dirAFSFid.Vnode = dscp->fid.vnode;
2627         dirAFSFid.Unique = dscp->fid.unique;
2628
2629         callp = cm_GetRxConn(connp);
2630         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2631                                 &updatedDirStatus, &volSync);
2632         rx_PutConnection(callp);
2633
2634     } while (cm_Analyze(connp, userp, reqp,
2635                          &dscp->fid, &volSync, NULL, NULL, code));
2636     code = cm_MapRPCErrorRmdir(code, reqp);
2637
2638     if (code)
2639         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2640     else
2641         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2642
2643     lock_ObtainMutex(&dscp->mx);
2644     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2645     if (code == 0) {
2646         cm_dnlcRemove(dscp, namep); 
2647         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2648     }
2649     lock_ReleaseMutex(&dscp->mx);
2650
2651     /* and return error code */
2652     return code;
2653 }
2654
2655 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2656 {
2657     /* grab mutex on contents */
2658     lock_ObtainMutex(&scp->mx);
2659
2660     /* reset the prefetch info */
2661     scp->prefetch.base.LowPart = 0;             /* base */
2662     scp->prefetch.base.HighPart = 0;
2663     scp->prefetch.end.LowPart = 0;              /* and end */
2664     scp->prefetch.end.HighPart = 0;
2665
2666     /* release mutex on contents */
2667     lock_ReleaseMutex(&scp->mx);
2668
2669     /* we're done */
2670     return 0;
2671 }       
2672
2673 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2674                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2675 {
2676     cm_conn_t *connp;
2677     long code;
2678     AFSFid oldDirAFSFid;
2679     AFSFid newDirAFSFid;
2680     int didEnd;
2681     AFSFetchStatus updatedOldDirStatus;
2682     AFSFetchStatus updatedNewDirStatus;
2683     AFSVolSync volSync;
2684     int oneDir;
2685     struct rx_connection * callp;
2686
2687     /* before starting the RPC, mark that we're changing the directory data,
2688      * so that someone who does a chmod on the dir will wait until our call
2689      * completes.  We do this in vnode order so that we don't deadlock,
2690      * which makes the code a little verbose.
2691      */
2692     if (oldDscp == newDscp) {
2693         /* check for identical names */
2694         if (strcmp(oldNamep, newNamep) == 0)
2695             return CM_ERROR_RENAME_IDENTICAL;
2696
2697         oneDir = 1;
2698         lock_ObtainMutex(&oldDscp->mx);
2699         cm_dnlcRemove(oldDscp, oldNamep);
2700         cm_dnlcRemove(oldDscp, newNamep);
2701         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2702                           CM_SCACHESYNC_STOREDATA);
2703         lock_ReleaseMutex(&oldDscp->mx);
2704     }
2705     else {
2706         /* two distinct dir vnodes */
2707         oneDir = 0;
2708         if (oldDscp->fid.cell != newDscp->fid.cell ||
2709              oldDscp->fid.volume != newDscp->fid.volume)
2710             return CM_ERROR_CROSSDEVLINK;
2711
2712         /* shouldn't happen that we have distinct vnodes for two
2713          * different files, but could due to deliberate attack, or
2714          * stale info.  Avoid deadlocks and quit now.
2715          */
2716         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2717             return CM_ERROR_CROSSDEVLINK;
2718
2719         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2720             lock_ObtainMutex(&oldDscp->mx);
2721             cm_dnlcRemove(oldDscp, oldNamep);
2722             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2723                               CM_SCACHESYNC_STOREDATA);
2724             lock_ReleaseMutex(&oldDscp->mx);
2725             if (code == 0) {
2726                 lock_ObtainMutex(&newDscp->mx);
2727                 cm_dnlcRemove(newDscp, newNamep);
2728                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2729                                   CM_SCACHESYNC_STOREDATA);
2730                 lock_ReleaseMutex(&newDscp->mx);
2731                 if (code) {
2732                     /* cleanup first one */
2733                     lock_ObtainMutex(&newDscp->mx);
2734                     cm_SyncOpDone(oldDscp, NULL,
2735                                    CM_SCACHESYNC_STOREDATA);
2736                     lock_ReleaseMutex(&oldDscp->mx);
2737                 }       
2738             }
2739         }
2740         else {
2741             /* lock the new vnode entry first */
2742             lock_ObtainMutex(&newDscp->mx);
2743             cm_dnlcRemove(newDscp, newNamep);
2744             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2745                               CM_SCACHESYNC_STOREDATA);
2746             lock_ReleaseMutex(&newDscp->mx);
2747             if (code == 0) {
2748                 lock_ObtainMutex(&oldDscp->mx);
2749                 cm_dnlcRemove(oldDscp, oldNamep);
2750                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2751                                   CM_SCACHESYNC_STOREDATA);
2752                 lock_ReleaseMutex(&oldDscp->mx);
2753                 if (code) {
2754                     /* cleanup first one */
2755                     lock_ObtainMutex(&newDscp->mx);
2756                     cm_SyncOpDone(newDscp, NULL,
2757                                    CM_SCACHESYNC_STOREDATA);
2758                     lock_ReleaseMutex(&newDscp->mx);
2759                 }       
2760             }
2761         }
2762     }   /* two distinct vnodes */
2763
2764     if (code) {
2765         return code;
2766     }
2767     didEnd = 0;
2768
2769     /* try the RPC now */
2770     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p", 
2771               oldDscp, newDscp);
2772     do {
2773         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2774         if (code) 
2775             continue;
2776
2777         oldDirAFSFid.Volume = oldDscp->fid.volume;
2778         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2779         oldDirAFSFid.Unique = oldDscp->fid.unique;
2780         newDirAFSFid.Volume = newDscp->fid.volume;
2781         newDirAFSFid.Vnode = newDscp->fid.vnode;
2782         newDirAFSFid.Unique = newDscp->fid.unique;
2783
2784         callp = cm_GetRxConn(connp);
2785         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2786                              &newDirAFSFid, newNamep,
2787                              &updatedOldDirStatus, &updatedNewDirStatus,
2788                              &volSync);
2789         rx_PutConnection(callp);
2790
2791     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2792                          &volSync, NULL, NULL, code));
2793     code = cm_MapRPCError(code, reqp);
2794         
2795     if (code)
2796         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2797     else
2798         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2799
2800     /* update the individual stat cache entries for the directories */
2801     lock_ObtainMutex(&oldDscp->mx);
2802     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2803     if (code == 0) {
2804         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2805                         userp, 0);
2806     }
2807     lock_ReleaseMutex(&oldDscp->mx);
2808
2809     /* and update it for the new one, too, if necessary */
2810     if (!oneDir) {
2811         lock_ObtainMutex(&newDscp->mx);
2812         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2813         if (code == 0) {
2814             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2815                             userp, 0);
2816         }
2817         lock_ReleaseMutex(&newDscp->mx);
2818     }
2819
2820     /* and return error code */
2821     return code;
2822 }
2823
2824 /* Byte range locks:
2825
2826    The OpenAFS Windows client has to fake byte range locks given no
2827    server side support for such locks.  This is implemented as keyed
2828    byte range locks on the cache manager.
2829
2830    Keyed byte range locks:
2831
2832    Each cm_scache_t structure keeps track of a list of keyed locks.
2833    The key for a lock identifies an owner of a set of locks (referred
2834    to as a client).  Each key is represented by a value.  The set of
2835    key values used within a specific cm_scache_t structure form a
2836    namespace that has a scope of just that cm_scache_t structure.  The
2837    same key value can be used with another cm_scache_t structure and
2838    correspond to a completely different client.  However it is
2839    advantageous for the SMB or IFS layer to make sure that there is a
2840    1-1 mapping between client and keys over all cm_scache_t objects.
2841
2842    Assume a client C has key Key(C) (although, since the scope of the
2843    key is a cm_scache_t, the key can be Key(C,S), where S is the
2844    cm_scache_t.  But assume a 1-1 relation between keys and clients).
2845    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
2846    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
2847    through cm_generateKey() function for both SMB and IFS.
2848
2849    The list of locks for a cm_scache_t object S is maintained in
2850    S->fileLocks.  The cache manager will set a lock on the AFS file
2851    server in order to assert the locks in S->fileLocks.  If only
2852    shared locks are in place for S, then the cache manager will obtain
2853    a LockRead lock, while if there are any exclusive locks, it will
2854    obtain a LockWrite lock.  If the exclusive locks are all released
2855    while the shared locks remain, then the cache manager will
2856    downgrade the lock from LockWrite to LockRead.  Similarly, if an
2857    exclusive lock is obtained when only shared locks exist, then the
2858    cache manager will try to upgrade the lock from LockRead to
2859    LockWrite.
2860
2861    Each lock L owned by client C maintains a key L->key such that
2862    L->key == Key(C), the effective range defined by L->LOffset and
2863    L->LLength such that the range of bytes affected by the lock is
2864    (L->LOffset, +L->LLength), a type maintained in L->LockType which
2865    is either exclusive or shared.
2866
2867    Lock states:
2868
2869    A lock exists iff it is in S->fileLocks for some cm_scache_t
2870    S. Existing locks are in one of the following states: ACTIVE,
2871    WAITLOCK, WAITUNLOCK, LOST, DELETED.
2872
2873    The following sections describe each lock and the associated
2874    transitions.
2875
2876    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
2877       the lock with the AFS file server.  This type of lock can be
2878       exercised by a client to read or write to the locked region (as
2879       the lock allows).
2880
2881       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
2882         server lock that was required to assert the lock.  Before
2883         marking the lock as lost, the cache manager checks if the file
2884         has changed on the server.  If the file has not changed, then
2885         the cache manager will attempt to obtain a new server lock
2886         that is sufficient to assert the client side locks for the
2887         file.  If any of these fail, the lock is marked as LOST.
2888         Otherwise, it is left as ACTIVE.
2889
2890       1.2 ACTIVE->DELETED: Lock is released.
2891
2892    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
2893       grants the lock but the lock is yet to be asserted with the AFS
2894       file server.  Once the file server grants the lock, the state
2895       will transition to an ACTIVE lock.
2896
2897       2.1 WAITLOCK->ACTIVE: The server granted the lock.
2898
2899       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
2900         waiting.
2901
2902       2.3 WAITLOCK->LOST: One or more locks from this client were
2903         marked as LOST.  No further locks will be granted to this
2904         client until all lost locks are removed.
2905
2906    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
2907       receives a request for a lock that conflicts with an existing
2908       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
2909       and will be granted at such time the conflicting locks are
2910       removed, at which point the state will transition to either
2911       WAITLOCK or ACTIVE.
2912
2913       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
2914         current serverLock is sufficient to assert this lock, or a
2915         sufficient serverLock is obtained.
2916
2917       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
2918         however the required serverLock is yet to be asserted with the
2919         server.
2920
2921       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
2922         released.
2923
2924       3.5 WAITUNLOCK->LOST: One or more locks from this client were
2925         marked as LOST.  No further locks will be granted to this
2926         client until all lost locks are removed.
2927
2928    4. LOST: A lock L is LOST if the server lock that was required to
2929       assert the lock could not be obtained or if it could not be
2930       extended, or if other locks by the same client were LOST.
2931       Essentially, once a lock is LOST, the contract between the cache
2932       manager and that specific client is no longer valid.
2933
2934       The cache manager rechecks the server lock once every minute and
2935       extends it as appropriate.  If this is not done for 5 minutes,
2936       the AFS file server will release the lock (the 5 minute timeout
2937       is based on current file server code and is fairly arbitrary).
2938       Once released, the lock cannot be re-obtained without verifying
2939       that the contents of the file hasn't been modified since the
2940       time the lock was released.  Re-obtaining the lock without
2941       verifying this may lead to data corruption.  If the lock can not
2942       be obtained safely, then all active locks for the cm_scache_t
2943       are marked as LOST.
2944
2945       4.1 LOST->DELETED: The lock is released.
2946
2947    5. DELETED: The lock is no longer relevant.  Eventually, it will
2948       get removed from the cm_scache_t. In the meantime, it will be
2949       treated as if it does not exist.
2950
2951       5.1 DELETED->not exist: The lock is removed from the
2952         cm_scache_t.
2953
2954    The following are classifications of locks based on their state.
2955
2956    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
2957       have been accepted by the cache manager, but may or may not have
2958       been granted back to the client.
2959
2960    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
2961
2962    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
2963
2964    Lock operation:
2965
2966    A client C can READ range (Offset,+Length) of a file represented by
2967    cm_scache_t S iff (1):
2968
2969    1. for all _a_ in (Offset,+Length), all of the following is true:
2970
2971        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
2972          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
2973          shared.
2974
2975        1.2 For each LOST lock L in S->fileLocks such that _a_ in
2976          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
2977          Key(C)
2978
2979        (When locks are lost on an cm_scache_t, all locks are lost.  By
2980        4.2 (below), if there is an exclusive LOST lock, then there
2981        can't be any overlapping ACTIVE locks.)
2982
2983    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
2984
2985    2. for all _a_ in (Offset,+Length), one of the following is true:
2986
2987        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
2988          does not exist a LOST lock L such that _a_ in
2989          (L->LOffset,+L->LLength).
2990
2991        2.2 Byte _a_ of S is owned by C under lock L (as specified in
2992          1.2) AND L->LockType is exclusive.
2993
2994    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
2995
2996    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
2997       true:
2998
2999        3.1 If L->LockType is exclusive then there does NOT exist a
3000          ACCEPTED lock M in S->fileLocks such that _a_ in
3001          (M->LOffset,+M->LLength).
3002
3003          (If we count all QUEUED locks then we hit cases such as
3004          cascading waiting locks where the locks later on in the queue
3005          can be granted without compromising file integrity.  On the
3006          other hand if only ACCEPTED locks are considered, then locks
3007          that were received earlier may end up waiting for locks that
3008          were received later to be unlocked. The choice of ACCEPTED
3009          locks was made to mimic the Windows byte range lock
3010          semantics.)
3011
3012        3.2 If L->LockType is shared then for each ACCEPTED lock M in
3013          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
3014          M->LockType is shared.
3015
3016    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
3017
3018        4.1 M->key != Key(C)
3019
3020        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
3021          and (M->LOffset,+M->LLength) do not intersect.
3022
3023          (Note: If a client loses a lock, it loses all locks.
3024          Subsequently, it will not be allowed to obtain any more locks
3025          until all existing LOST locks that belong to the client are
3026          released.  Once all locks are released by a single client,
3027          there exists no further contract between the client and AFS
3028          about the contents of the file, hence the client can then
3029          proceed to obtain new locks and establish a new contract.
3030
3031          This doesn't quite work as you think it should, because most
3032          applications aren't built to deal with losing locks they
3033          thought they once had.  For now, we don't have a good
3034          solution to lost locks.
3035
3036          Also, for consistency reasons, we have to hold off on
3037          granting locks that overlap exclusive LOST locks.)
3038
3039    A client C can only unlock locks L in S->fileLocks which have
3040    L->key == Key(C).
3041
3042    The representation and invariants are as follows:
3043
3044    - Each cm_scache_t structure keeps:
3045
3046        - A queue of byte-range locks (cm_scache_t::fileLocks) which
3047          are of type cm_file_lock_t.
3048
3049        - A record of the highest server-side lock that has been
3050          obtained for this object (cm_scache_t::serverLock), which is
3051          one of (-1), LockRead, LockWrite.
3052
3053        - A count of ACCEPTED exclusive and shared locks that are in the
3054          queue (cm_scache_t::sharedLocks and
3055          cm_scache_t::exclusiveLocks)
3056
3057    - Each cm_file_lock_t structure keeps:
3058
3059        - The type of lock (cm_file_lock_t::LockType)
3060
3061        - The key associated with the lock (cm_file_lock_t::key)
3062
3063        - The offset and length of the lock (cm_file_lock_t::LOffset
3064          and cm_file_lock_t::LLength)
3065
3066        - The state of the lock.
3067
3068        - Time of issuance or last successful extension
3069
3070    Semantic invariants:
3071
3072        I1. The number of ACCEPTED locks in S->fileLocks are
3073            (S->sharedLocks + S->exclusiveLocks)
3074
3075    External invariants:
3076
3077        I3. S->serverLock is the lock that we have asserted with the
3078            AFS file server for this cm_scache_t.
3079
3080        I4. S->serverLock == LockRead iff there is at least one ACTIVE
3081            shared lock, but no ACTIVE exclusive locks.
3082
3083        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
3084            exclusive lock.
3085
3086        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
3087            M->key == L->key IMPLIES M is LOST or DELETED.
3088
3089    --asanka
3090  */
3091
3092 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
3093
3094 #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)
3095
3096 #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)
3097
3098 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
3099
3100 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
3101
3102 /* unsafe */
3103 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
3104
3105 /* unsafe */
3106 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
3107
3108 /* unsafe */
3109 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
3110
3111 /* unsafe */
3112 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
3113
3114 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
3115 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
3116 #else
3117 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
3118 #endif
3119
3120 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
3121
3122 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
3123 {
3124     afs_int64 int_begin;
3125     afs_int64 int_end;
3126
3127     int_begin = MAX(pos->offset, neg->offset);
3128     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
3129
3130     if (int_begin < int_end) {
3131         if (int_begin == pos->offset) {
3132             pos->length = pos->offset + pos->length - int_end;
3133             pos->offset = int_end;
3134         } else if (int_end == pos->offset + pos->length) {
3135             pos->length = int_begin - pos->offset;
3136         }
3137
3138         /* We only subtract ranges if the resulting range is
3139            contiguous.  If we try to support non-contigous ranges, we
3140            aren't actually improving performance. */
3141     }
3142 }
3143
3144 /* Called with scp->mx held.  Returns 0 if all is clear to read the
3145    specified range by the client identified by key.
3146  */
3147 long cm_LockCheckRead(cm_scache_t *scp, 
3148                       LARGE_INTEGER LOffset, 
3149                       LARGE_INTEGER LLength, 
3150                       cm_key_t key)
3151 {
3152 #ifndef ADVISORY_LOCKS
3153
3154     cm_file_lock_t *fileLock;
3155     osi_queue_t *q;
3156     long code = 0;
3157     cm_range_t range;
3158     int substract_ranges = FALSE;
3159
3160     range.offset = LOffset.QuadPart;
3161     range.length = LLength.QuadPart;
3162
3163     /*
3164
3165      1. for all _a_ in (Offset,+Length), all of the following is true:
3166
3167        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3168          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3169          shared.
3170
3171        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3172          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3173          Key(C)
3174
3175     */
3176
3177     lock_ObtainRead(&cm_scacheLock);
3178
3179     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3180         fileLock = 
3181             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3182
3183         if (INTERSECT_RANGE(range, fileLock->range)) {
3184             if (IS_LOCK_ACTIVE(fileLock)) {
3185                 if (fileLock->key == key) {
3186
3187                     /* If there is an active lock for this client, it
3188                        is safe to substract ranges.*/
3189                     cm_LockRangeSubtract(&range, &fileLock->range);
3190                     substract_ranges = TRUE;
3191                 } else {
3192                     if (fileLock->lockType != LockRead) {
3193                         code = CM_ERROR_LOCK_CONFLICT;
3194                         break;
3195                     }
3196
3197                     /* even if the entire range is locked for reading,
3198                        we still can't grant the lock at this point
3199                        because the client may have lost locks. That
3200                        is, unless we have already seen an active lock
3201                        belonging to the client, in which case there
3202                        can't be any lost locks for this client. */
3203                     if (substract_ranges)
3204                         cm_LockRangeSubtract(&range, &fileLock->range);
3205                 }
3206             } else if (IS_LOCK_LOST(fileLock) &&
3207                       (fileLock->key == key || fileLock->lockType == LockWrite)) {
3208                 code = CM_ERROR_BADFD;
3209                 break;
3210             }
3211         }
3212     }
3213
3214     lock_ReleaseRead(&cm_scacheLock);
3215
3216     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
3217               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3218
3219     return code;
3220
3221 #else
3222
3223     return 0;
3224
3225 #endif
3226 }
3227
3228 /* Called with scp->mx held.  Returns 0 if all is clear to write the
3229    specified range by the client identified by key.
3230  */
3231 long cm_LockCheckWrite(cm_scache_t *scp,
3232                        LARGE_INTEGER LOffset,
3233                        LARGE_INTEGER LLength,
3234                        cm_key_t key)
3235 {
3236 #ifndef ADVISORY_LOCKS
3237
3238     cm_file_lock_t *fileLock;
3239     osi_queue_t *q;
3240     long code = 0;
3241     cm_range_t range;
3242
3243     range.offset = LOffset.QuadPart;
3244     range.length = LLength.QuadPart;
3245
3246     /*
3247    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
3248
3249    2. for all _a_ in (Offset,+Length), one of the following is true:
3250
3251        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
3252          lock L such that _a_ in (L->LOffset,+L->LLength).
3253
3254        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
3255          exclusive.
3256     */
3257
3258     lock_ObtainRead(&cm_scacheLock);
3259
3260     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
3261         fileLock = 
3262             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
3263
3264         if (INTERSECT_RANGE(range, fileLock->range)) {
3265             if (IS_LOCK_ACTIVE(fileLock)) {
3266                 if (fileLock->key == key) {
3267                     if (fileLock->lockType == LockWrite) {
3268
3269                         /* if there is an active lock for this client, it
3270                            is safe to substract ranges */
3271                         cm_LockRangeSubtract(&range, &fileLock->range);
3272                     } else {
3273                         code = CM_ERROR_LOCK_CONFLICT;
3274                         break;
3275                     }
3276                 } else {
3277                     code = CM_ERROR_LOCK_CONFLICT;
3278                     break;
3279                 }
3280             } else if (IS_LOCK_LOST(fileLock)) {
3281                 code = CM_ERROR_BADFD;
3282                 break;
3283             }
3284         }
3285     }
3286
3287     lock_ReleaseRead(&cm_scacheLock);
3288
3289     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
3290               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
3291
3292     return code;
3293
3294 #else
3295
3296     return 0;
3297
3298 #endif
3299 }
3300
3301 /* Forward dcl. */
3302 static void cm_LockMarkSCacheLost(cm_scache_t * scp);
3303
3304 /* Called with cm_scacheLock write locked */
3305 static cm_file_lock_t * cm_GetFileLock(void) {
3306     cm_file_lock_t * l;
3307
3308     l = (cm_file_lock_t *) cm_freeFileLocks;
3309     if (l) {
3310         osi_QRemove(&cm_freeFileLocks, &l->q);
3311     } else {
3312         l = malloc(sizeof(cm_file_lock_t));
3313         osi_assert(l);
3314     }
3315
3316     memset(l, 0, sizeof(cm_file_lock_t));
3317
3318     return l;
3319 }
3320
3321 /* Called with cm_scacheLock write locked */
3322 static void cm_PutFileLock(cm_file_lock_t *l) {
3323     osi_QAdd(&cm_freeFileLocks, &l->q);
3324 }
3325
3326 /* called with scp->mx held.  May release it during processing, but
3327    leaves it held on exit. */
3328 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
3329                    cm_req_t * reqp) {
3330     long code = 0;
3331     AFSFid tfid;
3332     cm_fid_t cfid;
3333     cm_conn_t * connp;
3334     struct rx_connection * callp;
3335     AFSVolSync volSync;
3336
3337     tfid.Volume = scp->fid.volume;
3338     tfid.Vnode = scp->fid.vnode;
3339     tfid.Unique = scp->fid.unique;
3340     cfid = scp->fid;
3341
3342     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
3343
3344     lock_ReleaseMutex(&scp->mx);
3345
3346     do {
3347         code = cm_Conn(&cfid, userp, reqp, &connp);
3348         if (code) 
3349             break;
3350
3351         callp = cm_GetRxConn(connp);
3352         code = RXAFS_SetLock(callp, &tfid, lockType,
3353                              &volSync);
3354         rx_PutConnection(callp);
3355
3356     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3357                         NULL, NULL, code));
3358
3359     code = cm_MapRPCError(code, reqp);
3360     if (code) {
3361         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
3362     } else {
3363         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
3364     }
3365
3366     lock_ObtainMutex(&scp->mx);
3367
3368     return code;
3369 }
3370
3371 /* called with scp->mx held.  Releases it during processing */
3372 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
3373                        cm_req_t * reqp) {
3374     long code = 0;
3375     AFSFid tfid;
3376     cm_fid_t cfid;
3377     cm_conn_t * connp;
3378     struct rx_connection * callp;
3379     AFSVolSync volSync;
3380
3381     tfid.Volume = scp->fid.volume;
3382     tfid.Vnode = scp->fid.vnode;
3383     tfid.Unique = scp->fid.unique;
3384     cfid = scp->fid;
3385
3386     lock_ReleaseMutex(&scp->mx);
3387
3388     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
3389
3390     do {
3391         code = cm_Conn(&cfid, userp, reqp, &connp);
3392         if (code) 
3393             break;
3394
3395         callp = cm_GetRxConn(connp);
3396         code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
3397         rx_PutConnection(callp);
3398
3399     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
3400                         NULL, NULL, code));
3401     code = cm_MapRPCError(code, reqp);
3402     if (code)
3403         osi_Log1(afsd_logp,
3404                  "CALL ReleaseLock FAILURE, code 0x%x", code);
3405     else
3406         osi_Log0(afsd_logp,
3407                  "CALL ReleaseLock SUCCESS");
3408         
3409     lock_ObtainMutex(&scp->mx);
3410
3411     return code;
3412 }
3413
3414 /* called with scp->mx held.  May release it during processing, but
3415    will exit with lock held.
3416
3417    This will return:
3418
3419    - 0 if the user has permission to get the specified lock for the scp
3420
3421    - CM_ERROR_NOACCESS if not
3422
3423    Any other error from cm_SyncOp will be sent down untranslated.
3424 */
3425 long cm_LockCheckPerms(cm_scache_t * scp,
3426                        int lock_type,
3427                        cm_user_t * userp,
3428                        cm_req_t * reqp)
3429 {
3430     long rights = 0;
3431     long code = 0;
3432
3433     /* lock permissions are slightly tricky because of the 'i' bit.
3434        If the user has PRSFS_LOCK, she can read-lock the file.  If the
3435        user has PRSFS_WRITE, she can write-lock the file.  However, if
3436        the user has PRSFS_INSERT, then she can write-lock new files,
3437        but not old ones.  Since we don't have information about
3438        whether a file is new or not, we assume that if the user owns
3439        the scp, then she has the permissions that are granted by
3440        PRSFS_INSERT. */
3441
3442     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
3443              scp, lock_type, userp);
3444
3445     if (lock_type == LockRead)
3446         rights |= PRSFS_LOCK;
3447     else if (lock_type == LockWrite)
3448         rights |= PRSFS_WRITE;
3449     else {
3450         /* hmmkay */
3451         osi_assert(FALSE);
3452         return 0;
3453     }
3454
3455     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
3456                      CM_SCACHESYNC_GETSTATUS |
3457                      CM_SCACHESYNC_NEEDCALLBACK);
3458
3459     if (code == CM_ERROR_NOACCESS &&
3460         lock_type == LockWrite &&
3461         scp->creator == userp) {
3462         /* check for PRSFS_INSERT. */
3463
3464         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
3465                          CM_SCACHESYNC_GETSTATUS |
3466                          CM_SCACHESYNC_NEEDCALLBACK);
3467
3468         if (code == CM_ERROR_NOACCESS)
3469             osi_Log0(afsd_logp, "cm_LockCheckPerms user is creator but has no INSERT bits for scp");
3470     }
3471
3472     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
3473
3474     return code;
3475 }
3476
3477 /* called with scp->mx held */
3478 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
3479              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
3480              cm_key_t key,
3481              int allowWait, cm_user_t *userp, cm_req_t *reqp,
3482              cm_file_lock_t **lockpp)
3483 {
3484     long code = 0;