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