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