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