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