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