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