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