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