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