case-sensitivity-20040506
[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             code = cm_FreelanceAddMount(namep, namep, "root.cell.",
1016                                         &rock.fid);
1017             if (code < 0) {   /* add mount point failed, so give up */
1018                 if (flags & CM_FLAG_CHECKPATH)
1019                     return CM_ERROR_NOSUCHPATH;
1020                 else
1021                     return CM_ERROR_NOSUCHFILE;
1022             }
1023             tscp = NULL;   /* to force call of cm_GetSCache */
1024         }
1025         }
1026
1027 haveFid:       
1028         if ( !tscp )    /* we did not find it in the dnlc */
1029         {
1030                 dnlcHit = 0;    
1031         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1032         if (code) 
1033             return code;
1034         }
1035     /* tscp is now held */
1036
1037         lock_ObtainMutex(&tscp->mx);
1038         code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1039                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1040     if (code) { 
1041                 lock_ReleaseMutex(&tscp->mx);
1042                 cm_ReleaseSCache(tscp);
1043         return code;
1044         }
1045     /* tscp is now locked */
1046
1047     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1048               && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1049                 /* mount points are funny: they have a volume name to mount
1050          * the root of.
1051          */
1052                 code = cm_ReadMountPoint(tscp, userp, reqp);
1053         if (code == 0)
1054                         code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1055                                         &mountedScp);
1056                 lock_ReleaseMutex(&tscp->mx);
1057                 cm_ReleaseSCache(tscp);
1058                 if (code) {
1059             return code;
1060         }
1061         tscp = mountedScp;
1062     }
1063         else {
1064                 lock_ReleaseMutex(&tscp->mx);
1065         }
1066
1067         /* copy back pointer */
1068     *outpScpp = tscp;
1069
1070         /* insert scache in dnlc */
1071         if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1072             /* lock the directory entry to prevent racing callback revokes */
1073             lock_ObtainMutex(&dscp->mx);
1074             if ( dscp->cbServerp && dscp->cbExpires )
1075             cm_dnlcEnter(dscp, namep, tscp);
1076             lock_ReleaseMutex(&dscp->mx);
1077         }
1078
1079         /* and return */
1080     return 0;
1081 }
1082
1083 long cm_Unlink(cm_scache_t *dscp, char *namep, cm_user_t *userp, cm_req_t *reqp)
1084 {
1085         long code;
1086         cm_conn_t *connp;
1087         AFSFid afsFid;
1088         int sflags;
1089         AFSFetchStatus newDirStatus;
1090         AFSVolSync volSync;
1091
1092 #ifdef AFS_FREELANCE_CLIENT
1093         if (cm_freelanceEnabled && dscp == cm_rootSCachep) {
1094           /* deleting a mount point from the root dir. */
1095           code = cm_FreelanceRemoveMount(namep);
1096           return code;
1097         }
1098 #endif
1099
1100         /* make sure we don't screw up the dir status during the merge */
1101         lock_ObtainMutex(&dscp->mx);
1102         sflags = CM_SCACHESYNC_STOREDATA;
1103         code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1104         lock_ReleaseMutex(&dscp->mx);
1105         if (code) return code;
1106
1107         /* make the RPC */
1108         afsFid.Volume = dscp->fid.volume;
1109         afsFid.Vnode = dscp->fid.vnode;
1110         afsFid.Unique = dscp->fid.unique;
1111         do {
1112                 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1113                 if (code) continue;
1114                 
1115                 code = RXAFS_RemoveFile(connp->callp, &afsFid, namep,
1116                                         &newDirStatus, &volSync);
1117                 
1118         } while (cm_Analyze(connp, userp, reqp,
1119                             &dscp->fid, &volSync, NULL, code));
1120         code = cm_MapRPCError(code, reqp);
1121
1122         lock_ObtainMutex(&dscp->mx);
1123         cm_dnlcRemove(dscp, namep);
1124         cm_SyncOpDone(dscp, NULL, sflags);
1125         if (code == 0) cm_MergeStatus(dscp, &newDirStatus, &volSync, userp, 0);
1126         lock_ReleaseMutex(&dscp->mx);
1127
1128         return code;
1129 }
1130
1131 /* called with a locked vnode, and fills in the link info.
1132  * returns this the vnode still locked.
1133  */
1134 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1135 {
1136         long code;
1137         cm_buf_t *bufp;
1138         long temp;
1139         osi_hyper_t thyper;
1140
1141         lock_AssertMutex(&linkScp->mx);
1142         if (!linkScp->mountPointStringp) {
1143                 /* read the link data */
1144                 lock_ReleaseMutex(&linkScp->mx);
1145                 thyper.LowPart = thyper.HighPart = 0;
1146                 code = buf_Get(linkScp, &thyper, &bufp);
1147                 lock_ObtainMutex(&linkScp->mx);
1148                 if (code) return code;
1149                 while (1) {
1150                         code = cm_SyncOp(linkScp, bufp, userp, reqp, 0,
1151                                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
1152                         if (code) {
1153                                 buf_Release(bufp);
1154                                 return code;
1155                         }
1156                         if (cm_HaveBuffer(linkScp, bufp, 0)) break;
1157                         
1158                         code = cm_GetBuffer(linkScp, bufp, NULL, userp, reqp);
1159                         if (code) {
1160                                 buf_Release(bufp);
1161                                 return code;
1162                         }
1163                 } /* while loop to get the data */
1164                 
1165                 /* now if we still have no link read in,
1166                  * copy the data from the buffer */
1167                 if ((temp = linkScp->length.LowPart) >= 1024) {
1168                         buf_Release(bufp);
1169                         return CM_ERROR_TOOBIG;
1170                 }
1171                 
1172                 /* otherwise, it fits; make sure it is still null (could have
1173                  * lost race with someone else referencing this link above),
1174                  * and if so, copy in the data.
1175                  */
1176                 if (linkScp->mountPointStringp == NULL) {
1177                         linkScp->mountPointStringp = malloc(temp+1);
1178                         strncpy(linkScp->mountPointStringp, bufp->datap, temp);
1179                         linkScp->mountPointStringp[temp] = 0;   /* null terminate */
1180                 }
1181                 buf_Release(bufp);
1182         }       /* don't have sym link contents cached */
1183         
1184         return 0;
1185 }
1186
1187 /* called with a held vnode and a path suffix, with the held vnode being a
1188  * symbolic link.  Our goal is to generate a new path to interpret, and return
1189  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1190  * other than the directory containing the symbolic link, then the new root is
1191  * returned in *newRootScpp, otherwise a null is returned there.
1192  */
1193 long cm_AssembleLink(cm_scache_t *linkScp, char *pathSuffixp,
1194         cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1195         cm_user_t *userp, cm_req_t *reqp)
1196 {
1197         long code;
1198         char *linkp;
1199         cm_space_t *tsp;
1200
1201         lock_ObtainMutex(&linkScp->mx);
1202         code = cm_HandleLink(linkScp, userp, reqp);
1203         if (code) goto done;
1204
1205         /* if we may overflow the buffer, bail out; buffer is signficantly
1206          * bigger than max path length, so we don't really have to worry about
1207          * being a little conservative here.
1208          */
1209         if (strlen(linkScp->mountPointStringp) + strlen(pathSuffixp) + 2
1210              >= CM_UTILS_SPACESIZE)
1211                 return CM_ERROR_TOOBIG;
1212
1213         tsp = cm_GetSpace();
1214         linkp = linkScp->mountPointStringp;
1215         if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1216                 if (strlen(linkp) > cm_mountRootLen)
1217                         strcpy(tsp->data, linkp+cm_mountRootLen+1);
1218                 else
1219                         tsp->data[0] = 0;
1220                 *newRootScpp = cm_rootSCachep;
1221                 cm_HoldSCache(cm_rootSCachep);
1222         } else if (*linkp == '\\' || *linkp == '/') {
1223           /* formerly, this was considered to be from the AFS root,
1224              but this seems to create problems.  instead, we will just
1225              reject the link */
1226 #if 0
1227                 strcpy(tsp->data, linkp+1);
1228                 *newRootScpp = cm_rootSCachep;
1229                 cm_HoldSCache(cm_rootSCachep);
1230 #else
1231                 code = CM_ERROR_NOSUCHPATH;
1232                 goto done;
1233 #endif
1234         }
1235         else {
1236                 /* a relative link */
1237                 strcpy(tsp->data, linkp);
1238                 *newRootScpp = NULL;
1239         }
1240         if (pathSuffixp[0] != 0) {      /* if suffix string is non-null */
1241                 strcat(tsp->data, "\\");
1242                 strcat(tsp->data, pathSuffixp);
1243         }
1244         *newSpaceBufferp = tsp;
1245         code = 0;
1246
1247 done:
1248         lock_ReleaseMutex(&linkScp->mx);
1249         return code;
1250 }
1251
1252 long cm_NameI(cm_scache_t *rootSCachep, char *pathp, long flags,
1253         cm_user_t *userp, char *tidPathp, cm_req_t *reqp, cm_scache_t **outScpp)
1254 {
1255         long code;
1256         char *tp;                       /* ptr moving through input buffer */
1257         char tc;                        /* temp char */
1258         int haveComponent;              /* has new component started? */
1259         char component[256];            /* this is the new component */
1260         char *cp;                       /* component name being assembled */
1261         cm_scache_t *tscp;              /* current location in the hierarchy */
1262         cm_scache_t *nscp;              /* next dude down */
1263         cm_scache_t *dirScp;            /* last dir we searched */
1264         cm_scache_t *linkScp;           /* new root for the symlink we just
1265                                          * looked up */
1266         cm_space_t *psp;                /* space for current path, if we've hit
1267                                          * any symlinks */
1268         cm_space_t *tempsp;             /* temp vbl */
1269         char *restp;                    /* rest of the pathname to interpret */
1270         int symlinkCount;               /* count of # of symlinks traversed */
1271         int extraFlag;                  /* avoid chasing mt pts for dir cmd */
1272         int phase = 1;                  /* 1 = tidPathp, 2 = pathp */
1273
1274         tp = tidPathp;
1275         if (tp == NULL) {
1276                 tp = pathp;
1277                 phase = 2;
1278         }
1279         haveComponent = 0;
1280         psp = NULL;
1281         tscp = rootSCachep;
1282         cm_HoldSCache(tscp);
1283         symlinkCount = 0;
1284         while (1) {
1285             tc = *tp++;
1286                 
1287             /* map Unix slashes into DOS ones so we can interpret Unix
1288              * symlinks properly
1289              */
1290             if (tc == '/') tc = '\\';
1291
1292             if (!haveComponent) {
1293                         if (tc == '\\') continue;
1294             else if (tc == 0) {
1295                                 if (phase == 1) {
1296                                         phase = 2;
1297                                         tp = pathp;
1298                                         continue;
1299                                 }
1300                 code = 0;
1301                 break;
1302             }
1303             else {
1304                                 haveComponent = 1;
1305                 cp = component;
1306                 *cp++ = tc;
1307             }
1308             }
1309             else {
1310                 /* we have a component here */
1311                 if (tc == 0 || tc == '\\') {
1312                     /* end of the component; we're at the last
1313                      * component if tc == 0.  However, if the last
1314                      * is a symlink, we have more to do.
1315                      */
1316                     *cp++ = 0;  /* add null termination */
1317                     extraFlag = 0;
1318                     if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1319                         extraFlag = CM_FLAG_NOMOUNTCHASE;
1320                     code = cm_Lookup(tscp, component,
1321                                       flags | extraFlag,
1322                                       userp, reqp, &nscp);
1323
1324                     if (code) {
1325                         cm_ReleaseSCache(tscp);
1326                         if (psp) cm_FreeSpace(psp);
1327                         return code;
1328                     }
1329                     haveComponent = 0;  /* component done */
1330                     dirScp = tscp;              /* for some symlinks */
1331                     tscp = nscp;        /* already held */
1332                     if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
1333                         code = 0;
1334                         cm_ReleaseSCache(dirScp);
1335                         break;
1336                     }
1337
1338                     /* now, if tscp is a symlink, we should follow
1339                      * it and assemble the path again.
1340                      */
1341                     lock_ObtainMutex(&tscp->mx);
1342                     code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1343                                       CM_SCACHESYNC_GETSTATUS
1344                                       | CM_SCACHESYNC_NEEDCALLBACK);
1345                     if (code) {
1346                         lock_ReleaseMutex(&tscp->mx);
1347                         cm_ReleaseSCache(tscp);
1348                         cm_ReleaseSCache(dirScp);
1349                         break;
1350                     }
1351                     if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
1352                         /* this is a symlink; assemble a new buffer */
1353                         lock_ReleaseMutex(&tscp->mx);
1354                         if (symlinkCount++ >= 16) {
1355                             cm_ReleaseSCache(tscp);
1356                             cm_ReleaseSCache(dirScp);
1357                             if (psp) cm_FreeSpace(psp);
1358                             return CM_ERROR_TOOBIG;
1359                         }
1360                         if (tc == 0) restp = "";
1361                         else restp = tp;
1362                         code = cm_AssembleLink(tscp, restp, &linkScp, &tempsp, userp, reqp);
1363                         if (code) {
1364                             /* something went wrong */
1365                             cm_ReleaseSCache(tscp);
1366                             cm_ReleaseSCache(dirScp);
1367                             break;
1368                         }
1369
1370                         /* otherwise, tempsp has the new path,
1371                          * and linkScp is the new root from
1372                          * which to interpret that path.
1373                          * Continue with the namei processing,
1374                          * also doing the bookkeeping for the
1375                          * space allocation and tracking the
1376                          * vnode reference counts.
1377                          */
1378                         if (psp) cm_FreeSpace(psp);
1379                         psp = tempsp;
1380                         tp = psp->data;
1381                         cm_ReleaseSCache(tscp);
1382                         tscp = linkScp; /* already held
1383                                          * by AssembleLink */
1384                         /* now, if linkScp is null, that's
1385                          * AssembleLink's way of telling us that
1386                          * the sym link is relative to the dir
1387                          * containing the link.  We have a ref
1388                          * to it in dirScp, and we hold it now
1389                          * and reuse it as the new spot in the
1390                          * dir hierarchy.
1391                          */
1392                         if (tscp == NULL) {
1393                             cm_HoldSCache(dirScp);
1394                             tscp = dirScp;
1395                         }
1396                     }   /* if we have a sym link */
1397                     else {
1398                         /* not a symlink, we may be done */
1399                         lock_ReleaseMutex(&tscp->mx);
1400                         if (tc == 0) {
1401                             if (phase == 1) {
1402                                 phase = 2;
1403                                 tp = pathp;
1404                                 continue;
1405                             }
1406                             cm_ReleaseSCache(dirScp);
1407                             code = 0;
1408                             break;
1409                         }
1410                     }
1411                     cm_ReleaseSCache(dirScp);
1412                 } /* end of a component */
1413                 else *cp++ = tc;
1414             } /* we have a component */
1415         } /* big while loop over all components */
1416
1417         /* already held */
1418     if (psp) cm_FreeSpace(psp);
1419         if (code == 0) *outScpp = tscp;
1420         return code;
1421 }
1422
1423 /* called with a dir, and a vnode within the dir that happens to be a symlink.
1424  * We chase the link, and return a held pointer to the target, if it exists,
1425  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
1426  * and do not hold or return a target vnode.
1427  *
1428  * This is very similar to calling cm_NameI with the last component of a name,
1429  * which happens to be a symlink, except that we've already passed by the name.
1430  *
1431  * This function is typically called by the directory listing functions, which
1432  * encounter symlinks but need to return the proper file length so programs
1433  * like "more" work properly when they make use of the attributes retrieved from
1434  * the dir listing.
1435  *
1436  * The input vnode should not be locked when this function is called.
1437  */
1438 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
1439         cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
1440 {
1441         long code;
1442         cm_space_t *spacep;
1443         cm_scache_t *newRootScp;
1444
1445         osi_Log1(afsd_logp, "Evaluating symlink vp %x", linkScp);
1446
1447         code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
1448         if (code) return code;
1449         
1450         /* now, if newRootScp is NULL, we're really being told that the symlink
1451          * is relative to the current directory (dscp).
1452          */
1453         if (newRootScp == NULL) {
1454                 newRootScp = dscp;
1455                 cm_HoldSCache(dscp);
1456         }
1457         
1458         code = cm_NameI(newRootScp, spacep->data,
1459                 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
1460                 userp, NULL, reqp, outScpp);
1461
1462         /* this stuff is allocated no matter what happened on the namei call,
1463          * so free it */
1464         cm_FreeSpace(spacep);
1465         cm_ReleaseSCache(newRootScp);
1466
1467         return code;
1468 }
1469
1470 /* make this big enough so that one buffer of dir pages won't overflow.  We'll
1471  * check anyway, but we want to minimize the chance that we have to leave stuff
1472  * unstat'd.
1473  */
1474 #define CM_BULKMAX              128
1475
1476 /* rock for bulk stat calls */
1477 typedef struct cm_bulkStat {
1478         osi_hyper_t bufOffset;  /* only do it for things in this buffer page */
1479
1480         /* info for the actual call */
1481         int counter;                    /* next free slot */
1482         AFSFid fids[CM_BULKMAX];
1483         AFSFetchStatus stats[CM_BULKMAX];
1484         AFSCallBack callbacks[CM_BULKMAX];
1485 } cm_bulkStat_t;
1486
1487 /* for a given entry, make sure that it isn't in the stat cache, and then
1488  * add it to the list of file IDs to be obtained.
1489  *
1490  * Don't bother adding it if we already have a vnode.  Note that the dir
1491  * is locked, so we have to be careful checking the vnode we're thinking of
1492  * processing, to avoid deadlocks.
1493  */
1494 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
1495         osi_hyper_t *offp)
1496 {
1497         osi_hyper_t thyper;
1498         cm_bulkStat_t *bsp;
1499         int i;
1500         cm_scache_t *tscp;
1501         cm_fid_t tfid;
1502
1503         bsp = rockp;
1504
1505         /* Don't overflow bsp. */
1506         if (bsp->counter >= CM_BULKMAX)
1507                 return CM_ERROR_STOPNOW;
1508
1509         thyper.LowPart = buf_bufferSize;
1510         thyper.HighPart = 0;
1511         thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
1512         
1513         /* thyper is now the first byte past the end of the record we're
1514          * interested in, and bsp->bufOffset is the first byte of the record
1515          * we're interested in.
1516          * Skip data in the others.
1517          * Skip '.' and '..'
1518          */
1519         if (LargeIntegerLessThan(*offp, bsp->bufOffset))
1520                 return 0;
1521         if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
1522                 return CM_ERROR_STOPNOW;
1523         if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
1524                 return 0;
1525         
1526         tfid.cell = scp->fid.cell;
1527         tfid.volume = scp->fid.volume;
1528         tfid.vnode = ntohl(dep->fid.vnode);
1529         tfid.unique = ntohl(dep->fid.unique);
1530         tscp = cm_FindSCache(&tfid);
1531         if (tscp) {
1532                 if (lock_TryMutex(&tscp->mx)) {
1533                         /* we have an entry that we can look at */
1534                         if (cm_HaveCallback(tscp)) {
1535                                 /* we have a callback on it.  Don't bother
1536                                  * fetching this stat entry, since we're happy
1537                                  * with the info we have.
1538                                  */
1539                                 lock_ReleaseMutex(&tscp->mx);
1540                                 cm_ReleaseSCache(tscp);
1541                                 return 0;
1542                         }
1543                         lock_ReleaseMutex(&tscp->mx);
1544                 }       /* got lock */
1545                 cm_ReleaseSCache(tscp);
1546         }       /* found entry */
1547
1548 #ifdef AFS_FREELANCE_CLIENT
1549         // yj: if this is a mountpoint under root.afs then we don't want it
1550         // to be bulkstat-ed, instead, we call getSCache directly and under
1551         // getSCache, it is handled specially.
1552         if      (cm_freelanceEnabled &&
1553            tfid.cell==0x1 && tfid.volume==0x20000001 &&
1554                            !(tfid.vnode==0x1 && tfid.unique==0x1) )
1555         {
1556 #ifdef DEBUG
1557                 afsi_log("   cm_trybulkproc going to call getscache");
1558 #endif
1559                 return cm_GetSCache(&tfid, &tscp, NULL, NULL);
1560         }
1561 #endif /* AFS_FREELANCE_CLIENT */
1562
1563         i = bsp->counter++;
1564         bsp->fids[i].Volume = scp->fid.volume;
1565         bsp->fids[i].Vnode = tfid.vnode;
1566         bsp->fids[i].Unique = tfid.unique;
1567         return 0;
1568 }
1569
1570 /* called with a locked scp and a pointer to a buffer.  Make bulk stat
1571  * calls on all undeleted files in the page of the directory specified.
1572  */
1573 void cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
1574         cm_req_t *reqp)
1575 {
1576         long code;
1577         cm_bulkStat_t bb;       /* this is *BIG*, probably 12K or so;
1578                                  * watch for stack problems */
1579         AFSCBFids fidStruct;
1580         AFSBulkStats statStruct;
1581         cm_conn_t *connp;
1582         AFSCBs callbackStruct;
1583         long filex;
1584         AFSVolSync volSync;
1585         cm_callbackRequest_t cbReq;
1586         long filesThisCall;
1587         long i;
1588         long j;
1589         cm_scache_t *scp;
1590         cm_fid_t tfid;
1591
1592         osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%x", (long) dscp);
1593
1594         /* should be on a buffer boundary */
1595         osi_assert((offsetp->LowPart & (buf_bufferSize - 1)) == 0);
1596
1597         bb.counter = 0;
1598         bb.bufOffset = *offsetp;
1599
1600         /* first, assemble the file IDs we need to stat */
1601         code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) &bb, offsetp, userp,
1602                            reqp, NULL);
1603
1604         /* if we failed, bail out early */
1605         if (code && code != CM_ERROR_STOPNOW) return;
1606         
1607         /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
1608          * make the calls to create the entries.  Handle AFSCBMAX files at a
1609          * time.
1610          */
1611         filex = 0;
1612         while(filex < bb.counter) {
1613                 filesThisCall = bb.counter - filex;
1614                 if (filesThisCall > AFSCBMAX) filesThisCall = AFSCBMAX;
1615
1616                 fidStruct.AFSCBFids_len = filesThisCall;
1617                 fidStruct.AFSCBFids_val = &bb.fids[filex];
1618                 statStruct.AFSBulkStats_len = filesThisCall;
1619                 statStruct.AFSBulkStats_val = &bb.stats[filex];
1620                 callbackStruct.AFSCBs_len = filesThisCall;
1621                 callbackStruct.AFSCBs_val = &bb.callbacks[filex];
1622                 cm_StartCallbackGrantingCall(NULL, &cbReq);
1623                 osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
1624                 do {
1625                         code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1626                         if (code) continue;
1627                 
1628                         code = RXAFS_BulkStatus(connp->callp, &fidStruct,
1629                                 &statStruct, &callbackStruct, &volSync);
1630
1631                 } while (cm_Analyze(connp, userp, reqp, &dscp->fid,
1632                                     &volSync, &cbReq, code));
1633                 code = cm_MapRPCError(code, reqp);
1634
1635                 osi_Log0(afsd_logp, "CALL BulkStatus DONE");
1636                 
1637                 /* may as well quit on an error, since we're not going to do
1638                  * much better on the next immediate call, either.
1639                  */
1640                 if (code) break;
1641                 
1642                 /* otherwise, we should do the merges */
1643                 for(i = 0; i<filesThisCall; i++) {
1644                         j = filex + i;
1645                         tfid.cell = dscp->fid.cell;
1646                         tfid.volume = bb.fids[j].Volume;
1647                         tfid.vnode = bb.fids[j].Vnode;
1648                         tfid.unique = bb.fids[j].Unique;
1649                         code = cm_GetSCache(&tfid, &scp, userp, reqp);
1650                         if (code != 0) continue;
1651                         
1652                         /* otherwise, if this entry has no callback info, 
1653                          * merge in this.
1654                          */
1655                         lock_ObtainMutex(&scp->mx);
1656                         /* now, we have to be extra paranoid on merging in this
1657                          * information, since we didn't use cm_SyncOp before
1658                          * starting the fetch to make sure that no bad races
1659                          * were occurring.  Specifically, we need to make sure
1660                          * we don't obliterate any newer information in the
1661                          * vnode than have here.
1662                          *
1663                          * Right now, be pretty conservative: if there's a
1664                          * callback or a pending call, skip it.
1665                          */
1666                         if (scp->cbServerp == NULL
1667                                 && !(scp->flags &
1668                                         (CM_SCACHEFLAG_FETCHING
1669                                          | CM_SCACHEFLAG_STORING
1670                                          | CM_SCACHEFLAG_SIZESTORING))) {
1671                                 cm_EndCallbackGrantingCall(scp, &cbReq,
1672                                         &bb.callbacks[j],
1673                                         CM_CALLBACK_MAINTAINCOUNT);
1674                                 cm_MergeStatus(scp, &bb.stats[j], &volSync,
1675                                         userp, 0);
1676                         }
1677                         lock_ReleaseMutex(&scp->mx);
1678                         cm_ReleaseSCache(scp);
1679                 } /* all files in the response */
1680                 /* now tell it to drop the count,
1681                  * after doing the vnode processing above */
1682                 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1683                 
1684                 filex += filesThisCall;
1685         }       /* while there are still more files to process */
1686         osi_Log0(afsd_logp, "END cm_TryBulkStat");
1687 }
1688
1689 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
1690 {
1691         long mask;
1692
1693         /* initialize store back mask as inexpensive local variable */
1694         mask = 0;
1695         memset(statusp, 0, sizeof(AFSStoreStatus));
1696
1697         /* copy out queued info from scache first, if scp passed in */
1698         if (scp) {
1699                 if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
1700                         statusp->ClientModTime = scp->clientModTime;
1701                         mask |= AFS_SETMODTIME;
1702                         scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
1703                 }
1704         }
1705
1706         if (attrp) {
1707                 /* now add in our locally generated request */
1708                 if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
1709                         statusp->ClientModTime = attrp->clientModTime;
1710                         mask |= AFS_SETMODTIME;
1711                 }
1712                 if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
1713                         statusp->UnixModeBits = attrp->unixModeBits;
1714                         mask |= AFS_SETMODE;
1715                 }
1716                 if (attrp->mask & CM_ATTRMASK_OWNER) {
1717                         statusp->Owner = attrp->owner;
1718                         mask |= AFS_SETOWNER;
1719                 }
1720                 if (attrp->mask & CM_ATTRMASK_GROUP) {
1721                         statusp->Group = attrp->group;
1722                         mask |= AFS_SETGROUP;
1723                 }
1724         }
1725         statusp->Mask = mask;
1726 }
1727
1728 /* set the file size, and make sure that all relevant buffers have been
1729  * truncated.  Ensure that any partially truncated buffers have been zeroed
1730  * to the end of the buffer.
1731  */
1732 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
1733         cm_req_t *reqp)
1734 {
1735         long code;
1736         int shrinking;
1737
1738         /* start by locking out buffer creation */
1739         lock_ObtainWrite(&scp->bufCreateLock);
1740
1741         /* verify that this is a file, not a dir or a symlink */
1742         lock_ObtainMutex(&scp->mx);
1743         code = cm_SyncOp(scp, NULL, userp, reqp, 0,
1744                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1745         if (code) goto done;
1746         
1747         if (scp->fileType != CM_SCACHETYPE_FILE) {
1748                 code = CM_ERROR_ISDIR;
1749                 goto done;
1750         }
1751
1752 startover:
1753         if (LargeIntegerLessThan(*sizep, scp->length))
1754                 shrinking = 1;
1755         else
1756                 shrinking = 0;
1757
1758         lock_ReleaseMutex(&scp->mx);
1759
1760         /* can't hold scp->mx lock here, since we may wait for a storeback to
1761          * finish if the buffer package is cleaning a buffer by storing it to
1762          * the server.
1763          */
1764         if (shrinking)
1765                 buf_Truncate(scp, userp, reqp, sizep);
1766         
1767         /* now ensure that file length is short enough, and update truncPos */
1768         lock_ObtainMutex(&scp->mx);
1769         
1770         /* make sure we have a callback (so we have the right value for the
1771          * length), and wait for it to be safe to do a truncate.
1772          */
1773         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
1774                 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
1775                 | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
1776         if (code) goto done;
1777
1778         if (LargeIntegerLessThan(*sizep, scp->length)) {
1779                 /* a real truncation.  If truncPos is not set yet, or is bigger
1780                  * than where we're truncating the file, set truncPos to this
1781                  * new value.
1782                  */
1783                 if (!shrinking)
1784                         goto startover;
1785                 if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
1786                         || LargeIntegerLessThan(*sizep, scp->length)) {
1787                         /* set trunc pos */
1788                         scp->truncPos = *sizep;
1789                         scp->mask |= CM_SCACHEMASK_TRUNCPOS;
1790                 }
1791                 /* in either case, the new file size has been changed */
1792                 scp->length = *sizep;
1793                 scp->mask |= CM_SCACHEMASK_LENGTH;
1794         }
1795         else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
1796                 /* really extending the file */
1797                 scp->length = *sizep;
1798                 scp->mask |= CM_SCACHEMASK_LENGTH;
1799         }
1800
1801         /* done successfully */
1802         code = 0;
1803
1804 done:
1805         lock_ReleaseMutex(&scp->mx);
1806         lock_ReleaseWrite(&scp->bufCreateLock);
1807
1808         return code;
1809 }
1810
1811 /* set the file size or other attributes (but not both at once) */
1812 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
1813         cm_req_t *reqp)
1814 {
1815         long code;
1816         int flags;
1817         AFSFetchStatus afsOutStatus;
1818         AFSVolSync volSync;
1819         cm_conn_t *connp;
1820         AFSFid tfid;
1821         AFSStoreStatus afsInStatus;
1822
1823         /* handle file length setting */
1824         if (attrp->mask & CM_ATTRMASK_LENGTH)
1825                 return cm_SetLength(scp, &attrp->length, userp, reqp);
1826
1827         flags = CM_SCACHESYNC_STORESTATUS;
1828
1829         lock_ObtainMutex(&scp->mx);
1830         /* otherwise, we have to make an RPC to get the status */
1831         code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
1832
1833         /* make the attr structure */
1834         cm_StatusFromAttr(&afsInStatus, scp, attrp);
1835
1836         lock_ReleaseMutex(&scp->mx);
1837         if (code) return code;
1838                 
1839         /* now make the RPC */
1840         osi_Log1(afsd_logp, "CALL StoreStatus vp %x", (long) scp);
1841         tfid.Volume = scp->fid.volume;
1842         tfid.Vnode = scp->fid.vnode;
1843         tfid.Unique = scp->fid.unique;
1844         do {
1845                 code = cm_Conn(&scp->fid, userp, reqp, &connp);
1846                 if (code) continue;
1847                 
1848                 code = RXAFS_StoreStatus(connp->callp, &tfid,
1849                         &afsInStatus, &afsOutStatus, &volSync);
1850
1851         } while (cm_Analyze(connp, userp, reqp,
1852                             &scp->fid, &volSync, NULL, code));
1853         code = cm_MapRPCError(code, reqp);
1854
1855         osi_Log1(afsd_logp, "CALL StoreStatus DONE, code %d", code);
1856
1857         lock_ObtainMutex(&scp->mx);
1858         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
1859         if (code == 0)
1860                 cm_MergeStatus(scp, &afsOutStatus, &volSync, userp,
1861                                 CM_MERGEFLAG_FORCE);
1862         
1863         /* if we're changing the mode bits, discard the ACL cache, 
1864          * since we changed the mode bits.
1865          */
1866         if (afsInStatus.Mask & AFS_SETMODE) cm_FreeAllACLEnts(scp);
1867         lock_ReleaseMutex(&scp->mx);
1868         return code;
1869 }
1870
1871 long cm_Create(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1872         cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
1873 {
1874         cm_conn_t *connp;
1875         long code;
1876         AFSFid dirAFSFid;
1877         cm_callbackRequest_t cbReq;
1878         AFSFid newAFSFid;
1879         cm_fid_t newFid;
1880         cm_scache_t *scp;
1881         int didEnd;
1882         AFSStoreStatus inStatus;
1883         AFSFetchStatus updatedDirStatus;
1884         AFSFetchStatus newFileStatus;
1885         AFSCallBack newFileCallback;
1886         AFSVolSync volSync;
1887
1888         /* can't create names with @sys in them; must expand it manually first.
1889          * return "invalid request" if they try.
1890          */
1891         if (cm_ExpandSysName(namep, NULL, 0)) {
1892                 return CM_ERROR_ATSYS;
1893         }
1894
1895         /* before starting the RPC, mark that we're changing the file data, so
1896          * that someone who does a chmod will know to wait until our call
1897          * completes.
1898          */
1899         lock_ObtainMutex(&dscp->mx);
1900         code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
1901         if (code == 0) {
1902                 cm_StartCallbackGrantingCall(NULL, &cbReq);
1903         }
1904         lock_ReleaseMutex(&dscp->mx);
1905         if (code) {
1906                 return code;
1907         }
1908         didEnd = 0;
1909
1910         cm_StatusFromAttr(&inStatus, NULL, attrp);
1911
1912         /* try the RPC now */
1913         do {
1914                 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
1915                 if (code) continue;
1916                 
1917                 dirAFSFid.Volume = dscp->fid.volume;
1918                 dirAFSFid.Vnode = dscp->fid.vnode;
1919                 dirAFSFid.Unique = dscp->fid.unique;
1920                 code = RXAFS_CreateFile(connp->callp, &dirAFSFid, namep,
1921                                         &inStatus, &newAFSFid, &newFileStatus,
1922                                         &updatedDirStatus, &newFileCallback,
1923                                         &volSync);
1924         } while (cm_Analyze(connp, userp, reqp,
1925                             &dscp->fid, &volSync, &cbReq, code));
1926         code = cm_MapRPCError(code, reqp);
1927         
1928         lock_ObtainMutex(&dscp->mx);
1929         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
1930         if (code == 0) {
1931                 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
1932         }
1933         lock_ReleaseMutex(&dscp->mx);
1934
1935         /* now try to create the file's entry, too, but be careful to 
1936          * make sure that we don't merge in old info.  Since we weren't locking
1937          * out any requests during the file's creation, we may have pretty old
1938          * info.
1939          */
1940         if (code == 0) {
1941                 newFid.cell = dscp->fid.cell;
1942                 newFid.volume = dscp->fid.volume;
1943                 newFid.vnode = newAFSFid.Vnode;
1944                 newFid.unique = newAFSFid.Unique;
1945                 code = cm_GetSCache(&newFid, &scp, userp, reqp);
1946                 if (code == 0) {
1947                         lock_ObtainMutex(&scp->mx);
1948                         if (!cm_HaveCallback(scp)) {
1949                                 cm_MergeStatus(scp, &newFileStatus, &volSync,
1950                                                 userp, 0);
1951                                 cm_EndCallbackGrantingCall(scp, &cbReq,
1952                                                         &newFileCallback, 0);
1953                                 didEnd = 1;
1954                         }
1955                         lock_ReleaseMutex(&scp->mx);
1956                         *scpp = scp;
1957                 }
1958         }
1959         
1960         /* make sure we end things properly */
1961         if (!didEnd)
1962                 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
1963
1964         return code;
1965 }
1966
1967 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
1968 {
1969         long code;
1970
1971         lock_ObtainWrite(&scp->bufCreateLock);
1972         code = buf_CleanVnode(scp, userp, reqp);
1973         lock_ReleaseWrite(&scp->bufCreateLock);
1974         if (code == 0) {
1975                 lock_ObtainMutex(&scp->mx);
1976                 scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA
1977                                  | CM_SCACHEFLAG_OUTOFSPACE);
1978                 if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
1979                                    | CM_SCACHEMASK_CLIENTMODTIME
1980                                    | CM_SCACHEMASK_LENGTH))
1981                         code = cm_StoreMini(scp, userp, reqp);
1982                 lock_ReleaseMutex(&scp->mx);
1983         }
1984         return code;
1985 }
1986
1987 long cm_MakeDir(cm_scache_t *dscp, char *namep, long flags, cm_attr_t *attrp,
1988         cm_user_t *userp, cm_req_t *reqp)
1989 {
1990         cm_conn_t *connp;
1991         long code;
1992         AFSFid dirAFSFid;
1993         cm_callbackRequest_t cbReq;
1994         AFSFid newAFSFid;
1995         cm_fid_t newFid;
1996         cm_scache_t *scp;
1997         int didEnd;
1998         AFSStoreStatus inStatus;
1999         AFSFetchStatus updatedDirStatus;
2000         AFSFetchStatus newDirStatus;
2001         AFSCallBack newDirCallback;
2002         AFSVolSync volSync;
2003
2004         /* can't create names with @sys in them; must expand it manually first.
2005          * return "invalid request" if they try.
2006          */
2007         if (cm_ExpandSysName(namep, NULL, 0)) {
2008                 return CM_ERROR_ATSYS;
2009         }
2010
2011         /* before starting the RPC, mark that we're changing the directory
2012          * data, so that someone who does a chmod on the dir will wait until
2013          * our call completes.
2014          */
2015         lock_ObtainMutex(&dscp->mx);
2016         code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2017         if (code == 0) {
2018                 cm_StartCallbackGrantingCall(NULL, &cbReq);
2019         }
2020         lock_ReleaseMutex(&dscp->mx);
2021         if (code) {
2022                 return code;
2023         }
2024         didEnd = 0;
2025
2026         cm_StatusFromAttr(&inStatus, NULL, attrp);
2027
2028         /* try the RPC now */
2029         do {
2030                 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2031                 if (code) continue;
2032                 
2033                 dirAFSFid.Volume = dscp->fid.volume;
2034                 dirAFSFid.Vnode = dscp->fid.vnode;
2035                 dirAFSFid.Unique = dscp->fid.unique;
2036                 code = RXAFS_MakeDir(connp->callp, &dirAFSFid, namep,
2037                                      &inStatus, &newAFSFid, &newDirStatus,
2038                                      &updatedDirStatus, &newDirCallback,
2039                                      &volSync);
2040         } while (cm_Analyze(connp, userp, reqp,
2041                             &dscp->fid, &volSync, &cbReq, code));
2042         code = cm_MapRPCError(code, reqp);
2043         
2044         lock_ObtainMutex(&dscp->mx);
2045         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2046         if (code == 0) {
2047                 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2048         }
2049         lock_ReleaseMutex(&dscp->mx);
2050
2051         /* now try to create the new dir's entry, too, but be careful to 
2052          * make sure that we don't merge in old info.  Since we weren't locking
2053          * out any requests during the file's creation, we may have pretty old
2054          * info.
2055          */
2056         if (code == 0) {
2057                 newFid.cell = dscp->fid.cell;
2058                 newFid.volume = dscp->fid.volume;
2059                 newFid.vnode = newAFSFid.Vnode;
2060                 newFid.unique = newAFSFid.Unique;
2061                 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2062                 if (code == 0) {
2063                         lock_ObtainMutex(&scp->mx);
2064                         if (!cm_HaveCallback(scp)) {
2065                                 cm_MergeStatus(scp, &newDirStatus, &volSync,
2066                                                 userp, 0);
2067                                 cm_EndCallbackGrantingCall(scp, &cbReq,
2068                                                         &newDirCallback, 0);
2069                                 didEnd = 1;
2070                         }
2071                         lock_ReleaseMutex(&scp->mx);
2072                         cm_ReleaseSCache(scp);
2073                 }
2074         }
2075         
2076         /* make sure we end things properly */
2077         if (!didEnd)
2078                 cm_EndCallbackGrantingCall(NULL, NULL, NULL, 0);
2079         
2080         /* and return error code */
2081         return code;
2082 }
2083
2084 long cm_SymLink(cm_scache_t *dscp, char *namep, char *contentsp, long flags,
2085         cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
2086 {
2087         cm_conn_t *connp;
2088         long code;
2089         AFSFid dirAFSFid;
2090         AFSFid newAFSFid;
2091         cm_fid_t newFid;
2092         cm_scache_t *scp;
2093         AFSStoreStatus inStatus;
2094         AFSFetchStatus updatedDirStatus;
2095         AFSFetchStatus newLinkStatus;
2096         AFSVolSync volSync;
2097
2098         /* before starting the RPC, mark that we're changing the directory data,
2099          * so that someone who does a chmod on the dir will wait until our
2100          * call completes.
2101          */
2102         lock_ObtainMutex(&dscp->mx);
2103         code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2104         lock_ReleaseMutex(&dscp->mx);
2105         if (code) {
2106                 return code;
2107         }
2108
2109         cm_StatusFromAttr(&inStatus, NULL, attrp);
2110
2111         /* try the RPC now */
2112         do {
2113                 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2114                 if (code) continue;
2115                 
2116                 dirAFSFid.Volume = dscp->fid.volume;
2117                 dirAFSFid.Vnode = dscp->fid.vnode;
2118                 dirAFSFid.Unique = dscp->fid.unique;
2119                 code = RXAFS_Symlink(connp->callp, &dirAFSFid, namep, contentsp,
2120                                      &inStatus, &newAFSFid, &newLinkStatus,
2121                                      &updatedDirStatus, &volSync);
2122         } while (cm_Analyze(connp, userp, reqp,
2123                             &dscp->fid, &volSync, NULL, code));
2124         code = cm_MapRPCError(code, reqp);
2125         
2126         lock_ObtainMutex(&dscp->mx);
2127         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2128         if (code == 0) {
2129                 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2130         }
2131         lock_ReleaseMutex(&dscp->mx);
2132
2133         /* now try to create the new dir's entry, too, but be careful to 
2134          * make sure that we don't merge in old info.  Since we weren't locking
2135          * out any requests during the file's creation, we may have pretty old
2136          * info.
2137          */
2138         if (code == 0) {
2139                 newFid.cell = dscp->fid.cell;
2140                 newFid.volume = dscp->fid.volume;
2141                 newFid.vnode = newAFSFid.Vnode;
2142                 newFid.unique = newAFSFid.Unique;
2143                 code = cm_GetSCache(&newFid, &scp, userp, reqp);
2144                 if (code == 0) {
2145                         lock_ObtainMutex(&scp->mx);
2146                         if (!cm_HaveCallback(scp)) {
2147                                 cm_MergeStatus(scp, &newLinkStatus, &volSync,
2148                                                 userp, 0);
2149                         }
2150                         lock_ReleaseMutex(&scp->mx);
2151                         cm_ReleaseSCache(scp);
2152                 }
2153         }
2154         
2155         /* and return error code */
2156         return code;
2157 }
2158
2159 long cm_RemoveDir(cm_scache_t *dscp, char *namep, cm_user_t *userp,
2160         cm_req_t *reqp)
2161 {
2162         cm_conn_t *connp;
2163         long code;
2164         AFSFid dirAFSFid;
2165         int didEnd;
2166         AFSFetchStatus updatedDirStatus;
2167         AFSVolSync volSync;
2168
2169         /* before starting the RPC, mark that we're changing the directory data,
2170          * so that someone who does a chmod on the dir will wait until our
2171          * call completes.
2172          */
2173         lock_ObtainMutex(&dscp->mx);
2174         code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2175         lock_ReleaseMutex(&dscp->mx);
2176         if (code) {
2177                 return code;
2178         }
2179         didEnd = 0;
2180
2181         /* try the RPC now */
2182         do {
2183                 code = cm_Conn(&dscp->fid, userp, reqp, &connp);
2184                 if (code) continue;
2185                 
2186                 dirAFSFid.Volume = dscp->fid.volume;
2187                 dirAFSFid.Vnode = dscp->fid.vnode;
2188                 dirAFSFid.Unique = dscp->fid.unique;
2189                 code = RXAFS_RemoveDir(connp->callp, &dirAFSFid, namep,
2190                                         &updatedDirStatus, &volSync);
2191         } while (cm_Analyze(connp, userp, reqp,
2192                             &dscp->fid, &volSync, NULL, code));
2193         code = cm_MapRPCErrorRmdir(code, reqp);
2194         
2195         lock_ObtainMutex(&dscp->mx);
2196         cm_dnlcRemove(dscp, namep); 
2197         cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2198         if (code == 0) {
2199                 cm_MergeStatus(dscp, &updatedDirStatus, &volSync, userp, 0);
2200         }
2201         lock_ReleaseMutex(&dscp->mx);
2202
2203         /* and return error code */
2204         return code;
2205 }
2206
2207 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
2208 {
2209         /* grab mutex on contents */
2210         lock_ObtainMutex(&scp->mx);
2211
2212         /* reset the prefetch info */
2213         scp->prefetch.base.LowPart = 0;         /* base */
2214         scp->prefetch.base.HighPart = 0;
2215         scp->prefetch.end.LowPart = 0;          /* and end */
2216         scp->prefetch.end.HighPart = 0;
2217         
2218         /* release mutex on contents */
2219         lock_ReleaseMutex(&scp->mx);
2220         
2221         /* we're done */
2222         return 0;
2223 }
2224
2225 long cm_Rename(cm_scache_t *oldDscp, char *oldNamep, cm_scache_t *newDscp,
2226         char *newNamep, cm_user_t *userp, cm_req_t *reqp)
2227 {
2228         cm_conn_t *connp;
2229         long code;
2230         AFSFid oldDirAFSFid;
2231         AFSFid newDirAFSFid;
2232         int didEnd;
2233         AFSFetchStatus updatedOldDirStatus;
2234         AFSFetchStatus updatedNewDirStatus;
2235         AFSVolSync volSync;
2236         int oneDir;
2237
2238         /* before starting the RPC, mark that we're changing the directory data,
2239          * so that someone who does a chmod on the dir will wait until our call
2240          * completes.  We do this in vnode order so that we don't deadlock,
2241          * which makes the code a little verbose.
2242          */
2243         if (oldDscp == newDscp) {
2244                 /* check for identical names */
2245                 if (strcmp(oldNamep, newNamep) == 0)
2246                         return CM_ERROR_RENAME_IDENTICAL;
2247
2248                 oneDir = 1;
2249                 lock_ObtainMutex(&oldDscp->mx);
2250                 cm_dnlcRemove(oldDscp, oldNamep);
2251                 cm_dnlcRemove(oldDscp, newNamep);
2252                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2253                                  CM_SCACHESYNC_STOREDATA);
2254                 lock_ReleaseMutex(&oldDscp->mx);
2255         }
2256         else {
2257                 /* two distinct dir vnodes */
2258                 oneDir = 0;
2259                 if (oldDscp->fid.cell != newDscp->fid.cell ||
2260                         oldDscp->fid.volume != newDscp->fid.volume)
2261                                 return CM_ERROR_CROSSDEVLINK;
2262
2263                 /* shouldn't happen that we have distinct vnodes for two
2264                  * different files, but could due to deliberate attack, or
2265                  * stale info.  Avoid deadlocks and quit now.
2266                  */
2267                 if (oldDscp->fid.vnode == newDscp->fid.vnode)
2268                         return CM_ERROR_CROSSDEVLINK;
2269                         
2270                 if (oldDscp->fid.vnode < newDscp->fid.vnode) {
2271                         lock_ObtainMutex(&oldDscp->mx);
2272                         cm_dnlcRemove(oldDscp, oldNamep);
2273                         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2274                                          CM_SCACHESYNC_STOREDATA);
2275                         lock_ReleaseMutex(&oldDscp->mx);
2276                         if (code == 0) {
2277                                 lock_ObtainMutex(&newDscp->mx);
2278                                 cm_dnlcRemove(newDscp, newNamep);
2279                                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2280                                                  CM_SCACHESYNC_STOREDATA);
2281                                 lock_ReleaseMutex(&newDscp->mx);
2282                                 if (code) {
2283                                         /* cleanup first one */
2284                                         cm_SyncOpDone(oldDscp, NULL,
2285                                                       CM_SCACHESYNC_STOREDATA);
2286                                 }
2287                         }
2288                 }
2289                 else {
2290                         /* lock the new vnode entry first */
2291                         lock_ObtainMutex(&newDscp->mx);
2292                         cm_dnlcRemove(newDscp, newNamep);
2293                         code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
2294                                          CM_SCACHESYNC_STOREDATA);
2295                         lock_ReleaseMutex(&newDscp->mx);
2296                         if (code == 0) {
2297                                 lock_ObtainMutex(&oldDscp->mx);
2298                                 cm_dnlcRemove(oldDscp, oldNamep);
2299                                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
2300                                                  CM_SCACHESYNC_STOREDATA);
2301                                 lock_ReleaseMutex(&oldDscp->mx);
2302                                 if (code) {
2303                                         /* cleanup first one */
2304                                         cm_SyncOpDone(newDscp, NULL,
2305                                                       CM_SCACHESYNC_STOREDATA);
2306                                 }
2307                         }
2308                 }
2309         }       /* two distinct vnodes */
2310
2311         if (code) {
2312                 return code;
2313         }
2314         didEnd = 0;
2315
2316         /* try the RPC now */
2317         do {
2318                 code = cm_Conn(&oldDscp->fid, userp, reqp, &connp);
2319                 if (code) continue;
2320                 
2321                 oldDirAFSFid.Volume = oldDscp->fid.volume;
2322                 oldDirAFSFid.Vnode = oldDscp->fid.vnode;
2323                 oldDirAFSFid.Unique = oldDscp->fid.unique;
2324                 newDirAFSFid.Volume = newDscp->fid.volume;
2325                 newDirAFSFid.Vnode = newDscp->fid.vnode;
2326                 newDirAFSFid.Unique = newDscp->fid.unique;
2327                 code = RXAFS_Rename(connp->callp, &oldDirAFSFid, oldNamep,
2328                         &newDirAFSFid, newNamep,
2329                         &updatedOldDirStatus, &updatedNewDirStatus,
2330                         &volSync);
2331         } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
2332                             &volSync, NULL, code));
2333         code = cm_MapRPCError(code, reqp);
2334         
2335         /* update the individual stat cache entries for the directories */
2336         lock_ObtainMutex(&oldDscp->mx);
2337         cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
2338         if (code == 0) {
2339                 cm_MergeStatus(oldDscp, &updatedOldDirStatus, &volSync,
2340                                 userp, 0);
2341         }
2342         lock_ReleaseMutex(&oldDscp->mx);
2343
2344         /* and update it for the new one, too, if necessary */
2345         if (!oneDir) {
2346                 lock_ObtainMutex(&newDscp->mx);
2347                 cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
2348                 if (code == 0) {
2349                         cm_MergeStatus(newDscp, &updatedNewDirStatus, &volSync,
2350                                         userp, 0);
2351                 }
2352                 lock_ReleaseMutex(&newDscp->mx);
2353         }
2354
2355         /* and return error code */
2356         return code;
2357 }
2358
2359 long cm_Lock(cm_scache_t *scp, unsigned char LockType,
2360         LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2361         u_long Timeout, cm_user_t *userp, cm_req_t *reqp,
2362         void **lockpp)
2363 {
2364         long code;
2365         int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2366         AFSFid tfid;
2367         AFSVolSync volSync;
2368         cm_conn_t *connp;
2369         cm_file_lock_t *fileLock;
2370         osi_queue_t *q;
2371         int found = 0;
2372
2373         /* Look for a conflict.  Also, if we are asking for a shared lock,
2374          * look for another shared lock, so we don't have to do an RPC.
2375          */
2376         q = scp->fileLocks;
2377         while (q) {
2378                 fileLock = (cm_file_lock_t *)
2379                                 ((char *) q - offsetof(cm_file_lock_t, fileq));
2380                 if ((fileLock->flags &
2381                         (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2382                     == 0) {
2383                         if ((LockType & 0x1) == 0
2384                             || (fileLock->LockType & 0x1) == 0)
2385                                 return CM_ERROR_WOULDBLOCK;
2386                         found = 1;
2387                 }
2388                 q = osi_QNext(q);
2389         }
2390
2391         if (found)
2392                 code = 0;
2393         else {
2394                 tfid.Volume = scp->fid.volume;
2395                 tfid.Vnode = scp->fid.vnode;
2396                 tfid.Unique = scp->fid.unique;
2397                 lock_ReleaseMutex(&scp->mx);
2398                 do {
2399                         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2400                         if (code) break;
2401                         code = RXAFS_SetLock(connp->callp, &tfid, Which,
2402                                              &volSync);
2403                 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2404                                     NULL, code));
2405                 lock_ObtainMutex(&scp->mx);
2406                 code = cm_MapRPCError(code, reqp);
2407         }
2408
2409         if (code == 0 || Timeout != 0) {
2410                 fileLock = malloc(sizeof(cm_file_lock_t));
2411                 fileLock->LockType = LockType;
2412                 cm_HoldUser(userp);
2413                 fileLock->userp = userp;
2414                 fileLock->fid = scp->fid;
2415                 fileLock->LOffset = LOffset;
2416                 fileLock->LLength = LLength;
2417                 fileLock->flags = (code == 0 ? 0 : CM_FILELOCK_FLAG_WAITING);
2418                 osi_QAdd(&scp->fileLocks, &fileLock->fileq);
2419                 lock_ObtainWrite(&cm_scacheLock);
2420                 osi_QAdd(&cm_allFileLocks, &fileLock->q);
2421                 lock_ReleaseWrite(&cm_scacheLock);
2422                 if (code != 0) *lockpp = fileLock;
2423         }
2424         return code;
2425 }
2426
2427 long cm_Unlock(cm_scache_t *scp, unsigned char LockType,
2428         LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
2429         cm_user_t *userp, cm_req_t *reqp)
2430 {
2431         long code = 0;
2432         int Which = ((LockType & 0x1) ? LockRead : LockWrite);
2433         AFSFid tfid;
2434         AFSVolSync volSync;
2435         cm_conn_t *connp;
2436         cm_file_lock_t *fileLock, *ourLock;
2437         osi_queue_t *q, *qq;
2438         int anotherReader = 0;
2439         int smallLock = 0;
2440         int found = 0;
2441
2442         if (LargeIntegerLessThan(LLength, scp->length))
2443                 smallLock = 1;
2444
2445         /* Look for our own lock on the list, so as to remove it.
2446          * Also, determine if we're the last reader; if not, avoid an RPC.
2447          */
2448         q = scp->fileLocks;
2449         while (q) {
2450                 fileLock = (cm_file_lock_t *)
2451             ((char *) q - offsetof(cm_file_lock_t, fileq));
2452                 if (!found
2453                     && fileLock->userp == userp
2454                     && LargeIntegerEqualTo(fileLock->LOffset, LOffset)
2455                     && LargeIntegerEqualTo(fileLock->LLength, LLength)) {
2456                         found = 1;
2457                         ourLock = fileLock;
2458                         qq = q;
2459                 }
2460                 else if (fileLock->LockType & 0x1)
2461                         anotherReader = 1;
2462                 q = osi_QNext(q);
2463         }
2464
2465         /* ignore byte ranges */
2466         if (smallLock && !found)
2467                 return 0;
2468
2469         /* don't try to unlock other people's locks */
2470         if (!found)
2471                 return CM_ERROR_WOULDBLOCK;
2472
2473         /* discard lock record */
2474         osi_QRemove(&scp->fileLocks, qq);
2475         /*
2476          * Don't delete it here; let the daemon delete it, to simplify
2477          * the daemon's traversal of the list.
2478          */
2479         lock_ObtainWrite(&cm_scacheLock);
2480         ourLock->flags |= CM_FILELOCK_FLAG_INVALID;
2481         cm_ReleaseUser(ourLock->userp);
2482         lock_ReleaseWrite(&cm_scacheLock);
2483
2484         if (!anotherReader) {
2485                 tfid.Volume = scp->fid.volume;
2486                 tfid.Vnode = scp->fid.vnode;
2487                 tfid.Unique = scp->fid.unique;
2488                 lock_ReleaseMutex(&scp->mx);
2489                 do {
2490                         code = cm_Conn(&scp->fid, userp, reqp, &connp);
2491                         if (code) 
2492                 break;
2493                         code = RXAFS_ReleaseLock(connp->callp, &tfid, &volSync);
2494                 } while (cm_Analyze(connp, userp, reqp, &scp->fid, &volSync,
2495                                     NULL, code));
2496                 code = cm_MapRPCError(code, reqp);
2497                 lock_ObtainMutex(&scp->mx);
2498         }
2499
2500         return code;
2501 }
2502
2503 void cm_CheckLocks()
2504 {
2505         osi_queue_t *q, *nq;
2506         cm_file_lock_t *fileLock;
2507         cm_req_t req;
2508         AFSFid tfid;
2509         AFSVolSync volSync;
2510         cm_conn_t *connp;
2511         long code;
2512
2513         cm_InitReq(&req);
2514
2515         lock_ObtainWrite(&cm_scacheLock);
2516         q = cm_allFileLocks;
2517         while (q) {
2518                 fileLock = (cm_file_lock_t *) q;
2519                 nq = osi_QNext(q);
2520                 if (fileLock->flags & CM_FILELOCK_FLAG_INVALID) {
2521                         osi_QRemove(&cm_allFileLocks, q);
2522                         free(fileLock);
2523                 }
2524                 else if (!(fileLock->flags & CM_FILELOCK_FLAG_WAITING)) {
2525                         tfid.Volume = fileLock->fid.volume;
2526                         tfid.Vnode = fileLock->fid.vnode;
2527                         tfid.Unique = fileLock->fid.unique;
2528                         lock_ReleaseWrite(&cm_scacheLock);
2529                         do {
2530                                 code = cm_Conn(&fileLock->fid, fileLock->userp,
2531                                                 &req, &connp);
2532                                 if (code) break;
2533                                 code = RXAFS_ExtendLock(connp->callp, &tfid,
2534                                                         &volSync);
2535                         } while (cm_Analyze(connp, fileLock->userp, &req,
2536                                             &fileLock->fid, &volSync, NULL,
2537                                             code));
2538                         code = cm_MapRPCError(code, &req);
2539                         lock_ObtainWrite(&cm_scacheLock);
2540                 }
2541                 q = nq;
2542         }
2543         lock_ReleaseWrite(&cm_scacheLock);
2544 }
2545
2546 long cm_RetryLock(cm_file_lock_t *oldFileLock, int vcp_is_dead)
2547 {
2548         long code;
2549         int Which = ((oldFileLock->LockType & 0x1) ? LockRead : LockWrite);
2550         cm_scache_t *scp;
2551         AFSFid tfid;
2552         AFSVolSync volSync;
2553         cm_conn_t *connp;
2554         cm_file_lock_t *fileLock;
2555         osi_queue_t *q;
2556         cm_req_t req;
2557         int found = 0;
2558
2559         if (vcp_is_dead) {
2560                 code = CM_ERROR_TIMEDOUT;
2561                 goto handleCode;
2562         }
2563
2564         cm_InitReq(&req);
2565
2566         /* Look for a conflict.  Also, if we are asking for a shared lock,
2567          * look for another shared lock, so we don't have to do an RPC.
2568          */
2569         code = cm_GetSCache(&oldFileLock->fid, &scp, oldFileLock->userp, &req);
2570         if (code)
2571                 return code;
2572
2573         q = scp->fileLocks;
2574         while (q) {
2575                 fileLock = (cm_file_lock_t *)
2576                                 ((char *) q - offsetof(cm_file_lock_t, fileq));
2577                 if ((fileLock->flags &
2578                         (CM_FILELOCK_FLAG_INVALID | CM_FILELOCK_FLAG_WAITING))
2579                     == 0) {
2580                         if ((oldFileLock->LockType & 0x1) == 0
2581                             || (fileLock->LockType & 0x1) == 0) {
2582                                 cm_ReleaseSCache(scp);
2583                                 return CM_ERROR_WOULDBLOCK;
2584                         }
2585                         found = 1;
2586                 }
2587                 q = osi_QNext(q);
2588         }
2589
2590         if (found)
2591                 code = 0;
2592         else {
2593                 tfid.Volume = oldFileLock->fid.volume;
2594                 tfid.Vnode = oldFileLock->fid.vnode;
2595                 tfid.Unique = oldFileLock->fid.unique;
2596                 do {
2597                         code = cm_Conn(&oldFileLock->fid, oldFileLock->userp,
2598                                        &req, &connp);
2599                         if (code) break;
2600                         code = RXAFS_SetLock(connp->callp, &tfid, Which,
2601                                              &volSync);
2602                 } while (cm_Analyze(connp, oldFileLock->userp, &req,
2603                                     &oldFileLock->fid, &volSync,
2604                                     NULL, code));
2605                 code = cm_MapRPCError(code, &req);
2606         }
2607
2608   handleCode:
2609         if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
2610                 lock_ObtainMutex(&scp->mx);
2611                 osi_QRemove(&scp->fileLocks, &oldFileLock->fileq);
2612                 lock_ReleaseMutex(&scp->mx);
2613         }
2614         lock_ObtainWrite(&cm_scacheLock);
2615         if (code == 0)
2616                 oldFileLock->flags = 0;
2617         else if (code != CM_ERROR_WOULDBLOCK) {
2618                 oldFileLock->flags |= CM_FILELOCK_FLAG_INVALID;
2619                 cm_ReleaseUser(oldFileLock->userp);
2620         oldFileLock->userp = NULL;
2621         }
2622         lock_ReleaseWrite(&cm_scacheLock);
2623
2624         return code;
2625 }