windows-time-20050331
[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 (NT_TRANSACTION2, 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
1200     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%x", (long) dscp);
1201     do {
1202         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1203         if (code) 
1204             continue;
1205
1206         callp = cm_GetRxConn(connp);
1207         code = RXAFS_RemoveFile(callp, &afsFid, namep,
1208                                  &newDirStatus, &volSync);
1209         rx_PutConnection(callp);
1210
1211     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1212     code = cm_MapRPCError(code, reqp);
1213
1214     if (code)
1215         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1216     else
1217         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1218
1219     lock_ObtainMutex(&dscp->mx);
1220     cm_dnlcRemove(dscp, namep);
1221     cm_SyncOpDone(dscp, NULL, sflags);
1222     if (code == 0) 
1223         cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1224     lock_ReleaseMutex(&dscp->mx);
1225
1226     return code;
1227 }
1228
1229 /* called with a locked vnode, and fills in the link info.
1230  * returns this the vnode still locked.
1231  */
1232 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1233 {
1234     long code;
1235     cm_buf_t *bufp;
1236     long temp;
1237     osi_hyper_t thyper;
1238
1239     lock_AssertMutex(&linkScp->mx);
1240     if (!linkScp->mountPointStringp[0]) {
1241         /* read the link data */
1242         lock_ReleaseMutex(&linkScp->mx);
1243         thyper.LowPart = thyper.HighPart = 0;
1244         code = buf_Get(linkScp, &thyper, &bufp);
1245         lock_ObtainMutex(&linkScp->mx);
1246         if (code) 
1247             return code;
1248         while (1) {
1249             code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1250                               CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1251             if (code) {
1252                 buf_Release(bufp);
1253                 return code;
1254             }
1255             if (cm_HaveBuffer(linkScp, bufp, 0)) 
1256                 break;
1257
1258             code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1259             if (code) {
1260                 buf_Release(bufp);
1261                 return code;
1262             }
1263         } /* while loop to get the data */
1264                 
1265         /* now if we still have no link read in,
1266          * copy the data from the buffer */
1267         if ((temp = linkScp->length.LowPart) >= MOUNTPOINTLEN) {
1268             buf_Release(bufp);
1269             return CM_ERROR_TOOBIG;
1270         }
1271
1272         /* otherwise, it fits; make sure it is still null (could have
1273          * lost race with someone else referencing this link above),
1274          * and if so, copy in the data.
1275          */
1276         if (!linkScp->mountPointStringp[0]) {
1277             strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1278             linkScp->mountPointStringp[temp] = 0;       /* null terminate */
1279         }
1280         buf_Release(bufp);
1281     }   /* don't have sym link contents cached */
1282
1283     return 0;
1284 }       
1285
1286 /* called with a held vnode and a path suffix, with the held vnode being a
1287  * symbolic link.  Our goal is to generate a new path to interpret, and return
1288  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1289  * other than the directory containing the symbolic link, then the new root is
1290  * returned in *newRootScpp, otherwise a null is returned there.
1291  */
1292 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1293                       cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1294                       cm_user_t *userp, cm_req_t *reqp)
1295 {
1296     long code = 0;
1297     long len;
1298     char *linkp;
1299     cm_space_t *tsp;
1300
1301     lock_ObtainMutex(&linkScp->mx);
1302     code = cm_HandleLink(linkScp, userp, reqp);
1303     if (code) 
1304         goto done;
1305
1306     /* if we may overflow the buffer, bail out; buffer is signficantly
1307      * bigger than max path length, so we don't really have to worry about
1308      * being a little conservative here.
1309      */
1310     if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1311          >= CM_UTILS_SPACESIZE)
1312         return CM_ERROR_TOOBIG;
1313
1314     tsp = cm_GetSpace();
1315     linkp = linkScp->mountPointStringp;
1316     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1317         if (strlen(linkp) > cm_mountRootLen)
1318             strcpy(tsp->data, linkp+cm_mountRootLen+1);
1319         else
1320             tsp->data[0] = 0;
1321         *newRootScpp = cm_data.rootSCachep;
1322         cm_HoldSCache(cm_data.rootSCachep);
1323     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1324         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = strlen(cm_NetbiosName)))) 
1325         {
1326             char * p = &linkp[len + 3];
1327             if (strnicmp(p, "all", 3) == 0)
1328                 p += 4;
1329
1330             strcpy(tsp->data, p);
1331             for (p = tsp->data; *p; p++) {
1332                 if (*p == '\\')
1333                     *p = '/';
1334             }
1335             *newRootScpp = cm_data.rootSCachep;
1336             cm_HoldSCache(cm_data.rootSCachep);
1337         } else {
1338             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1339             strcpy(tsp->data, linkp);
1340             *newRootScpp = NULL;
1341             code = CM_ERROR_PATH_NOT_COVERED;
1342         }
1343     } else if ( !strnicmp(linkp, "msdfs:", (len = strlen("msdfs:"))) ) {
1344         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1345         strcpy(tsp->data, linkp);
1346         *newRootScpp = NULL;
1347         code = CM_ERROR_PATH_NOT_COVERED;
1348     } else if (*linkp == '\\' || *linkp == '/') {
1349 #if 0   
1350         /* formerly, this was considered to be from the AFS root,
1351          * but this seems to create problems.  instead, we will just
1352          * reject the link */
1353         strcpy(tsp->data, linkp+1);
1354         *newRootScpp = cm_data.rootSCachep;
1355         cm_HoldSCache(cm_data.rootSCachep);
1356 #else
1357         /* we still copy the link data into the response so that 
1358          * the user can see what the link points to
1359          */
1360         linkScp->fileType = CM_SCACHETYPE_INVALID;
1361         strcpy(tsp->data, linkp);
1362         *newRootScpp = NULL;
1363         code = CM_ERROR_NOSUCHPATH;
1364 #endif  
1365     } else {
1366         /* a relative link */
1367         strcpy(tsp->data, linkp);
1368         *newRootScpp = NULL;
1369     }
1370     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1371         strcat(tsp->data, "\\");
1372         strcat(tsp->data, pathSuffixp);
1373     }
1374     *newSpaceBufferp = tsp;
1375
1376   done:
1377     lock_ReleaseMutex(&linkScp->mx);
1378     return code;
1379 }
1380
1381 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1382                cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1383 {
1384     long code;
1385     char *tp;                   /* ptr moving through input buffer */
1386     char tc;                    /* temp char */
1387     int haveComponent;          /* has new component started? */
1388     char component[256];        /* this is the new component */
1389     char *cp;                   /* component name being assembled */
1390     cm_scache_t *tscp;          /* current location in the hierarchy */
1391     cm_scache_t *nscp;          /* next dude down */
1392     cm_scache_t *dirScp;        /* last dir we searched */
1393     cm_scache_t *linkScp;       /* new root for the symlink we just
1394     * looked up */
1395     cm_space_t *psp;            /* space for current path, if we've hit
1396     * any symlinks */
1397     cm_space_t *tempsp;         /* temp vbl */
1398     char *restp;                /* rest of the pathname to interpret */
1399     int symlinkCount;           /* count of # of symlinks traversed */
1400     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1401     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1402
1403     tp = tidPathp;
1404     if (tp == NULL) {
1405         tp = pathp;
1406         phase = 2;
1407     }
1408     if (tp == NULL) {
1409         tp = "";
1410     }
1411     haveComponent = 0;
1412     psp = NULL;
1413     tscp = rootSCachep;
1414     cm_HoldSCache(tscp);
1415     symlinkCount = 0;
1416     dirScp = 0;
1417
1418     while (1) {
1419         tc = *tp++;
1420
1421         /* map Unix slashes into DOS ones so we can interpret Unix
1422          * symlinks properly
1423          */
1424         if (tc == '/') 
1425             tc = '\\';
1426
1427         if (!haveComponent) {
1428             if (tc == '\\') {
1429                 continue;
1430             } else if (tc == 0) {
1431                 if (phase == 1) {
1432                     phase = 2;
1433                     tp = pathp;
1434                     continue;
1435                 }
1436                 code = 0;
1437                 break;
1438             } else {
1439                 haveComponent = 1;
1440                 cp = component;
1441                 *cp++ = tc;
1442             }
1443         } else {
1444             /* we have a component here */
1445             if (tc == 0 || tc == '\\') {
1446                 /* end of the component; we're at the last
1447                  * component if tc == 0.  However, if the last
1448                  * is a symlink, we have more to do.
1449                  */
1450                 *cp++ = 0;      /* add null termination */
1451                 extraFlag = 0;
1452                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1453                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1454                 code = cm_Lookup(tscp, component,
1455                                   flags | extraFlag,
1456                                   userp, reqp, &nscp);
1457                 if (code) {
1458                     cm_ReleaseSCache(tscp);
1459                     if (dirScp)
1460                         cm_ReleaseSCache(dirScp);
1461                     if (psp) 
1462                         cm_FreeSpace(psp);
1463                     if (code == CM_ERROR_NOSUCHFILE && tscp->fileType == CM_SCACHETYPE_SYMLINK)
1464                         return CM_ERROR_NOSUCHPATH;
1465                     else
1466                         return code;
1467                 }
1468                 haveComponent = 0;      /* component done */
1469                 if (dirScp)
1470                     cm_ReleaseSCache(dirScp);
1471                 dirScp = tscp;          /* for some symlinks */
1472                 tscp = nscp;            /* already held */
1473                 nscp = 0;
1474                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1475                     code = 0;
1476                     if (dirScp) {
1477                         cm_ReleaseSCache(dirScp);
1478                         dirScp = 0;
1479                     }
1480                     break;
1481                 }
1482
1483                 /* now, if tscp is a symlink, we should follow
1484                  * it and assemble the path again.
1485                  */
1486                 lock_ObtainMutex(&tscp->mx);
1487                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1488                                   CM_SCACHESYNC_GETSTATUS
1489                                   | CM_SCACHESYNC_NEEDCALLBACK);
1490                 if (code) {
1491                     lock_ReleaseMutex(&tscp->mx);
1492                     cm_ReleaseSCache(tscp);
1493                     tscp = 0;
1494                     if (dirScp) {
1495                         cm_ReleaseSCache(dirScp);
1496                         dirScp = 0;
1497                     }
1498                     break;
1499                 }
1500                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1501                     /* this is a symlink; assemble a new buffer */
1502                     lock_ReleaseMutex(&tscp->mx);
1503                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
1504                         cm_ReleaseSCache(tscp);
1505                         tscp = 0;
1506                         if (dirScp) {
1507                             cm_ReleaseSCache(dirScp);
1508                             dirScp = 0;
1509                         }
1510                         if (psp) 
1511                             cm_FreeSpace(psp);
1512                         return CM_ERROR_TOO_MANY_SYMLINKS;
1513                     }
1514                     if (tc == 0) 
1515                         restp = "";
1516                     else 
1517                         restp = tp;
1518                     code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1519                     if (code) {
1520                         /* something went wrong */
1521                         cm_ReleaseSCache(tscp);
1522                         tscp = 0;
1523                         if (dirScp) {
1524                             cm_ReleaseSCache(dirScp);
1525                             dirScp = 0;
1526                         }
1527                         break;
1528                     }
1529
1530                     /* otherwise, tempsp has the new path,
1531                      * and linkScp is the new root from
1532                      * which to interpret that path.
1533                      * Continue with the namei processing,
1534                      * also doing the bookkeeping for the
1535                      * space allocation and tracking the
1536                      * vnode reference counts.
1537                      */
1538                     if (psp) 
1539                         cm_FreeSpace(psp);
1540                     psp = tempsp;
1541                     tp = psp->data;
1542                     cm_ReleaseSCache(tscp);
1543                     tscp = linkScp;
1544                     linkScp = 0;
1545                     /* already held
1546                      * by AssembleLink
1547                      * now, if linkScp is null, that's
1548                      * AssembleLink's way of telling us that
1549                      * the sym link is relative to the dir
1550                      * containing the link.  We have a ref
1551                      * to it in dirScp, and we hold it now
1552                      * and reuse it as the new spot in the
1553                      * dir hierarchy.
1554                      */
1555                     if (tscp == NULL) {
1556                         tscp = dirScp;
1557                         dirScp = 0;
1558                     }
1559                 } else {
1560                     /* not a symlink, we may be done */
1561                     lock_ReleaseMutex(&tscp->mx);
1562                     if (tc == 0) {
1563                         if (phase == 1) {
1564                             phase = 2;
1565                             tp = pathp;
1566                             continue;
1567                         }
1568                         if (dirScp) {
1569                             cm_ReleaseSCache(dirScp);
1570                             dirScp = 0;
1571                         }
1572                         code = 0;
1573                         break;
1574                     }
1575                 }
1576                 if (dirScp) {
1577                     cm_ReleaseSCache(dirScp);
1578                     dirScp = 0;
1579                 }
1580             } /* end of a component */
1581             else 
1582                 *cp++ = tc;
1583         } /* we have a component */
1584     } /* big while loop over all components */
1585
1586     /* already held */
1587     if (dirScp)
1588         cm_ReleaseSCache(dirScp);
1589     if (psp) 
1590         cm_FreeSpace(psp);
1591     if (code == 0) 
1592         *outScpp = tscp;
1593     else if (tscp)
1594         cm_ReleaseSCache(tscp);
1595     return code;
1596 }
1597
1598 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1599  * We chase the link, and return a held pointer to the target, if it exists,
1600  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1601  * and do not hold or return a target vnode.
1602  *
1603  * This is very similar to calling cm_NameI with the last component of a name,
1604  * which happens to be a symlink, except that we've already passed by the name.
1605  *
1606  * This function is typically called by the directory listing functions, which
1607  * encounter symlinks but need to return the proper file length so programs
1608  * like "more" work properly when they make use of the attributes retrieved from
1609  * the dir listing.
1610  *
1611  * The input vnode should not be locked when this function is called.
1612  */
1613 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1614                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1615 {
1616     long code;
1617     cm_space_t *spacep;
1618     cm_scache_t *newRootScp;
1619
1620     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%x", linkScp);
1621
1622     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1623     if (code) 
1624         return code;
1625
1626     /* now, if newRootScp is NULL, we're really being told that the symlink
1627      * is relative to the current directory (dscp).
1628      */
1629     if (newRootScp == NULL) {
1630         newRootScp = dscp;
1631         cm_HoldSCache(dscp);
1632     }
1633
1634     code = cm_NameI(newRootScp, spacep->data,
1635                      CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1636                      userp, NULL, reqp, outScpp);
1637
1638         if (code == CM_ERROR_NOSUCHFILE)
1639                 code = CM_ERROR_NOSUCHPATH;
1640
1641     /* this stuff is allocated no matter what happened on the namei call,
1642      * so free it */
1643     cm_FreeSpace(spacep);
1644     cm_ReleaseSCache(newRootScp);
1645
1646     return code;
1647 }
1648
1649 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1650  * check anyway, but we want to minimize the chance that we have to leave stuff
1651  * unstat'd.
1652  */
1653 #define CM_BULKMAX              128
1654
1655 /* rock for bulk stat calls */
1656 typedef struct cm_bulkStat {
1657     osi_hyper_t bufOffset;      /* only do it for things in this buffer page */
1658
1659     /* info for the actual call */
1660     int counter;                        /* next free slot */
1661     AFSFid fids[CM_BULKMAX];
1662     AFSFetchStatus stats[CM_BULKMAX];
1663     AFSCallBack callbacks[CM_BULKMAX];
1664 } cm_bulkStat_t;
1665
1666 /* for a given entry, make sure that it isn't in the stat cache, and then
1667  * add it to the list of file IDs to be obtained.
1668  *
1669  * Don't bother adding it if we already have a vnode.  Note that the dir
1670  * is locked, so we have to be careful checking the vnode we're thinking of
1671  * processing, to avoid deadlocks.
1672  */
1673 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1674                      osi_hyper_t *offp)
1675 {
1676     osi_hyper_t thyper;
1677     cm_bulkStat_t *bsp;
1678     int i;
1679     cm_scache_t *tscp;
1680     cm_fid_t tfid;
1681
1682     bsp = rockp;
1683
1684     /* Don't overflow bsp. */
1685     if (bsp->counter >= CM_BULKMAX)
1686         return CM_ERROR_STOPNOW;
1687
1688     thyper.LowPart = cm_data.buf_blockSize;
1689     thyper.HighPart = 0;
1690     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1691
1692     /* thyper is now the first byte past the end of the record we're
1693      * interested in, and bsp->bufOffset is the first byte of the record
1694      * we're interested in.
1695      * Skip data in the others.
1696      * Skip '.' and '..'
1697      */
1698     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1699         return 0;
1700     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1701         return CM_ERROR_STOPNOW;
1702     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1703         return 0;
1704
1705     tfid.cell = scp->fid.cell;
1706     tfid.volume = scp->fid.volume;
1707     tfid.vnode = ntohl(dep->fid.vnode);
1708     tfid.unique = ntohl(dep->fid.unique);
1709     tscp = cm_FindSCache(&tfid);
1710     if (tscp) {
1711         if (lock_TryMutex(&tscp->mx)) {
1712             /* we have an entry that we can look at */
1713             if (cm_HaveCallback(tscp)) {
1714                 /* we have a callback on it.  Don't bother
1715                  * fetching this stat entry, since we're happy
1716                  * with the info we have.
1717                  */
1718                 lock_ReleaseMutex(&tscp->mx);
1719                 cm_ReleaseSCache(tscp);
1720                 return 0;
1721             }
1722             lock_ReleaseMutex(&tscp->mx);
1723         }       /* got lock */
1724         cm_ReleaseSCache(tscp);
1725     }   /* found entry */
1726
1727 #ifdef AFS_FREELANCE_CLIENT
1728     // yj: if this is a mountpoint under root.afs then we don't want it
1729     // to be bulkstat-ed, instead, we call getSCache directly and under
1730     // getSCache, it is handled specially.
1731     if  ( cm_freelanceEnabled &&
1732           tfid.cell==AFS_FAKE_ROOT_CELL_ID && 
1733           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
1734           !(tfid.vnode==0x1 && tfid.unique==0x1) )
1735     {       
1736         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
1737         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1738     }
1739 #endif /* AFS_FREELANCE_CLIENT */
1740
1741     i = bsp->counter++;
1742     bsp->fids[i].Volume = scp->fid.volume;
1743     bsp->fids[i].Vnode = tfid.vnode;
1744     bsp->fids[i].Unique = tfid.unique;
1745     return 0;
1746 }       
1747
1748 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1749  * calls on all undeleted files in the page of the directory specified.
1750  */
1751 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1752                      cm_req_t *reqp)
1753 {
1754     long code;
1755     cm_bulkStat_t bb;   /* this is *BIG*, probably 12K or so;
1756                          * watch for stack problems */
1757     AFSCBFids fidStruct;
1758     AFSBulkStats statStruct;
1759     cm_conn_t *connp;
1760     AFSCBs callbackStruct;
1761     long filex;
1762     AFSVolSync volSync;
1763     cm_callbackRequest_t cbReq;
1764     long filesThisCall;
1765     long i;
1766     long j;
1767     cm_scache_t *scp;
1768     cm_fid_t tfid;
1769     struct rx_connection * callp;
1770
1771     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1772
1773     /* should be on a buffer boundary */
1774     osi_assert((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0);
1775
1776     bb.counter = 0;
1777     bb.bufOffset = *offsetp;
1778
1779     lock_ReleaseMutex(&dscp->mx);
1780     /* first, assemble the file IDs we need to stat */
1781     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp, reqp, NULL);
1782     lock_ObtainMutex(&dscp->mx);
1783
1784     /* if we failed, bail out early */
1785     if (code && code != CM_ERROR_STOPNOW) 
1786         return;
1787
1788     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1789      * make the calls to create the entries.  Handle AFSCBMAX files at a
1790      * time.
1791      */
1792     filex = 0;
1793     while (filex < bb.counter) {
1794         filesThisCall = bb.counter - filex;
1795         if (filesThisCall > AFSCBMAX) 
1796             filesThisCall = AFSCBMAX;
1797
1798         fidStruct.AFSCBFids_len = filesThisCall;
1799         fidStruct.AFSCBFids_val = &bb.fids[filex];
1800         statStruct.AFSBulkStats_len = filesThisCall;
1801         statStruct.AFSBulkStats_val = &bb.stats[filex];
1802         callbackStruct.AFSCBs_len = filesThisCall;
1803         callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1804         cm_StartCallbackGrantingCall(NULL, &cbReq);
1805         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1806         do {
1807             code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1808             if (code) 
1809                 continue;
1810
1811             callp = cm_GetRxConn(connp);
1812             code = RXAFS_BulkStatus(callp, &fidStruct,
1813                                      &statStruct, &callbackStruct, &volSync);
1814             rx_PutConnection(callp);
1815
1816         } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1817                              &volSync, NULL, &cbReq, code));
1818         code = cm_MapRPCError(code, reqp);
1819
1820         if (code)
1821             osi_Log1(afsd_logp, "CALL BulkStatus FAILURE code 0x%x", code);
1822         else
1823             osi_Log0(afsd_logp, "CALL BulkStatus SUCCESS");
1824
1825         /* may as well quit on an error, since we're not going to do
1826          * much better on the next immediate call, either.
1827          */
1828         if (code) {
1829             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1830             break;
1831         }
1832
1833         /* otherwise, we should do the merges */
1834         for (i = 0; i<filesThisCall; i++) {
1835             j = filex + i;
1836             tfid.cell = dscp->fid.cell;
1837             tfid.volume = bb.fids[j].Volume;
1838             tfid.vnode = bb.fids[j].Vnode;
1839             tfid.unique = bb.fids[j].Unique;
1840             code = cm_GetSCache(&tfid, &scp, userp, reqp);
1841             if (code != 0) 
1842                 continue;
1843
1844             /* otherwise, if this entry has no callback info, 
1845              * merge in this.
1846              */
1847             lock_ObtainMutex(&scp->mx);
1848             /* now, we have to be extra paranoid on merging in this
1849              * information, since we didn't use cm_SyncOp before
1850              * starting the fetch to make sure that no bad races
1851              * were occurring.  Specifically, we need to make sure
1852              * we don't obliterate any newer information in the
1853              * vnode than have here.
1854              *
1855              * Right now, be pretty conservative: if there's a
1856              * callback or a pending call, skip it.
1857              */
1858             if (scp->cbServerp == NULL
1859                  && !(scp->flags &
1860                        (CM_SCACHEFLAG_FETCHING
1861                          | CM_SCACHEFLAG_STORING
1862                          | CM_SCACHEFLAG_SIZESTORING))) {
1863                 cm_EndCallbackGrantingCall(scp, &cbReq,
1864                                             &bb.callbacks[j],
1865                                             CM_CALLBACK_MAINTAINCOUNT);
1866                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1867                                 userp, 0);
1868             }       
1869             lock_ReleaseMutex(&scp->mx);
1870             cm_ReleaseSCache(scp);
1871         } /* all files in the response */
1872         /* now tell it to drop the count,
1873          * after doing the vnode processing above */
1874         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
1875
1876         filex += filesThisCall;
1877     }   /* while there are still more files to process */
1878     osi_Log0(afsd_logp, "END cm_TryBulkStat");
1879 }       
1880
1881 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1882 {
1883     long mask;
1884
1885     /* initialize store back mask as inexpensive local variable */
1886     mask = 0;
1887     memset(statusp, 0, sizeof(AFSStoreStatus));
1888
1889     /* copy out queued info from scache first, if scp passed in */
1890     if (scp) {
1891         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1892             statusp->ClientModTime = scp->clientModTime;
1893             mask |= AFS_SETMODTIME;
1894             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1895         }
1896     }
1897
1898     if (attrp) {
1899         /* now add in our locally generated request */
1900         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1901             statusp->ClientModTime = attrp->clientModTime;
1902             mask |= AFS_SETMODTIME;
1903         }
1904         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1905             statusp->UnixModeBits = attrp->unixModeBits;
1906             mask |= AFS_SETMODE;
1907         }
1908         if (attrp->mask & CM_ATTRMASK_OWNER) {
1909             statusp->Owner = attrp->owner;
1910             mask |= AFS_SETOWNER;
1911         }
1912         if (attrp->mask & CM_ATTRMASK_GROUP) {
1913             statusp->Group = attrp->group;
1914             mask |= AFS_SETGROUP;
1915         }
1916     }
1917     statusp->Mask = mask;
1918 }       
1919
1920 /* set the file size, and make sure that all relevant buffers have been
1921  * truncated.  Ensure that any partially truncated buffers have been zeroed
1922  * to the end of the buffer.
1923  */
1924 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1925                    cm_req_t *reqp)
1926 {
1927     long code;
1928     int shrinking;
1929
1930     /* start by locking out buffer creation */
1931     lock_ObtainWrite(&scp->bufCreateLock);
1932
1933     /* verify that this is a file, not a dir or a symlink */
1934     lock_ObtainMutex(&scp->mx);
1935     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1936                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1937     if (code) 
1938         goto done;
1939         
1940     if (scp->fileType != CM_SCACHETYPE_FILE) {
1941         code = CM_ERROR_ISDIR;
1942         goto done;
1943     }
1944
1945   startover:
1946     if (LargeIntegerLessThan(*sizep, scp->length))
1947         shrinking = 1;
1948     else
1949         shrinking = 0;
1950
1951     lock_ReleaseMutex(&scp->mx);
1952
1953     /* can't hold scp->mx lock here, since we may wait for a storeback to
1954      * finish if the buffer package is cleaning a buffer by storing it to
1955      * the server.
1956      */
1957     if (shrinking)
1958         buf_Truncate(scp, userp, reqp, sizep);
1959
1960     /* now ensure that file length is short enough, and update truncPos */
1961     lock_ObtainMutex(&scp->mx);
1962
1963     /* make sure we have a callback (so we have the right value for the
1964      * length), and wait for it to be safe to do a truncate.
1965      */
1966     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1967                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1968                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1969     if (code) 
1970         goto done;
1971
1972     if (LargeIntegerLessThan(*sizep, scp->length)) {
1973         /* a real truncation.  If truncPos is not set yet, or is bigger
1974          * than where we're truncating the file, set truncPos to this
1975          * new value.
1976          */
1977         if (!shrinking)
1978             goto startover;
1979         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1980              || LargeIntegerLessThan(*sizep, scp->length)) {
1981             /* set trunc pos */
1982             scp->truncPos = *sizep;
1983             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1984         }
1985         /* in either case, the new file size has been changed */
1986         scp->length = *sizep;
1987         scp->mask |= CM_SCACHEMASK_LENGTH;
1988     }
1989     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1990         /* really extending the file */
1991         scp->length = *sizep;
1992         scp->mask |= CM_SCACHEMASK_LENGTH;
1993     }
1994
1995     /* done successfully */
1996     code = 0;
1997
1998   done:
1999     lock_ReleaseMutex(&scp->mx);
2000     lock_ReleaseWrite(&scp->bufCreateLock);
2001
2002     return code;
2003 }
2004
2005 /* set the file size or other attributes (but not both at once) */
2006 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2007                 cm_req_t *reqp)
2008 {
2009     long code;
2010     int flags;
2011     AFSFetchStatus afsOutStatus;
2012     AFSVolSync volSync;
2013     cm_conn_t *connp;
2014     AFSFid tfid;
2015     AFSStoreStatus afsInStatus;
2016     struct rx_connection * callp;
2017
2018     /* handle file length setting */
2019     if (attrp->mask & CM_ATTRMASK_LENGTH)
2020         return cm_SetLength(scp, &attrp->length, userp, reqp);
2021
2022     flags = CM_SCACHESYNC_STORESTATUS;
2023
2024     lock_ObtainMutex(&scp->mx);
2025     /* otherwise, we have to make an RPC to get the status */
2026     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2027
2028     /* make the attr structure */
2029     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2030
2031     lock_ReleaseMutex(&scp->mx);
2032     if (code) 
2033         return code;
2034
2035     /* now make the RPC */
2036     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%x", (long) scp);
2037     tfid.Volume = scp->fid.volume;
2038     tfid.Vnode = scp->fid.vnode;
2039     tfid.Unique = scp->fid.unique;
2040     do {
2041         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2042         if (code) 
2043             continue;
2044
2045         callp = cm_GetRxConn(connp);
2046         code = RXAFS_StoreStatus(callp, &tfid,
2047                                   &afsInStatus, &afsOutStatus, &volSync);
2048         rx_PutConnection(callp);
2049
2050     } while (cm_Analyze(connp, userp, reqp,
2051                          &scp->fid, &volSync, NULL, NULL, code));
2052     code = cm_MapRPCError(code, reqp);
2053
2054     if (code)
2055         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2056     else
2057         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2058
2059     lock_ObtainMutex(&scp->mx);
2060     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2061     if (code == 0)
2062         cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
2063                         CM_MERGEFLAG_FORCE);
2064         
2065     /* if we're changing the mode bits, discard the ACL cache, 
2066      * since we changed the mode bits.
2067      */
2068     if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
2069     lock_ReleaseMutex(&scp->mx);
2070     return code;
2071 }       
2072
2073 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2074                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2075 {       
2076     cm_conn_t *connp;
2077     long code;
2078     AFSFid dirAFSFid;
2079     cm_callbackRequest_t cbReq;
2080     AFSFid newAFSFid;
2081     cm_fid_t newFid;
2082     cm_scache_t *scp;
2083     int didEnd;
2084     AFSStoreStatus inStatus;
2085     AFSFetchStatus updatedDirStatus;
2086     AFSFetchStatus newFileStatus;
2087     AFSCallBack newFileCallback;
2088     AFSVolSync volSync;
2089     struct rx_connection * callp;
2090
2091     /* can't create names with @sys in them; must expand it manually first.
2092      * return "invalid request" if they try.
2093      */
2094     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2095         return CM_ERROR_ATSYS;
2096     }
2097
2098     /* before starting the RPC, mark that we're changing the file data, so
2099      * that someone who does a chmod will know to wait until our call
2100      * completes.
2101      */
2102     lock_ObtainMutex(&dscp->mx);
2103     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2104     if (code == 0) {
2105         cm_StartCallbackGrantingCall(NULL, &cbReq);
2106     }
2107     lock_ReleaseMutex(&dscp->mx);
2108     if (code) {
2109         return code;
2110     }
2111     didEnd = 0;
2112
2113     cm_StatusFromAttr(&inStatus, NULL, attrp);
2114
2115     /* try the RPC now */
2116     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%x", (long) dscp);
2117     do {
2118         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2119         if (code) 
2120             continue;
2121
2122         dirAFSFid.Volume = dscp->fid.volume;
2123         dirAFSFid.Vnode = dscp->fid.vnode;
2124         dirAFSFid.Unique = dscp->fid.unique;
2125
2126         callp = cm_GetRxConn(connp);
2127         code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
2128                                  &inStatus, &newAFSFid, &newFileStatus,
2129                                  &updatedDirStatus, &newFileCallback,
2130                                  &volSync);
2131         rx_PutConnection(callp);
2132
2133     } while (cm_Analyze(connp, userp, reqp,
2134                          &dscp->fid, &volSync, NULL, &cbReq, code));
2135     code = cm_MapRPCError(code, reqp);
2136         
2137     if (code)
2138         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2139     else
2140         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2141
2142     lock_ObtainMutex(&dscp->mx);
2143     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2144     if (code == 0) {
2145         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2146     }
2147     lock_ReleaseMutex(&dscp->mx);
2148
2149     /* now try to create the file's entry, too, but be careful to 
2150      * make sure that we don't merge in old info.  Since we weren't locking
2151      * out any requests during the file's creation, we may have pretty old
2152      * info.
2153      */
2154     if (code == 0) {
2155         newFid.cell = dscp->fid.cell;
2156         newFid.volume = dscp->fid.volume;
2157         newFid.vnode = newAFSFid.Vnode;
2158         newFid.unique = newAFSFid.Unique;
2159         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2160         if (code == 0) {
2161             lock_ObtainMutex(&scp->mx);
2162             if (!cm_HaveCallback(scp)) {
2163                 cm_MergeStatus(scp, &newFileStatus, &volSync,
2164                                 userp, 0);
2165                 cm_EndCallbackGrantingCall(scp, &cbReq,
2166                                             &newFileCallback, 0);
2167                 didEnd = 1;     
2168             }       
2169             lock_ReleaseMutex(&scp->mx);
2170             *scpp = scp;
2171         }
2172     }
2173
2174     /* make sure we end things properly */
2175     if (!didEnd)
2176         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2177
2178     return code;
2179 }       
2180
2181 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
2182 {
2183     long code;
2184
2185     lock_ObtainWrite(&scp->bufCreateLock);
2186     code = buf_CleanVnode(scp, userp, reqp);
2187     lock_ReleaseWrite(&scp->bufCreateLock);
2188     if (code == 0) {
2189         lock_ObtainMutex(&scp->mx);
2190         scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
2191                          | CM_SCACHEFLAG_OUTOFSPACE);
2192         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2193                           | CM_SCACHEMASK_CLIENTMODTIME
2194                           | CM_SCACHEMASK_LENGTH))
2195             code = cm_StoreMini(scp, userp, reqp);
2196         lock_ReleaseMutex(&scp->mx);
2197     }
2198     return code;
2199 }
2200
2201 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
2202                  cm_user_t *userp, cm_req_t *reqp)
2203 {
2204     cm_conn_t *connp;
2205     long code;
2206     AFSFid dirAFSFid;
2207     cm_callbackRequest_t cbReq;
2208     AFSFid newAFSFid;
2209     cm_fid_t newFid;
2210     cm_scache_t *scp;
2211     int didEnd;
2212     AFSStoreStatus inStatus;
2213     AFSFetchStatus updatedDirStatus;
2214     AFSFetchStatus newDirStatus;
2215     AFSCallBack newDirCallback;
2216     AFSVolSync volSync;
2217     struct rx_connection * callp;
2218
2219     /* can't create names with @sys in them; must expand it manually first.
2220      * return "invalid request" if they try.
2221      */
2222     if (cm_ExpandSysName(namep, NULL, 0, 0)) {
2223         return CM_ERROR_ATSYS;
2224     }
2225
2226     /* before starting the RPC, mark that we're changing the directory
2227      * data, so that someone who does a chmod on the dir will wait until
2228      * our call completes.
2229      */
2230     lock_ObtainMutex(&dscp->mx);
2231     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2232     if (code == 0) {
2233         cm_StartCallbackGrantingCall(NULL, &cbReq);
2234     }
2235     lock_ReleaseMutex(&dscp->mx);
2236     if (code) {
2237         return code;
2238     }
2239     didEnd = 0;
2240
2241     cm_StatusFromAttr(&inStatus, NULL, attrp);
2242
2243     /* try the RPC now */
2244     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%x", (long) dscp);
2245     do {
2246         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2247         if (code) 
2248             continue;
2249
2250         dirAFSFid.Volume = dscp->fid.volume;
2251         dirAFSFid.Vnode = dscp->fid.vnode;
2252         dirAFSFid.Unique = dscp->fid.unique;
2253
2254         callp = cm_GetRxConn(connp);
2255         code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2256                               &inStatus, &newAFSFid, &newDirStatus,
2257                               &updatedDirStatus, &newDirCallback,
2258                               &volSync);
2259         rx_PutConnection(callp);
2260
2261     } while (cm_Analyze(connp, userp, reqp,
2262                          &dscp->fid, &volSync, NULL, &cbReq, code));
2263     code = cm_MapRPCError(code, reqp);
2264         
2265     if (code)
2266         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
2267     else
2268         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
2269
2270     lock_ObtainMutex(&dscp->mx);
2271     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2272     if (code == 0) {
2273         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2274     }
2275     lock_ReleaseMutex(&dscp->mx);
2276
2277     /* now try to create the new dir's entry, too, but be careful to 
2278      * make sure that we don't merge in old info.  Since we weren't locking
2279      * out any requests during the file's creation, we may have pretty old
2280      * info.
2281      */
2282     if (code == 0) {
2283         newFid.cell = dscp->fid.cell;
2284         newFid.volume = dscp->fid.volume;
2285         newFid.vnode = newAFSFid.Vnode;
2286         newFid.unique = newAFSFid.Unique;
2287         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2288         if (code == 0) {
2289             lock_ObtainMutex(&scp->mx);
2290             if (!cm_HaveCallback(scp)) {
2291                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2292                                 userp, 0);
2293                 cm_EndCallbackGrantingCall(scp, &cbReq,
2294                                             &newDirCallback, 0);
2295                 didEnd = 1;             
2296             }
2297             lock_ReleaseMutex(&scp->mx);
2298             cm_ReleaseSCache(scp);
2299         }
2300     }
2301
2302     /* make sure we end things properly */
2303     if (!didEnd)
2304         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, 0);
2305
2306     /* and return error code */
2307     return code;
2308 }       
2309
2310 long cm_Link(cm_scache_t *dscp, char *namep, cm_scache_t *sscp, long flags,
2311              cm_user_t *userp, cm_req_t *reqp)
2312 {
2313     cm_conn_t *connp;
2314     long code = 0;
2315     AFSFid dirAFSFid;
2316     AFSFid existingAFSFid;
2317     AFSFetchStatus updatedDirStatus;
2318     AFSFetchStatus newLinkStatus;
2319     AFSVolSync volSync;
2320     struct rx_connection * callp;
2321
2322     if (dscp->fid.cell != sscp->fid.cell ||
2323         dscp->fid.volume != sscp->fid.volume) {
2324         return CM_ERROR_CROSSDEVLINK;
2325     }
2326
2327     lock_ObtainMutex(&dscp->mx);
2328     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2329     lock_ReleaseMutex(&dscp->mx);
2330
2331     if (code)
2332         return code;
2333
2334     /* try the RPC now */
2335     osi_Log1(afsd_logp, "CALL Link scp 0x%x", (long) dscp);
2336     do {
2337         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2338         if (code) continue;
2339
2340         dirAFSFid.Volume = dscp->fid.volume;
2341         dirAFSFid.Vnode = dscp->fid.vnode;
2342         dirAFSFid.Unique = dscp->fid.unique;
2343
2344         existingAFSFid.Volume = sscp->fid.volume;
2345         existingAFSFid.Vnode = sscp->fid.vnode;
2346         existingAFSFid.Unique = sscp->fid.unique;
2347
2348         callp = cm_GetRxConn(connp);
2349         code = RXAFS_Link(callp, &dirAFSFid, namep, &existingAFSFid,
2350             &newLinkStatus, &updatedDirStatus, &volSync);
2351         rx_PutConnection(callp);
2352         osi_Log1(smb_logp,"  RXAFS_Link returns 0x%x", code);
2353
2354     } while (cm_Analyze(connp, userp, reqp,
2355         &dscp->fid, &volSync, NULL, NULL, code));
2356
2357     code = cm_MapRPCError(code, reqp);
2358
2359     if (code)
2360         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
2361     else
2362         osi_Log0(afsd_logp, "CALL Link SUCCESS");
2363
2364     lock_ObtainMutex(&dscp->mx);
2365     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2366     if (code == 0) {
2367         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2368     }
2369     lock_ReleaseMutex(&dscp->mx);
2370
2371     return code;
2372 }
2373
2374 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2375                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2376 {
2377     cm_conn_t *connp;
2378     long code;
2379     AFSFid dirAFSFid;
2380     AFSFid newAFSFid;
2381     cm_fid_t newFid;
2382     cm_scache_t *scp;
2383     AFSStoreStatus inStatus;
2384     AFSFetchStatus updatedDirStatus;
2385     AFSFetchStatus newLinkStatus;
2386     AFSVolSync volSync;
2387     struct rx_connection * callp;
2388
2389     /* before starting the RPC, mark that we're changing the directory data,
2390      * so that someone who does a chmod on the dir will wait until our
2391      * call completes.
2392      */
2393     lock_ObtainMutex(&dscp->mx);
2394     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2395     lock_ReleaseMutex(&dscp->mx);
2396     if (code) {
2397         return code;
2398     }
2399
2400     cm_StatusFromAttr(&inStatus, NULL, attrp);
2401
2402     /* try the RPC now */
2403     osi_Log1(afsd_logp, "CALL Symlink scp 0x%x", (long) dscp);
2404     do {
2405         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2406         if (code) 
2407             continue;
2408
2409         dirAFSFid.Volume = dscp->fid.volume;
2410         dirAFSFid.Vnode = dscp->fid.vnode;
2411         dirAFSFid.Unique = dscp->fid.unique;
2412
2413         callp = cm_GetRxConn(connp);
2414         code = RXAFS_Symlink(callp, &dirAFSFid, namep, contentsp,
2415                               &inStatus, &newAFSFid, &newLinkStatus,
2416                               &updatedDirStatus, &volSync);
2417         rx_PutConnection(callp);
2418
2419     } while (cm_Analyze(connp, userp, reqp,
2420                          &dscp->fid, &volSync, NULL, NULL, code));
2421     code = cm_MapRPCError(code, reqp);
2422         
2423     if (code)
2424         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
2425     else
2426         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
2427
2428     lock_ObtainMutex(&dscp->mx);
2429     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2430     if (code == 0) {
2431         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2432     }
2433     lock_ReleaseMutex(&dscp->mx);
2434
2435     /* now try to create the new dir's entry, too, but be careful to 
2436      * make sure that we don't merge in old info.  Since we weren't locking
2437      * out any requests during the file's creation, we may have pretty old
2438      * info.
2439      */
2440     if (code == 0) {
2441         newFid.cell = dscp->fid.cell;
2442         newFid.volume = dscp->fid.volume;
2443         newFid.vnode = newAFSFid.Vnode;
2444         newFid.unique = newAFSFid.Unique;
2445         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2446         if (code == 0) {
2447             lock_ObtainMutex(&scp->mx);
2448             if (!cm_HaveCallback(scp)) {
2449                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2450                                 userp, 0);
2451             }       
2452             lock_ReleaseMutex(&scp->mx);
2453             cm_ReleaseSCache(scp);
2454         }
2455     }
2456         
2457     /* and return error code */
2458     return code;
2459 }
2460
2461 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2462                    cm_req_t *reqp)
2463 {
2464     cm_conn_t *connp;
2465     long code;
2466     AFSFid dirAFSFid;
2467     int didEnd;
2468     AFSFetchStatus updatedDirStatus;
2469     AFSVolSync volSync;
2470     struct rx_connection * callp;
2471
2472     /* before starting the RPC, mark that we're changing the directory data,
2473      * so that someone who does a chmod on the dir will wait until our
2474      * call completes.
2475      */
2476     lock_ObtainMutex(&dscp->mx);
2477     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2478     lock_ReleaseMutex(&dscp->mx);
2479     if (code) {
2480         return code;
2481     }
2482     didEnd = 0;
2483
2484     /* try the RPC now */
2485     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%x", (long) dscp);
2486     do {
2487         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2488         if (code) 
2489             continue;
2490
2491         dirAFSFid.Volume = dscp->fid.volume;
2492         dirAFSFid.Vnode = dscp->fid.vnode;
2493         dirAFSFid.Unique = dscp->fid.unique;
2494
2495         callp = cm_GetRxConn(connp);
2496         code = RXAFS_RemoveDir(callp, &dirAFSFid, namep,
2497                                 &updatedDirStatus, &volSync);
2498         rx_PutConnection(callp);
2499
2500     } while (cm_Analyze(connp, userp, reqp,
2501                          &dscp->fid, &volSync, NULL, NULL, code));
2502     code = cm_MapRPCErrorRmdir(code, reqp);
2503
2504     if (code)
2505         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
2506     else
2507         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
2508
2509     lock_ObtainMutex(&dscp->mx);
2510     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2511     if (code == 0) {
2512         cm_dnlcRemove(dscp, namep); 
2513         cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2514     }
2515     lock_ReleaseMutex(&dscp->mx);
2516
2517     /* and return error code */
2518     return code;
2519 }
2520
2521 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2522 {
2523     /* grab mutex on contents */
2524     lock_ObtainMutex(&scp->mx);
2525
2526     /* reset the prefetch info */
2527     scp->prefetch.base.LowPart = 0;             /* base */
2528     scp->prefetch.base.HighPart = 0;
2529     scp->prefetch.end.LowPart = 0;              /* and end */
2530     scp->prefetch.end.HighPart = 0;
2531
2532     /* release mutex on contents */
2533     lock_ReleaseMutex(&scp->mx);
2534
2535     /* we're done */
2536     return 0;
2537 }       
2538
2539 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2540                 char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2541 {
2542     cm_conn_t *connp;
2543     long code;
2544     AFSFid oldDirAFSFid;
2545     AFSFid newDirAFSFid;
2546     int didEnd;
2547     AFSFetchStatus updatedOldDirStatus;
2548     AFSFetchStatus updatedNewDirStatus;
2549     AFSVolSync volSync;
2550     int oneDir;
2551     struct rx_connection * callp;
2552
2553     /* before starting the RPC, mark that we're changing the directory data,
2554      * so that someone who does a chmod on the dir will wait until our call
2555      * completes.  We do this in vnode order so that we don't deadlock,
2556      * which makes the code a little verbose.
2557      */
2558     if (oldDscp == newDscp) {
2559         /* check for identical names */
2560         if (strcmp(oldNamep, newNamep) == 0)
2561             return CM_ERROR_RENAME_IDENTICAL;
2562
2563         oneDir = 1;
2564         lock_ObtainMutex(&oldDscp->mx);
2565         cm_dnlcRemove(oldDscp, oldNamep);
2566         cm_dnlcRemove(oldDscp, newNamep);
2567         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2568                           CM_SCACHESYNC_STOREDATA);
2569         lock_ReleaseMutex(&oldDscp->mx);
2570     }
2571     else {
2572         /* two distinct dir vnodes */
2573         oneDir = 0;
2574         if (oldDscp->fid.cell != newDscp->fid.cell ||
2575              oldDscp->fid.volume != newDscp->fid.volume)
2576             return CM_ERROR_CROSSDEVLINK;
2577
2578         /* shouldn't happen that we have distinct vnodes for two
2579          * different files, but could due to deliberate attack, or
2580          * stale info.  Avoid deadlocks and quit now.
2581          */
2582         if (oldDscp->fid.vnode == newDscp->fid.vnode)
2583             return CM_ERROR_CROSSDEVLINK;
2584
2585         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2586             lock_ObtainMutex(&oldDscp->mx);
2587             cm_dnlcRemove(oldDscp, oldNamep);
2588             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2589                               CM_SCACHESYNC_STOREDATA);
2590             lock_ReleaseMutex(&oldDscp->mx);
2591             if (code == 0) {
2592                 lock_ObtainMutex(&newDscp->mx);
2593                 cm_dnlcRemove(newDscp, newNamep);
2594                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2595                                   CM_SCACHESYNC_STOREDATA);
2596                 lock_ReleaseMutex(&newDscp->mx);
2597                 if (code) {
2598                     /* cleanup first one */
2599                     cm_SyncOpDone(oldDscp, NULL,
2600                                    CM_SCACHESYNC_STOREDATA);
2601                 }       
2602             }
2603         }
2604         else {
2605             /* lock the new vnode entry first */
2606             lock_ObtainMutex(&newDscp->mx);
2607             cm_dnlcRemove(newDscp, newNamep);
2608             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2609                               CM_SCACHESYNC_STOREDATA);
2610             lock_ReleaseMutex(&newDscp->mx);
2611             if (code == 0) {
2612                 lock_ObtainMutex(&oldDscp->mx);
2613                 cm_dnlcRemove(oldDscp, oldNamep);
2614                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2615                                   CM_SCACHESYNC_STOREDATA);
2616                 lock_ReleaseMutex(&oldDscp->mx);
2617                 if (code) {
2618                     /* cleanup first one */
2619                     cm_SyncOpDone(newDscp, NULL,
2620                                    CM_SCACHESYNC_STOREDATA);
2621                 }       
2622             }
2623         }
2624     }   /* two distinct vnodes */
2625
2626     if (code) {
2627         return code;
2628     }
2629     didEnd = 0;
2630
2631     /* try the RPC now */
2632     osi_Log2(afsd_logp, "CALL Rename old scp 0x%x new scp 0x%x", 
2633               (long) oldDscp, (long) newDscp);
2634     do {
2635         code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2636         if (code) 
2637             continue;
2638
2639         oldDirAFSFid.Volume = oldDscp->fid.volume;
2640         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2641         oldDirAFSFid.Unique = oldDscp->fid.unique;
2642         newDirAFSFid.Volume = newDscp->fid.volume;
2643         newDirAFSFid.Vnode = newDscp->fid.vnode;
2644         newDirAFSFid.Unique = newDscp->fid.unique;
2645
2646         callp = cm_GetRxConn(connp);
2647         code = RXAFS_Rename(callp, &oldDirAFSFid, oldNamep,
2648                              &newDirAFSFid, newNamep,
2649                              &updatedOldDirStatus, &updatedNewDirStatus,
2650                              &volSync);
2651         rx_PutConnection(callp);
2652
2653     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2654                          &volSync, NULL, NULL, code));
2655     code = cm_MapRPCError(code, reqp);
2656         
2657     if (code)
2658         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
2659     else
2660         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
2661
2662     /* update the individual stat cache entries for the directories */
2663     lock_ObtainMutex(&oldDscp->mx);
2664     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2665     if (code == 0) {
2666         cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2667                         userp, 0);
2668     }
2669     lock_ReleaseMutex(&oldDscp->mx);
2670
2671     /* and update it for the new one, too, if necessary */
2672     if (!oneDir) {
2673         lock_ObtainMutex(&newDscp->mx);
2674         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2675         if (code == 0) {
2676             cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2677                             userp, 0);
2678         }
2679         lock_ReleaseMutex(&newDscp->mx);
2680     }
2681
2682     /* and return error code */
2683     return code;
2684 }
2685
2686 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2687               LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2688               u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2689               void **lockpp)
2690 {
2691     long code;
2692     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2693     AFSFid tfid;
2694     AFSVolSync volSync;
2695     cm_conn_t *connp;
2696     cm_file_lock_t *fileLock;
2697     osi_queue_t *q;
2698     int found = 0;
2699     struct rx_connection * callp;
2700
2701     /* Look for a conflict.  Also, if we are asking for a shared lock,
2702      * look for another shared lock, so we don't have to do an RPC.
2703      */
2704     q = scp->fileLocks;
2705     while (q) {
2706         fileLock = (cm_file_lock_t *)
2707             ((char *) q - offsetof(cm_file_lock_t, fileq));
2708         if ((fileLock->flags &
2709               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2710              == 0) {
2711             if ((LockType & 0x1) == 0
2712                  || (fileLock->LockType & 0x1) == 0)
2713                 return CM_ERROR_WOULDBLOCK;
2714             found = 1;
2715         }
2716         q = osi_QNext(q);
2717     }
2718
2719     if (found)
2720         code = 0;
2721     else {
2722         tfid.Volume = scp->fid.volume;
2723         tfid.Vnode = scp->fid.vnode;
2724         tfid.Unique = scp->fid.unique;
2725         lock_ReleaseMutex(&scp->mx);
2726         do {
2727             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2728             if (code) 
2729                 break;
2730
2731             callp = cm_GetRxConn(connp);
2732             code = RXAFS_SetLock(callp, &tfid, Which,
2733                                   &volSync);
2734             rx_PutConnection(callp);
2735
2736         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2737                              NULL, NULL, code));
2738         lock_ObtainMutex(&scp->mx);
2739         code = cm_MapRPCError(code, reqp);
2740     }
2741
2742     if (code == 0 || Timeout != 0) {
2743         fileLock = malloc(sizeof(cm_file_lock_t));
2744         fileLock->LockType = LockType;
2745         cm_HoldUser(userp);
2746         fileLock->userp = userp;
2747         fileLock->fid = scp->fid;
2748         fileLock->LOffset = LOffset;
2749         fileLock->LLength = LLength;
2750         fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2751         osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2752         lock_ObtainWrite(&cm_scacheLock);
2753         osi_QAdd(&cm_allFileLocks, &fileLock->q);
2754         lock_ReleaseWrite(&cm_scacheLock);
2755         if (code != 0) 
2756             *lockpp = fileLock;
2757     }
2758     return code;
2759 }
2760
2761 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2762                 LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2763                 cm_user_t *userp, cm_req_t *reqp)
2764 {
2765     long code = 0;
2766     int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2767     AFSFid tfid;
2768     AFSVolSync volSync;
2769     cm_conn_t *connp;
2770     cm_file_lock_t *fileLock, *ourLock;
2771     osi_queue_t *q, *qq;
2772     int anotherReader = 0;
2773     int smallLock = 0;
2774     int found = 0;
2775     struct rx_connection * callp;
2776
2777     if (LargeIntegerLessThan(LLength, scp->length))
2778         smallLock = 1;
2779
2780     /* Look for our own lock on the list, so as to remove it.
2781      * Also, determine if we're the last reader; if not, avoid an RPC.
2782      */
2783     q = scp->fileLocks;
2784     while (q) {
2785         fileLock = (cm_file_lock_t *)
2786             ((char *) q - offsetof(cm_file_lock_t, fileq));
2787         if (!found
2788              && fileLock->userp == userp
2789              && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2790              && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2791             found = 1;
2792             ourLock = fileLock;
2793             qq = q;
2794         }
2795         else if (fileLock->LockType & 0x1)
2796             anotherReader = 1;
2797         q = osi_QNext(q);
2798     }
2799
2800     /* ignore byte ranges */
2801     if (smallLock && !found)
2802         return 0;
2803
2804     /* don't try to unlock other people's locks */
2805     if (!found)
2806         return CM_ERROR_WOULDBLOCK;
2807
2808     /* discard lock record */
2809     osi_QRemove(&scp->fileLocks, qq);
2810     /*
2811      * Don't delete it here; let the daemon delete it, to simplify
2812      * the daemon's traversal of the list.
2813      */
2814     lock_ObtainWrite(&cm_scacheLock);
2815     ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2816     cm_ReleaseUser(ourLock->userp);
2817     lock_ReleaseWrite(&cm_scacheLock);
2818
2819     if (!anotherReader) {
2820         tfid.Volume = scp->fid.volume;
2821         tfid.Vnode = scp->fid.vnode;
2822         tfid.Unique = scp->fid.unique;
2823         lock_ReleaseMutex(&scp->mx);
2824         osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%x", (long) scp);
2825         do {
2826             code = cm_Conn(&scp->fid, userp, reqp, &connp);
2827             if (code) 
2828                 break;
2829
2830             callp = cm_GetRxConn(connp);
2831             code = RXAFS_ReleaseLock(callp, &tfid, &volSync);
2832             rx_PutConnection(callp);
2833
2834         } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2835                              NULL, NULL, code));
2836         code = cm_MapRPCError(code, reqp);
2837
2838         if (code)
2839             osi_Log1(afsd_logp, "CALL ReleaseLock FAILURE, code 0x%x", code);
2840         else
2841             osi_Log0(afsd_logp, "CALL ReleaseLock SUCCESS");
2842
2843         lock_ObtainMutex(&scp->mx);
2844     }
2845
2846     return code;
2847 }
2848
2849 void cm_CheckLocks()
2850 {
2851     osi_queue_t *q, *nq;
2852     cm_file_lock_t *fileLock;
2853     cm_req_t req;
2854     AFSFid tfid;
2855     AFSVolSync volSync;
2856     cm_conn_t *connp;
2857     long code;
2858     struct rx_connection * callp;
2859
2860     cm_InitReq(&req);
2861
2862     lock_ObtainWrite(&cm_scacheLock);
2863     q = cm_allFileLocks;
2864     while (q) {
2865         fileLock = (cm_file_lock_t *) q;
2866         nq = osi_QNext(q);
2867         if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2868             osi_QRemove(&cm_allFileLocks, q);
2869             free(fileLock);
2870         }
2871         else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2872             tfid.Volume = fileLock->fid.volume;
2873             tfid.Vnode = fileLock->fid.vnode;
2874             tfid.Unique = fileLock->fid.unique;
2875             lock_ReleaseWrite(&cm_scacheLock);
2876             osi_Log1(afsd_logp, "CALL ExtendLock lock 0x%x", (long) fileLock);
2877             do {
2878                 code = cm_Conn(&fileLock->fid, fileLock->userp,
2879                                 &req, &connp);
2880                 if (code) 
2881                     break;
2882
2883                 callp = cm_GetRxConn(connp);
2884                 code = RXAFS_ExtendLock(callp, &tfid,
2885                                          &volSync);
2886                 rx_PutConnection(callp);
2887
2888             } while (cm_Analyze(connp, fileLock->userp, &req,
2889                                  &fileLock->fid, &volSync, NULL, NULL,
2890                                  code));
2891             code = cm_MapRPCError(code, &req);
2892             if (code)
2893                 osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
2894             else
2895                 osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
2896
2897             lock_ObtainWrite(&cm_scacheLock);
2898         }
2899         q = nq;
2900     }
2901     lock_ReleaseWrite(&cm_scacheLock);
2902 }       
2903
2904 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2905 {
2906     long code;
2907     int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2908     cm_scache_t *scp;
2909     AFSFid tfid;
2910     AFSVolSync volSync;
2911     cm_conn_t *connp;
2912     cm_file_lock_t *fileLock;
2913     osi_queue_t *q;
2914     cm_req_t req;
2915     int found = 0;
2916     struct rx_connection * callp;
2917
2918     if (vcp_is_dead) {
2919         code = CM_ERROR_TIMEDOUT;
2920         goto handleCode;
2921     }
2922
2923     cm_InitReq(&req);
2924
2925     /* Look for a conflict.  Also, if we are asking for a shared lock,
2926      * look for another shared lock, so we don't have to do an RPC.
2927      */
2928     code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2929     if (code)
2930         return code;
2931
2932     q = scp->fileLocks;
2933     while (q) {
2934         fileLock = (cm_file_lock_t *)
2935             ((char *) q - offsetof(cm_file_lock_t, fileq));
2936         if ((fileLock->flags &
2937               (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2938              == 0) {
2939             if ((oldFileLock->LockType & 0x1) == 0
2940                  || (fileLock->LockType & 0x1) == 0) {
2941                 cm_ReleaseSCache(scp);
2942                 return CM_ERROR_WOULDBLOCK;
2943             }
2944             found = 1;
2945         }
2946         q = osi_QNext(q);
2947     }
2948
2949     if (found)
2950         code = 0;
2951     else {
2952         tfid.Volume = oldFileLock->fid.volume;
2953         tfid.Vnode = oldFileLock->fid.vnode;
2954         tfid.Unique = oldFileLock->fid.unique;
2955         osi_Log1(afsd_logp, "CALL SetLock lock 0x%x", (long) oldFileLock);
2956         do {
2957             code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2958                             &req, &connp);
2959             if (code) 
2960                 break;
2961
2962             callp = cm_GetRxConn(connp);
2963             code = RXAFS_SetLock(callp, &tfid, Which,
2964                                   &volSync);
2965             rx_PutConnection(callp);
2966
2967         } while (cm_Analyze(connp, oldFileLock->userp, &req,
2968                              &oldFileLock->fid, &volSync,
2969                              NULL, NULL, code));
2970         code = cm_MapRPCError(code, &req);
2971
2972         if (code)
2973             osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
2974         else
2975             osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
2976     }
2977
2978   handleCode:
2979     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2980         lock_ObtainMutex(&scp->mx);
2981         osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2982         lock_ReleaseMutex(&scp->mx);
2983     }
2984     lock_ObtainWrite(&cm_scacheLock);
2985     if (code == 0)
2986         oldFileLock->flags = 0;
2987     else if (code != CM_ERROR_WOULDBLOCK) {
2988         oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2989         cm_ReleaseUser(oldFileLock->userp);
2990         oldFileLock->userp = NULL;
2991     }
2992     lock_ReleaseWrite(&cm_scacheLock);
2993
2994     return code;
2995 }