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