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