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