Windows: cm_Rename break in 9cf7a628c2ff178b2fa236d2c0a41ca9be315036
[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 <afsconfig.h>
11 #include <afs/param.h>
12 #include <roken.h>
13
14 #include <afs/stds.h>
15
16 #include <windows.h>
17 #include <winsock2.h>
18 #include <stddef.h>
19 #include <malloc.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <errno.h>
23
24 #include <osi.h>
25
26 #include "afsd.h"
27 #include "smb.h"
28 #include "cm_btree.h"
29
30 #include <strsafe.h>
31
32 #ifdef DEBUG
33 extern void afsi_log(char *pattern, ...);
34 #endif
35
36 int cm_enableServerLocks = 1;
37
38 int cm_followBackupPath = 0;
39
40 /*
41  * Case-folding array.  This was constructed by inspecting of SMBtrace output.
42  * I do not know anything more about it.
43  */
44 unsigned char cm_foldUpper[256] = {
45      0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,
46      0x8,  0x9,  0xa,  0xb,  0xc,  0xd,  0xe,  0xf,
47     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
48     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
49     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
50     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
52     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
53     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
54     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
55     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
56     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
57     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
58     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
59     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
60     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
61     0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80,
62     0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f,
63     0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55,
64     0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
65     0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0x56, 0xa7,
66     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
67     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
68     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
69     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
70     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
71     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
72     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
73     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
74     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
75     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
76     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
77 };
78
79 /*
80  * Case-insensitive string comparison.  We used to use stricmp, but it doesn't
81  * know about 8-bit characters (e.g. 129 is lowercase u-umlaut, 154 is
82  * upper-case u-umlaut).
83  */
84 int cm_stricmp(const char *str1, const char *str2)
85 {
86     char c1, c2;
87
88     while (1) {
89         if (*str1 == 0)
90             if (*str2 == 0)
91                 return 0;
92             else
93                 return -1;
94         if (*str2 == 0)
95             return 1;
96         c1 = (char) cm_foldUpper[(unsigned char)(*str1++)];
97         c2 = (char) cm_foldUpper[(unsigned char)(*str2++)];
98         if (c1 < c2)
99             return -1;
100         if (c1 > c2)
101             return 1;
102     }
103 }
104
105
106
107 /* return success if we can open this file in this mode */
108 long cm_CheckOpen(cm_scache_t *scp, int openMode, int trunc, cm_user_t *userp,
109                   cm_req_t *reqp)
110 {
111     long rights;
112     long code;
113
114     rights = 0;
115     if (openMode != 1)
116         rights |= PRSFS_READ;
117     if (openMode == 1 || openMode == 2 || trunc)
118         rights |= PRSFS_WRITE;
119
120     lock_ObtainWrite(&scp->rw);
121
122     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
123                       CM_SCACHESYNC_GETSTATUS
124                      | CM_SCACHESYNC_NEEDCALLBACK
125                      | CM_SCACHESYNC_LOCK);
126
127     if (code == 0 &&
128         ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
129         scp->fileType == CM_SCACHETYPE_FILE) {
130
131         cm_key_t key;
132         unsigned int sLockType;
133         LARGE_INTEGER LOffset, LLength;
134
135         /* Check if there's some sort of lock on the file at the
136            moment. */
137
138         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
139
140         if (rights & PRSFS_WRITE)
141             sLockType = 0;
142         else
143             sLockType = LOCKING_ANDX_SHARED_LOCK;
144
145         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
146         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
147         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
148         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
149
150         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
151
152         if (code == 0) {
153             cm_Unlock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp);
154         } else {
155             /* In this case, we allow the file open to go through even
156                though we can't enforce mandatory locking on the
157                file. */
158             if (code == CM_ERROR_NOACCESS &&
159                 !(rights & PRSFS_WRITE))
160                 code = 0;
161             else {
162                 if (code == CM_ERROR_LOCK_NOT_GRANTED)
163                     code = CM_ERROR_SHARING_VIOLATION;
164             }
165         }
166
167     } else if (code != 0) {
168         goto _done;
169     }
170
171     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
172
173  _done:
174
175     lock_ReleaseWrite(&scp->rw);
176
177     return code;
178 }
179
180 /* return success if we can open this file in this mode */
181 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
182                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
183                     cm_lock_data_t **ldpp)
184 {
185     long rights;
186     long code = 0;
187
188     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
189     *ldpp = NULL;
190
191     /* Ignore the SYNCHRONIZE privilege */
192     desiredAccess &= ~SYNCHRONIZE;
193
194     /* Always allow delete; the RPC will tell us if it's OK */
195     rights = 0;
196
197     if (desiredAccess == DELETE)
198         goto done_2;
199
200     /* Always allow reading attributes (Hidden, System, Readonly, ...) */
201     if (desiredAccess == FILE_READ_ATTRIBUTES)
202         goto done_2;
203
204     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
205         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
206
207     /* We used to require PRSFS_WRITE if createDisp was 4
208        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
209        However, we don't need to do that since the existence of the
210        scp implies that we don't need to create it. */
211     if (desiredAccess & AFS_ACCESS_WRITE)
212         rights |= PRSFS_WRITE;
213
214     if (desiredAccess & DELETE)
215         rights |= PRSFS_DELETE;
216
217     lock_ObtainWrite(&scp->rw);
218
219     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
220                       CM_SCACHESYNC_GETSTATUS
221                      | CM_SCACHESYNC_NEEDCALLBACK
222                      | CM_SCACHESYNC_LOCK);
223
224     /*
225      * If the open will fail because the volume is readonly, then we will
226      * return an access denied error instead.  This is to help brain-dead
227      * apps run correctly on replicated volumes.
228      * See defect 10007 for more information.
229      */
230     if (code == CM_ERROR_READONLY)
231         code = CM_ERROR_NOACCESS;
232
233     if (code == 0 &&
234              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
235              scp->fileType == CM_SCACHETYPE_FILE) {
236         cm_key_t key;
237         unsigned int sLockType;
238         LARGE_INTEGER LOffset, LLength;
239
240         /* Check if there's some sort of lock on the file at the
241            moment. */
242
243         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
244         if (rights & PRSFS_WRITE)
245             sLockType = 0;
246         else
247             sLockType = LOCKING_ANDX_SHARED_LOCK;
248
249         /* single byte lock at offset 0x0100 0000 0000 0000 */
250         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
251         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
252         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
253         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
254
255         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
256
257         if (code == 0) {
258             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
259             if (!*ldpp) {
260                 code = ENOMEM;
261                 goto _done;
262             }
263
264             (*ldpp)->key = key;
265             (*ldpp)->sLockType = sLockType;
266             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
267             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
268             (*ldpp)->LLength.HighPart = LLength.HighPart;
269             (*ldpp)->LLength.LowPart = LLength.LowPart;
270         } else {
271             /* In this case, we allow the file open to go through even
272                though we can't enforce mandatory locking on the
273                file. */
274             if (code == CM_ERROR_NOACCESS &&
275                 !(rights & PRSFS_WRITE))
276                 code = 0;
277             else {
278                 if (code == CM_ERROR_LOCK_NOT_GRANTED)
279                     code = CM_ERROR_SHARING_VIOLATION;
280             }
281         }
282     } else if (code != 0) {
283         goto _done;
284     }
285
286  _done:
287     lock_ReleaseWrite(&scp->rw);
288
289  done_2:
290     osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
291     return code;
292 }
293
294 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
295                                cm_lock_data_t ** ldpp)
296 {
297     osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
298     lock_ObtainWrite(&scp->rw);
299     if (*ldpp) {
300         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
301                   (*ldpp)->key, 0, userp, reqp);
302         free(*ldpp);
303         *ldpp = NULL;
304     }
305     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
306     lock_ReleaseWrite(&scp->rw);
307     return 0;
308 }
309 /*
310  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
311  * done in three steps:
312  * (1) open for deletion (NT_CREATE_AND_X)
313  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
314  * (3) close (CLOSE)
315  * We must not do the RPC until step 3.  But if we are going to return an error
316  * code (e.g. directory not empty), we must return it by step 2, otherwise most
317  * clients will not notice it.  So we do a preliminary check.  For deleting
318  * files, this is almost free, since we have already done the RPC to get the
319  * parent directory's status bits.  But for deleting directories, we must do an
320  * additional RPC to get the directory's data to check if it is empty.  Sigh.
321  */
322 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
323         cm_req_t *reqp)
324 {
325     long code;
326     osi_hyper_t thyper;
327     cm_buf_t *bufferp;
328     cm_dirEntry_t *dep = 0;
329     unsigned short *hashTable;
330     unsigned int i, idx;
331     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
332     int releaseLock = 0;
333
334     /* First check permissions */
335     lock_ObtainWrite(&scp->rw);
336     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
337                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
338     if (!code)
339         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
340     lock_ReleaseWrite(&scp->rw);
341     if (code)
342         return code;
343
344     /* If deleting directory, must be empty */
345
346     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
347         return code;
348
349     thyper.HighPart = 0; thyper.LowPart = 0;
350     code = buf_Get(scp, &thyper, reqp, &bufferp);
351     if (code)
352         return code;
353
354     lock_ObtainMutex(&bufferp->mx);
355     lock_ObtainWrite(&scp->rw);
356     releaseLock = 1;
357     while (1) {
358         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
359                           CM_SCACHESYNC_NEEDCALLBACK
360                           | CM_SCACHESYNC_READ
361                           | CM_SCACHESYNC_BUFLOCKED);
362         if (code)
363             goto done;
364
365         if (cm_HaveBuffer(scp, bufferp, 1))
366             break;
367
368         /* otherwise, load the buffer and try again */
369         lock_ReleaseMutex(&bufferp->mx);
370         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
371         lock_ReleaseWrite(&scp->rw);
372         lock_ObtainMutex(&bufferp->mx);
373         lock_ObtainWrite(&scp->rw);
374         cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
375         if (code)
376             goto done;
377     }
378
379     lock_ReleaseWrite(&scp->rw);
380     releaseLock = 0;
381
382     /* We try to determine emptiness without looking beyond the first page,
383      * and without assuming "." and ".." are present and are on the first
384      * page (though these assumptions might, after all, be reasonable).
385      */
386     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
387     for (i=0; i<128; i++) {
388         idx = ntohs(hashTable[i]);
389         while (idx) {
390             if (idx >= 64) {
391                 BeyondPage = 1;
392                 break;
393             }
394             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
395             if (strcmp(dep->name, ".") == 0)
396                 HaveDot = 1;
397             else if (strcmp(dep->name, "..") == 0)
398                 HaveDotDot = 1;
399             else {
400                 code = CM_ERROR_NOTEMPTY;
401                 goto done;
402             }
403             idx = ntohs(dep->next);
404         }
405     }
406     if (BeyondPage && HaveDot && HaveDotDot)
407         code = CM_ERROR_NOTEMPTY;
408     else
409         code = 0;
410   done:
411     lock_ReleaseMutex(&bufferp->mx);
412     buf_Release(bufferp);
413     if (releaseLock)
414         lock_ReleaseWrite(&scp->rw);
415     return code;
416 }
417
418 /*
419  * Iterate through all entries in a directory.
420  * When the function funcp is called, the buffer is locked but the
421  * directory vnode is not.
422  *
423  * If the retscp parameter is not NULL, the parmp must be a
424  * cm_lookupSearch_t object.
425  */
426 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
427                  osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
428                  cm_scache_t **retscp)
429 {
430     char *tp;
431     long code;
432     cm_dirEntry_t *dep = 0;
433     cm_buf_t *bufferp;
434     long temp;
435     osi_hyper_t dirLength;
436     osi_hyper_t bufferOffset;
437     osi_hyper_t curOffset;
438     osi_hyper_t thyper;
439     long entryInDir;
440     long entryInBuffer;
441     cm_pageHeader_t *pageHeaderp;
442     int slotInPage;
443     long nextEntryCookie;
444     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
445
446     /* get the directory size */
447     lock_ObtainWrite(&scp->rw);
448     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
449                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
450     lock_ReleaseWrite(&scp->rw);
451     if (code)
452         return code;
453
454     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
455         return CM_ERROR_NOTDIR;
456
457     if (retscp)                         /* if this is a lookup call */
458     {
459         cm_lookupSearch_t*      sp = parmp;
460
461         if (
462 #ifdef AFS_FREELANCE_CLIENT
463         /* Freelance entries never end up in the DNLC because they
464          * do not have an associated cm_server_t
465          */
466             !(cm_freelanceEnabled &&
467             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
468               sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
469 #else /* !AFS_FREELANCE_CLIENT */
470             TRUE
471 #endif
472             )
473         {
474             int casefold = sp->caseFold;
475             sp->caseFold = 0; /* we have a strong preference for exact matches */
476             if ( *retscp = cm_dnlcLookup(scp, sp))      /* dnlc hit */
477             {
478                 sp->caseFold = casefold;
479                 return 0;
480             }
481             sp->caseFold = casefold;
482         }
483
484         /*
485          * see if we can find it using the directory hash tables.
486          * we can only do exact matches, since the hash is case
487          * sensitive.
488          */
489         if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
490         {
491             cm_dirOp_t dirop;
492 #ifdef USE_BPLUS
493             int usedBplus = 0;
494 #endif
495
496             code = ENOENT;
497
498             code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
499                                  CM_DIROP_FLAG_NONE, &dirop);
500             if (code == 0) {
501
502 #ifdef USE_BPLUS
503                 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
504                 if (code != EINVAL)
505                     usedBplus = 1;
506                 else
507 #endif
508                     code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
509
510                 cm_EndDirOp(&dirop);
511             }
512
513             if (code == 0) {
514                 /* found it */
515                 sp->found = TRUE;
516                 sp->ExactFound = TRUE;
517                 *retscp = NULL; /* force caller to call cm_GetSCache() */
518                 return 0;
519             }
520 #ifdef USE_BPLUS
521             if (usedBplus) {
522                 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
523                     /* found it */
524                     sp->found = TRUE;
525                     sp->ExactFound = FALSE;
526                     *retscp = NULL; /* force caller to call cm_GetSCache() */
527                     return 0;
528                 }
529
530                 return CM_ERROR_BPLUS_NOMATCH;
531             }
532 #endif
533         }
534     }
535
536     /*
537      * XXX We only get the length once.  It might change when we drop the
538      * lock.
539      */
540     dirLength = scp->length;
541
542     bufferp = NULL;
543     bufferOffset.LowPart = bufferOffset.HighPart = 0;
544     if (startOffsetp)
545         curOffset = *startOffsetp;
546     else {
547         curOffset.HighPart = 0;
548         curOffset.LowPart = 0;
549     }
550
551     while (1) {
552         /* make sure that curOffset.LowPart doesn't point to the first
553          * 32 bytes in the 2nd through last dir page, and that it
554          * doesn't point at the first 13 32-byte chunks in the first
555          * dir page, since those are dir and page headers, and don't
556          * contain useful information.
557          */
558         temp = curOffset.LowPart & (2048-1);
559         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
560             /* we're in the first page */
561             if (temp < 13*32) temp = 13*32;
562         }
563         else {
564             /* we're in a later dir page */
565             if (temp < 32) temp = 32;
566         }
567
568         /* make sure the low order 5 bits are zero */
569         temp &= ~(32-1);
570
571         /* now put temp bits back ito curOffset.LowPart */
572         curOffset.LowPart &= ~(2048-1);
573         curOffset.LowPart |= temp;
574
575         /* check if we've passed the dir's EOF */
576         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
577             break;
578
579         /* see if we can use the bufferp we have now; compute in which
580          * page the current offset would be, and check whether that's
581          * the offset of the buffer we have.  If not, get the buffer.
582          */
583         thyper.HighPart = curOffset.HighPart;
584         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
585         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
586             /* wrong buffer */
587             if (bufferp) {
588                 lock_ReleaseMutex(&bufferp->mx);
589                 buf_Release(bufferp);
590                 bufferp = NULL;
591             }
592
593             code = buf_Get(scp, &thyper, reqp, &bufferp);
594             if (code) {
595                 /* if buf_Get() fails we do not have a buffer object to lock */
596                 bufferp = NULL;
597                 break;
598             }
599
600             lock_ObtainMutex(&bufferp->mx);
601             bufferOffset = thyper;
602
603             /* now get the data in the cache */
604             while (1) {
605                 lock_ObtainWrite(&scp->rw);
606                 code = cm_SyncOp(scp, bufferp, userp, reqp,
607                                   PRSFS_LOOKUP,
608                                   CM_SCACHESYNC_NEEDCALLBACK
609                                   | CM_SCACHESYNC_READ
610                                   | CM_SCACHESYNC_BUFLOCKED);
611                 if (code) {
612                     lock_ReleaseWrite(&scp->rw);
613                     break;
614                 }
615                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
616
617                 if (cm_HaveBuffer(scp, bufferp, 1)) {
618                     lock_ReleaseWrite(&scp->rw);
619                     break;
620                 }
621
622                 /* otherwise, load the buffer and try again */
623                 lock_ReleaseMutex(&bufferp->mx);
624                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
625                                     reqp);
626                 lock_ReleaseWrite(&scp->rw);
627                 lock_ObtainMutex(&bufferp->mx);
628                 if (code)
629                     break;
630             }
631             if (code) {
632                 lock_ReleaseMutex(&bufferp->mx);
633                 buf_Release(bufferp);
634                 bufferp = NULL;
635                 break;
636             }
637         }       /* if (wrong buffer) ... */
638
639         /* now we have the buffer containing the entry we're interested
640          * in; copy it out if it represents a non-deleted entry.
641          */
642         entryInDir = curOffset.LowPart & (2048-1);
643         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
644
645         /* page header will help tell us which entries are free.  Page
646          * header can change more often than once per buffer, since
647          * AFS 3 dir page size may be less than (but not more than) a
648          * buffer package buffer.
649          */
650         /* only look intra-buffer */
651         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
652         temp &= ~(2048 - 1);    /* turn off intra-page bits */
653         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
654
655         /* now determine which entry we're looking at in the page.  If
656          * it is free (there's a free bitmap at the start of the dir),
657          * we should skip these 32 bytes.
658          */
659         slotInPage = (entryInDir & 0x7e0) >> 5;
660         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
661                & (1 << (slotInPage & 0x7)))) {
662             /* this entry is free */
663             numDirChunks = 1;   /* only skip this guy */
664             goto nextEntry;
665         }
666
667         tp = bufferp->datap + entryInBuffer;
668         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
669
670         /*
671          * here are some consistency checks
672          */
673         if (dep->flag != CM_DIR_FFIRST ||
674             strlen(dep->name) > 256) {
675             code = CM_ERROR_INVAL;
676             osi_Log2(afsd_logp,
677                      "cm_ApplyDir invalid directory entry for scp %p bufp %p",
678                      scp, bufferp);
679             osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
680                      scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
681             bufferp->dataVersion = CM_BUF_VERSION_BAD;
682             break;
683         }
684
685         /* while we're here, compute the next entry's location, too,
686          * since we'll need it when writing out the cookie into the
687          * dir listing stream.
688          */
689         numDirChunks = cm_NameEntries(dep->name, NULL);
690
691         /* compute the offset of the cookie representing the next entry */
692         nextEntryCookie = curOffset.LowPart
693             + (CM_DIR_CHUNKSIZE * numDirChunks);
694
695         if (dep->fid.vnode != 0) {
696             /* this is one of the entries to use: it is not deleted */
697             code = (*funcp)(scp, dep, parmp, &curOffset);
698             if (code)
699                 break;
700         }       /* if we're including this name */
701
702       nextEntry:
703         /* and adjust curOffset to be where the new cookie is */
704         thyper.HighPart = 0;
705         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
706         curOffset = LargeIntegerAdd(thyper, curOffset);
707     }           /* while copying data for dir listing */
708
709     /* release the mutex */
710     if (bufferp) {
711         lock_ReleaseMutex(&bufferp->mx);
712         buf_Release(bufferp);
713     }
714     return code;
715 }
716
717 int cm_NoneUpper(normchar_t *s)
718 {
719     normchar_t c;
720     while (c = *s++)
721         if (c >= 'A' && c <= 'Z')
722             return 0;
723     return 1;
724 }
725
726 int cm_NoneLower(normchar_t *s)
727 {
728     normchar_t c;
729     while (c = *s++)
730         if (c >= 'a' && c <= 'z')
731             return 0;
732     return 1;
733 }
734
735 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
736                          osi_hyper_t *offp)
737 {
738     cm_lookupSearch_t *sp;
739     int match;
740     normchar_t matchName[MAX_PATH];
741     int looking_for_short_name = FALSE;
742
743     sp = (cm_lookupSearch_t *) rockp;
744
745     if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
746         /* Can't normalize FS string. */
747         return 0;
748     }
749
750     if (sp->caseFold)
751         match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
752     else
753         match = cm_NormStrCmp(matchName, sp->nsearchNamep);
754
755     if (match != 0
756         && sp->hasTilde
757         && !cm_Is8Dot3(matchName)) {
758
759         cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
760         if (sp->caseFold)
761             match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
762         else
763             match = cm_NormStrCmp(matchName, sp->nsearchNamep);
764         looking_for_short_name = TRUE;
765     }
766
767     if (match != 0)
768         return 0;
769
770     sp->found = 1;
771     if (!sp->caseFold)
772         sp->ExactFound = 1;
773
774     if (!sp->caseFold || looking_for_short_name) {
775         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
776         return CM_ERROR_STOPNOW;
777     }
778
779     /*
780      * If we get here, we are doing a case-insensitive search, and we
781      * have found a match.  Now we determine what kind of match it is:
782      * exact, lower-case, upper-case, or none of the above.  This is done
783      * in order to choose among matches, if there are more than one.
784      */
785
786     /* Exact matches are the best. */
787     match = cm_NormStrCmp(matchName, sp->nsearchNamep);
788     if (match == 0) {
789         sp->ExactFound = 1;
790         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
791         return CM_ERROR_STOPNOW;
792     }
793
794     /* Lower-case matches are next. */
795     if (sp->LCfound)
796         return 0;
797     if (cm_NoneUpper(matchName)) {
798         sp->LCfound = 1;
799         goto inexact;
800     }
801
802     /* Upper-case matches are next. */
803     if (sp->UCfound)
804         return 0;
805     if (cm_NoneLower(matchName)) {
806         sp->UCfound = 1;
807         goto inexact;
808     }
809
810     /* General matches are last. */
811     if (sp->NCfound)
812         return 0;
813     sp->NCfound = 1;
814
815   inexact:
816     cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
817     return 0;
818 }
819
820 /* read the contents of a mount point into the appropriate string.
821  * called with write locked scp, and returns with locked scp.
822  */
823 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
824 {
825     long code;
826
827     if (scp->mountPointStringp[0])
828         return 0;
829
830 #ifdef AFS_FREELANCE_CLIENT
831     /* File servers do not have data for freelance entries */
832     if (cm_freelanceEnabled &&
833         scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
834         scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
835     {
836         code = cm_FreelanceFetchMountPointString(scp);
837     } else
838 #endif /* AFS_FREELANCE_CLIENT */
839     {
840         char temp[MOUNTPOINTLEN];
841         osi_hyper_t offset;
842
843         /* otherwise, we have to read it in */
844         offset.LowPart = offset.HighPart = 0;
845         code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
846         if (code)
847             return code;
848
849         /*
850          * scp->length is the actual length of the mount point string.
851          * It is current because cm_GetData merged the most up to date
852          * status info into scp and has not dropped the rwlock since.
853          */
854         if (scp->length.LowPart > MOUNTPOINTLEN - 1)
855             return CM_ERROR_TOOBIG;
856         if (scp->length.LowPart == 0)
857             return CM_ERROR_INVAL;
858
859         /* convert the terminating dot to a NUL */
860         temp[scp->length.LowPart - 1] = 0;
861         memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
862     }
863
864     return code;
865 }
866
867
868 /* called with a locked scp and chases the mount point, yielding outScpp.
869  * scp remains write locked, just for simplicity of describing the interface.
870  */
871 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
872                          cm_req_t *reqp, cm_scache_t **outScpp)
873 {
874     fschar_t *cellNamep = NULL;
875     fschar_t *volNamep = NULL;
876     afs_uint32 code;
877     fschar_t *cp;
878     fschar_t *mpNamep;
879     cm_volume_t *volp = NULL;
880     cm_cell_t *cellp;
881     fschar_t mtType;
882     cm_fid_t tfid;
883     size_t vnLength;
884     int targetType;
885
886     *outScpp = NULL;
887
888     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
889         tfid = scp->mountRootFid;
890         lock_ReleaseWrite(&scp->rw);
891         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
892         lock_ObtainWrite(&scp->rw);
893         return code;
894     }
895
896     /* parse the volume name */
897     mpNamep = scp->mountPointStringp;
898     if (!mpNamep[0])
899         return CM_ERROR_NOSUCHPATH;
900     mtType = *scp->mountPointStringp;
901
902     cp = cm_FsStrChr(mpNamep, _FS(':'));
903     if (cp) {
904         /* cellular mount point */
905         cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
906         cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
907         volNamep = cm_FsStrDup(cp+1);
908
909         /* now look up the cell */
910         lock_ReleaseWrite(&scp->rw);
911         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
912         lock_ObtainWrite(&scp->rw);
913     } else {
914         /* normal mt pt */
915         volNamep = cm_FsStrDup(mpNamep + 1);
916
917 #ifdef AFS_FREELANCE_CLIENT
918         /*
919          * Mount points in the Freelance cell should default
920          * to the workstation cell.
921          */
922         if (cm_freelanceEnabled &&
923              scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
924              scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
925         {
926             fschar_t rootCellName[256]="";
927             cm_GetRootCellName(rootCellName);
928             cellp = cm_GetCell(rootCellName, 0);
929         } else
930 #endif /* AFS_FREELANCE_CLIENT */
931             cellp = cm_FindCellByID(scp->fid.cell, 0);
932     }
933
934     if (!cellp) {
935         code = CM_ERROR_NOSUCHCELL;
936         goto done;
937     }
938
939     vnLength = cm_FsStrLen(volNamep);
940     if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
941         targetType = BACKVOL;
942     else if (vnLength >= 10
943              && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
944         targetType = ROVOL;
945     else
946         targetType = RWVOL;
947
948     /* check for backups within backups */
949     if (targetType == BACKVOL
950          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
951          == CM_SCACHEFLAG_RO) {
952         code = CM_ERROR_NOSUCHVOLUME;
953         goto done;
954     }
955
956     /* now we need to get the volume */
957     lock_ReleaseWrite(&scp->rw);
958     if (cm_VolNameIsID(volNamep)) {
959         code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
960                                 CM_GETVOL_FLAG_CREATE, &volp);
961     } else {
962         code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
963                                   CM_GETVOL_FLAG_CREATE, &volp);
964     }
965     lock_ObtainWrite(&scp->rw);
966
967     if (code == 0) {
968         afs_uint32 cell, volume;
969         cm_vol_state_t *statep;
970
971         cell = cellp->cellID;
972
973         /* if the mt pt originates in a .backup volume (not a .readonly)
974          * and FollowBackupPath is active, and if there is a .backup
975          * volume for the target, then use the .backup of the target
976          * instead of the read-write.
977          */
978         if (cm_followBackupPath &&
979             volp->vol[BACKVOL].ID != 0 &&
980             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
981             (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
982             ) {
983             targetType = BACKVOL;
984         }
985         /* if the mt pt is in a read-only volume (not just a
986          * backup), and if there is a read-only volume for the
987          * target, and if this is a targetType '#' mount point, use
988          * the read-only, otherwise use the one specified.
989          */
990         else if (mtType == '#' && targetType == RWVOL &&
991                  (scp->flags & CM_SCACHEFLAG_PURERO) &&
992                  volp->vol[ROVOL].ID != 0) {
993             targetType = ROVOL;
994         }
995
996         lock_ObtainWrite(&volp->rw);
997         statep = cm_VolumeStateByType(volp, targetType);
998         volume = statep->ID;
999         statep->dotdotFid = dscp->fid;
1000         lock_ReleaseWrite(&volp->rw);
1001
1002         /* the rest of the fid is a magic number */
1003         cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1004         scp->mountRootGen = cm_data.mountRootGen;
1005
1006         tfid = scp->mountRootFid;
1007         lock_ReleaseWrite(&scp->rw);
1008         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1009         lock_ObtainWrite(&scp->rw);
1010     }
1011
1012   done:
1013     if (volp)
1014         cm_PutVolume(volp);
1015     if (cellNamep)
1016         free(cellNamep);
1017     if (volNamep)
1018         free(volNamep);
1019     return code;
1020 }
1021
1022 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1023                        cm_req_t *reqp, cm_scache_t **outScpp)
1024 {
1025     long code;
1026     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1027     cm_scache_t *tscp = NULL;
1028     cm_scache_t *mountedScp;
1029     cm_lookupSearch_t rock;
1030     int getroot;
1031     normchar_t *nnamep = NULL;
1032     fschar_t *fnamep = NULL;
1033     size_t fnlen;
1034
1035     *outScpp = NULL;
1036
1037     memset(&rock, 0, sizeof(rock));
1038
1039     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1040         && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1041         if (dscp->dotdotFid.volume == 0)
1042             return CM_ERROR_NOSUCHVOLUME;
1043         rock.fid = dscp->dotdotFid;
1044         goto haveFid;
1045     } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1046         rock.fid = dscp->fid;
1047         goto haveFid;
1048     }
1049
1050     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1051     if (!nnamep) {
1052         code = CM_ERROR_NOSUCHFILE;
1053         goto done;
1054     }
1055     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1056     if (!fnamep) {
1057         code = CM_ERROR_NOSUCHFILE;
1058         goto done;
1059     }
1060
1061 retry_lookup:
1062     if (flags & CM_FLAG_NOMOUNTCHASE) {
1063         /* In this case, we should go and call cm_Dir* functions
1064            directly since the following cm_ApplyDir() function will
1065            not. */
1066
1067         cm_dirOp_t dirop;
1068 #ifdef USE_BPLUS
1069         int usedBplus = 0;
1070 #endif
1071
1072         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1073                              CM_DIROP_FLAG_NONE, &dirop);
1074         if (code == 0) {
1075 #ifdef USE_BPLUS
1076             code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1077             if (code != EINVAL)
1078                 usedBplus = 1;
1079             else
1080 #endif
1081                 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1082
1083             cm_EndDirOp(&dirop);
1084         }
1085
1086         if (code == 0) {
1087             /* found it */
1088             rock.found = TRUE;
1089             goto haveFid;
1090         }
1091 #ifdef USE_BPLUS
1092         if (usedBplus) {
1093             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1094                 /* found it */
1095                 code = 0;
1096                 rock.found = TRUE;
1097                 goto haveFid;
1098             }
1099
1100             code = CM_ERROR_BPLUS_NOMATCH;
1101             goto notfound;
1102         }
1103 #endif
1104     }
1105
1106     rock.fid.cell = dscp->fid.cell;
1107     rock.fid.volume = dscp->fid.volume;
1108     rock.searchNamep = fnamep;
1109     rock.nsearchNamep = nnamep;
1110     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1111     rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1112
1113     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1114     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1115                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1116
1117     /* code == 0 means we fell off the end of the dir, while stopnow means
1118      * that we stopped early, probably because we found the entry we're
1119      * looking for.  Any other non-zero code is an error.
1120      */
1121     if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1122         /* if the cm_scache_t we are searching in is not a directory
1123          * we must return path not found because the error
1124          * is to describe the final component not an intermediary
1125          */
1126         if (code == CM_ERROR_NOTDIR) {
1127             if (flags & CM_FLAG_CHECKPATH)
1128                 code = CM_ERROR_NOSUCHPATH;
1129             else
1130                 code = CM_ERROR_NOSUCHFILE;
1131         }
1132         goto done;
1133     }
1134
1135 notfound:
1136     getroot = (dscp==cm_data.rootSCachep) ;
1137     if (!rock.found) {
1138         if (!cm_freelanceEnabled || !getroot) {
1139             if (flags & CM_FLAG_CHECKPATH)
1140                 code = CM_ERROR_NOSUCHPATH;
1141             else
1142                 code = CM_ERROR_NOSUCHFILE;
1143             goto done;
1144         }
1145         else if (!cm_ClientStrChr(cnamep, '#') &&
1146                  !cm_ClientStrChr(cnamep, '%') &&
1147                  cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1148                  cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1149                  cm_ClientStrCmpI(cnamep, _C("ipc$")))
1150         {
1151             /* nonexistent dir on freelance root, so add it */
1152             fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1153             int  found = 0;
1154             int  retry = 0;
1155
1156             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1157                      osi_LogSaveClientString(afsd_logp,cnamep));
1158
1159             /*
1160              * There is an ugly behavior where a share name "foo" will be searched
1161              * for as "fo".  If the searched for name differs by an already existing
1162              * symlink or mount point in the Freelance directory, do not add the
1163              * new value automatically.
1164              */
1165
1166             code = -1;
1167             fnlen = strlen(fnamep);
1168             if ( fnamep[fnlen-1] == '.') {
1169                 fnamep[fnlen-1] = '\0';
1170                 fnlen--;
1171                 retry = 1;
1172             }
1173
1174             if (cnamep[0] == '.') {
1175                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1176                     found = 1;
1177                     code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1178                     if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1179                         /*
1180                          * Do not permit symlinks that are one of:
1181                          *  . the cellname followed by a dot
1182                          *  . the cellname minus a single character
1183                          *  . a substring of the cellname that does not consist of full components
1184                          */
1185                         if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1186                              (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1187                         {
1188                             /* do not add; substitute fullname for the search */
1189                             free(fnamep);
1190                             fnamep = malloc(strlen(fullname)+2);
1191                             fnamep[0] = '.';
1192                             strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1193                             retry = 1;
1194                         } else {
1195                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1196                         }
1197                     }
1198                 }
1199             } else {
1200                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1201                     found = 1;
1202                     code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1203                     if ( cm_FsStrCmpI(fnamep, fullname)) {
1204                         /*
1205                          * Do not permit symlinks that are one of:
1206                          *  . the cellname followed by a dot
1207                          *  . the cellname minus a single character
1208                          *  . a substring of the cellname that does not consist of full components
1209                          */
1210                         if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1211                              (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1212                         {
1213                             /* do not add; substitute fullname for the search */
1214                                 free(fnamep);
1215                                 fnamep = strdup(fullname);
1216                                 code = 0;
1217                                 retry = 1;
1218                         } else {
1219                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1220                         }
1221                     }
1222                 }
1223             }
1224
1225             if (retry) {
1226                 if (nnamep)
1227                     free(nnamep);
1228                 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1229                 goto retry_lookup;
1230             }
1231
1232             if (!found || code) {   /* add mount point failed, so give up */
1233                 if (flags & CM_FLAG_CHECKPATH)
1234                     code = CM_ERROR_NOSUCHPATH;
1235                 else
1236                     code = CM_ERROR_NOSUCHFILE;
1237                 goto done;
1238             }
1239             tscp = NULL;   /* to force call of cm_GetSCache */
1240         } else {
1241             if (flags & CM_FLAG_CHECKPATH)
1242                 code = CM_ERROR_NOSUCHPATH;
1243             else
1244                 code = CM_ERROR_NOSUCHFILE;
1245             goto done;
1246         }
1247     }
1248
1249   haveFid:
1250     if ( !tscp )    /* we did not find it in the dnlc */
1251     {
1252         dnlcHit = 0;
1253         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1254         if (code)
1255             goto done;
1256     }
1257     /* tscp is now held */
1258
1259     lock_ObtainWrite(&tscp->rw);
1260
1261     /*
1262      * Do not get status if we do not already have a callback.
1263      * The process of reading the mount point string will obtain status information
1264      * in a single RPC.  No reason to add a second round trip.
1265      *
1266      * If we do have a callback, use cm_SyncOp to get status in case the
1267      * current cm_user_t is not the same as the one that obtained the
1268      * mount point string contents.
1269      */
1270     if (cm_HaveCallback(tscp)) {
1271         code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1272                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1273         if (code) {
1274             lock_ReleaseWrite(&tscp->rw);
1275             cm_ReleaseSCache(tscp);
1276             goto done;
1277         }
1278         cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1279     }
1280     /* tscp is now locked */
1281
1282     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1283          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1284         /* mount points are funny: they have a volume name to mount
1285          * the root of.
1286          */
1287         code = cm_ReadMountPoint(tscp, userp, reqp);
1288         if (code == 0)
1289             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1290                                        &mountedScp);
1291         lock_ReleaseWrite(&tscp->rw);
1292         cm_ReleaseSCache(tscp);
1293         if (code)
1294             goto done;
1295
1296         tscp = mountedScp;
1297     }
1298     else {
1299         lock_ReleaseWrite(&tscp->rw);
1300     }
1301
1302     /* copy back pointer */
1303     *outScpp = tscp;
1304
1305     /* insert scache in dnlc */
1306     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1307         /* lock the directory entry to prevent racing callback revokes */
1308         lock_ObtainRead(&dscp->rw);
1309         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1310             /* TODO: reuse nnamep from above */
1311             if (nnamep)
1312                 free(nnamep);
1313             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1314             if (nnamep)
1315                 cm_dnlcEnter(dscp, nnamep, tscp);
1316         }
1317         lock_ReleaseRead(&dscp->rw);
1318     }
1319
1320     /* and return */
1321   done:
1322     if (fnamep) {
1323         free (fnamep);
1324         fnamep = NULL;
1325     }
1326     if (nnamep) {
1327         free (nnamep);
1328         nnamep = NULL;
1329     }
1330
1331     return code;
1332 }
1333
1334 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1335 {
1336     clientchar_t *tp;
1337     int prefixCount;
1338 #ifdef _WIN64
1339     int use_sysname64 = 0;
1340
1341     if (cm_sysName64Count > 0 && reqp && (reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1342         use_sysname64 = 1;
1343 #endif
1344
1345     tp = cm_ClientStrRChr(inp, '@');
1346     if (tp == NULL)
1347         return 0;               /* no @sys */
1348
1349     if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1350         return 0;       /* no @sys */
1351
1352     /* caller just wants to know if this is a valid @sys type of name */
1353     if (outp == NULL)
1354         return 1;
1355
1356 #ifdef _WIN64
1357     if (use_sysname64 && index >= cm_sysName64Count)
1358         return -1;
1359     else
1360 #endif
1361     if (index >= cm_sysNameCount)
1362         return -1;
1363
1364     /* otherwise generate the properly expanded @sys name */
1365     prefixCount = (int)(tp - inp);
1366
1367     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);       /* copy out "a." from "a.@sys" */
1368     outp[prefixCount] = 0;                                      /* null terminate the "a." */
1369 #ifdef _WIN64
1370     if (use_sysname64)
1371         cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1372     else
1373 #endif
1374         cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1375
1376     return 1;
1377 }
1378
1379 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1380                                 cm_req_t *reqp, cm_scache_t ** outScpp)
1381 {
1382     afs_uint32    code = 0;
1383     fschar_t      cellName[CELL_MAXNAMELEN];
1384     fschar_t      volumeName[VL_MAXNAMELEN];
1385     size_t        len;
1386     fschar_t *        cp;
1387     fschar_t *        tp;
1388     fschar_t *        fnamep = NULL;
1389
1390     cm_cell_t *   cellp = NULL;
1391     cm_volume_t * volp = NULL;
1392     cm_fid_t      fid;
1393     afs_uint32    volume;
1394     int           volType;
1395     int           mountType = RWVOL;
1396
1397     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1398              osi_LogSaveClientString(afsd_logp, namep));
1399
1400     if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1401         goto _exit_invalid_path;
1402     }
1403
1404     /* namep is assumed to look like the following:
1405
1406        @vol:<cellname>%<volume>\0
1407        or
1408        @vol:<cellname>#<volume>\0
1409
1410      */
1411
1412     fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1413     cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1414     tp = cm_FsStrChr(cp, '%');
1415     if (tp == NULL)
1416         tp = cm_FsStrChr(cp, '#');
1417     if (tp == NULL ||
1418         (len = tp - cp) == 0 ||
1419         len > CELL_MAXNAMELEN)
1420         goto _exit_invalid_path;
1421     cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1422
1423     if (*tp == '#')
1424         mountType = ROVOL;
1425
1426     cp = tp+1;                  /* cp now points to volume, supposedly */
1427     cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1428
1429     /* OK, now we have the cell and the volume */
1430     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1431              osi_LogSaveFsString(afsd_logp, cellName),
1432              osi_LogSaveFsString(afsd_logp, volumeName));
1433
1434     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1435     if (cellp == NULL) {
1436         goto _exit_invalid_path;
1437     }
1438
1439     len = cm_FsStrLen(volumeName);
1440     if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1441         volType = BACKVOL;
1442     else if (len >= 10 &&
1443              cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1444         volType = ROVOL;
1445     else
1446         volType = RWVOL;
1447
1448     if (cm_VolNameIsID(volumeName)) {
1449         code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1450                                 CM_GETVOL_FLAG_CREATE, &volp);
1451     } else {
1452         code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1453                                   CM_GETVOL_FLAG_CREATE, &volp);
1454     }
1455
1456     if (code != 0)
1457         goto _exit_cleanup;
1458
1459     if (volType == BACKVOL)
1460         volume = volp->vol[BACKVOL].ID;
1461     else if (volType == ROVOL ||
1462              (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1463         volume = volp->vol[ROVOL].ID;
1464     else
1465         volume = volp->vol[RWVOL].ID;
1466
1467     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1468
1469     code = cm_GetSCache(&fid, outScpp, userp, reqp);
1470
1471   _exit_cleanup:
1472     if (fnamep)
1473         free(fnamep);
1474
1475     if (volp)
1476         cm_PutVolume(volp);
1477
1478     if (code == 0)
1479         return code;
1480
1481  _exit_invalid_path:
1482     if (flags & CM_FLAG_CHECKPATH)
1483         return CM_ERROR_NOSUCHPATH;
1484     else
1485         return CM_ERROR_NOSUCHFILE;
1486 }
1487
1488 #ifdef DEBUG_REFCOUNT
1489 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1490                cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1491 #else
1492 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1493                cm_req_t *reqp, cm_scache_t **outScpp)
1494 #endif
1495 {
1496     long code;
1497     clientchar_t tname[AFSPATHMAX];
1498     int sysNameIndex = 0;
1499     cm_scache_t *scp = NULL;
1500
1501 #ifdef DEBUG_REFCOUNT
1502     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1503     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1504 #endif
1505
1506     if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1507         if (flags & CM_FLAG_CHECKPATH)
1508             return CM_ERROR_NOSUCHPATH;
1509         else
1510             return CM_ERROR_NOSUCHFILE;
1511     }
1512
1513     if (dscp == cm_data.rootSCachep &&
1514         cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1515         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1516     }
1517
1518     if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1519         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1520             code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1521             if (code > 0) {
1522                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1523 #ifdef DEBUG_REFCOUNT
1524                 afsi_log("%s:%d cm_LookupInternal (1) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1525                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1526 #endif
1527
1528                 if (code == 0) {
1529                     *outScpp = scp;
1530                     return 0;
1531                 }
1532                 if (scp) {
1533                     cm_ReleaseSCache(scp);
1534                     scp = NULL;
1535                 }
1536             } else {
1537                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1538 #ifdef DEBUG_REFCOUNT
1539                 afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1540                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1541 #endif
1542                 *outScpp = scp;
1543                 return code;
1544             }
1545         }
1546     } else {
1547         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1548 #ifdef DEBUG_REFCOUNT
1549         afsi_log("%s:%d cm_LookupInternal (2) code 0x%x dscp 0x%p ref %d scp 0x%p ref %d", file, line, code, dscp, dscp->refCount, scp, scp ? scp->refCount : 0);
1550         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1551 #endif
1552         *outScpp = scp;
1553         return code;
1554     }
1555
1556     /* None of the possible sysName expansions could be found */
1557     if (flags & CM_FLAG_CHECKPATH)
1558         return CM_ERROR_NOSUCHPATH;
1559     else
1560         return CM_ERROR_NOSUCHFILE;
1561 }
1562
1563 /*! \brief Unlink a file name
1564
1565   Encapsulates a call to RXAFS_RemoveFile().
1566
1567   \param[in] dscp cm_scache_t pointing at the directory containing the
1568       name to be unlinked.
1569
1570   \param[in] fnamep Original name to be unlinked.  This is the
1571       name that will be passed into the RXAFS_RemoveFile() call.
1572       This parameter is optional.  If not provided, the value will
1573       be looked up.
1574
1575   \param[in] came Client name to be unlinked.  This name will be used
1576       to update the local directory caches.
1577
1578   \param[in] userp cm_user_t for the request.
1579
1580   \param[in] reqp Request tracker.
1581
1582  */
1583 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1584                cm_user_t *userp, cm_req_t *reqp)
1585 {
1586     long code;
1587     cm_conn_t *connp;
1588     AFSFid afsFid;
1589     int sflags;
1590     AFSFetchStatus newDirStatus;
1591     AFSVolSync volSync;
1592     struct rx_connection * rxconnp;
1593     cm_dirOp_t dirop;
1594     cm_scache_t *scp = NULL;
1595     int free_fnamep = FALSE;
1596
1597     memset(&volSync, 0, sizeof(volSync));
1598
1599     if (fnamep == NULL) {
1600         code = -1;
1601 #ifdef USE_BPLUS
1602         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1603                              CM_DIROP_FLAG_NONE, &dirop);
1604         if (code == 0) {
1605             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1606             if (code == 0)
1607                 free_fnamep = TRUE;
1608             cm_EndDirOp(&dirop);
1609         }
1610 #endif
1611         if (code)
1612             goto done;
1613     }
1614
1615 #ifdef AFS_FREELANCE_CLIENT
1616     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1617         /* deleting a mount point from the root dir. */
1618         code = cm_FreelanceRemoveMount(fnamep);
1619         goto done;
1620     }
1621 #endif
1622
1623     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1624     if (code)
1625         goto done;
1626
1627     /* Check for RO volume */
1628     if (dscp->flags & CM_SCACHEFLAG_RO) {
1629         code = CM_ERROR_READONLY;
1630         goto done;
1631     }
1632
1633     /* make sure we don't screw up the dir status during the merge */
1634     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1635                          CM_DIROP_FLAG_NONE, &dirop);
1636
1637     lock_ObtainWrite(&dscp->rw);
1638     sflags = CM_SCACHESYNC_STOREDATA;
1639     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1640     lock_ReleaseWrite(&dscp->rw);
1641     if (code) {
1642         cm_EndDirOp(&dirop);
1643         goto done;
1644     }
1645
1646     /* make the RPC */
1647     afsFid.Volume = dscp->fid.volume;
1648     afsFid.Vnode = dscp->fid.vnode;
1649     afsFid.Unique = dscp->fid.unique;
1650
1651     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1652     do {
1653         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1654         if (code)
1655             continue;
1656
1657         rxconnp = cm_GetRxConn(connp);
1658         code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1659                                 &newDirStatus, &volSync);
1660         rx_PutConnection(rxconnp);
1661
1662     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1663     code = cm_MapRPCError(code, reqp);
1664
1665     if (code)
1666         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1667     else
1668         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1669
1670     if (dirop.scp) {
1671         lock_ObtainWrite(&dirop.scp->dirlock);
1672         dirop.lockType = CM_DIRLOCK_WRITE;
1673     }
1674     lock_ObtainWrite(&dscp->rw);
1675     cm_dnlcRemove(dscp, cnamep);
1676     if (code == 0) {
1677         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1678         if (RDR_Initialized &&
1679             scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1680             RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1681                                  dscp->fid.unique, dscp->fid.hash,
1682                                  dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1683     } else if (code == CM_ERROR_NOSUCHFILE) {
1684         /* windows would not have allowed the request to delete the file
1685          * if it did not believe the file existed.  therefore, we must
1686          * have an inconsistent view of the world.
1687          */
1688         dscp->cbServerp = NULL;
1689     }
1690     cm_SyncOpDone(dscp, NULL, sflags);
1691     lock_ReleaseWrite(&dscp->rw);
1692
1693     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1694         cm_DirDeleteEntry(&dirop, fnamep);
1695 #ifdef USE_BPLUS
1696         cm_BPlusDirDeleteEntry(&dirop, cnamep);
1697 #endif
1698     }
1699     cm_EndDirOp(&dirop);
1700
1701     if (scp) {
1702         cm_ReleaseSCache(scp);
1703         if (code == 0) {
1704             lock_ObtainWrite(&scp->rw);
1705             if (--scp->linkCount == 0) {
1706                 scp->flags |= CM_SCACHEFLAG_DELETED;
1707                 lock_ObtainWrite(&cm_scacheLock);
1708                 cm_AdjustScacheLRU(scp);
1709                 cm_RemoveSCacheFromHashTable(scp);
1710                 lock_ReleaseWrite(&cm_scacheLock);
1711             }
1712             cm_DiscardSCache(scp);
1713             lock_ReleaseWrite(&scp->rw);
1714             if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
1715                 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1716                                       scp->fid.unique, scp->fid.hash,
1717                                       scp->fileType, AFS_INVALIDATE_DELETED))
1718                 buf_ClearRDRFlag(scp, "unlink");
1719         }
1720     }
1721
1722   done:
1723     if (free_fnamep)
1724         free(fnamep);
1725
1726     return code;
1727 }
1728
1729 /* called with a write locked vnode, and fills in the link info.
1730  * returns this the vnode still write locked.
1731  */
1732 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1733 {
1734     long code = 0;
1735
1736     lock_AssertWrite(&linkScp->rw);
1737     if (!linkScp->mountPointStringp[0]) {
1738
1739 #ifdef AFS_FREELANCE_CLIENT
1740         /* File servers do not have data for freelance entries */
1741         if (cm_freelanceEnabled &&
1742             linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1743             linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1744         {
1745             code = cm_FreelanceFetchMountPointString(linkScp);
1746         } else
1747 #endif /* AFS_FREELANCE_CLIENT */
1748         {
1749             char temp[MOUNTPOINTLEN];
1750             osi_hyper_t offset;
1751
1752             /* read the link data from the file server */
1753             offset.LowPart = offset.HighPart = 0;
1754             code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1755             if (code)
1756                 return code;
1757
1758             /*
1759              * linkScp->length is the actual length of the symlink target string.
1760              * It is current because cm_GetData merged the most up to date
1761              * status info into scp and has not dropped the rwlock since.
1762              */
1763             if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1764                 return CM_ERROR_TOOBIG;
1765             if (linkScp->length.LowPart == 0)
1766                 return CM_ERROR_INVAL;
1767
1768             /* make sure we are NUL terminated */
1769             temp[linkScp->length.LowPart] = 0;
1770             memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1771         }
1772
1773         if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1774             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1775
1776     }   /* don't have symlink contents cached */
1777
1778     return code;
1779 }
1780
1781 /* called with a held vnode and a path suffix, with the held vnode being a
1782  * symbolic link.  Our goal is to generate a new path to interpret, and return
1783  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1784  * other than the directory containing the symbolic link, then the new root is
1785  * returned in *newRootScpp, otherwise a null is returned there.
1786  */
1787 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1788                      cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1789                      cm_user_t *userp, cm_req_t *reqp)
1790 {
1791     long code = 0;
1792     long len;
1793     fschar_t *linkp;
1794     cm_space_t *tsp;
1795
1796     *newRootScpp = NULL;
1797     *newSpaceBufferp = NULL;
1798
1799     lock_ObtainWrite(&linkScp->rw);
1800     /*
1801      * Do not get status if we do not already have a callback.
1802      * The process of reading the symlink string will obtain status information
1803      * in a single RPC.  No reason to add a second round trip.
1804      *
1805      * If we do have a callback, use cm_SyncOp to get status in case the
1806      * current cm_user_t is not the same as the one that obtained the
1807      * symlink string contents.
1808      */
1809     if (cm_HaveCallback(linkScp)) {
1810         code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1811                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1812         if (code) {
1813             lock_ReleaseWrite(&linkScp->rw);
1814             cm_ReleaseSCache(linkScp);
1815             goto done;
1816         }
1817         cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1818     }
1819     code = cm_HandleLink(linkScp, userp, reqp);
1820     if (code)
1821         goto done;
1822
1823     /* if we may overflow the buffer, bail out; buffer is signficantly
1824      * bigger than max path length, so we don't really have to worry about
1825      * being a little conservative here.
1826      */
1827     if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1828         >= CM_UTILS_SPACESIZE) {
1829         code = CM_ERROR_TOOBIG;
1830         goto done;
1831     }
1832
1833     tsp = cm_GetSpace();
1834     linkp = linkScp->mountPointStringp;
1835     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1836         if (strlen(linkp) > cm_mountRootLen)
1837             StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1838         else
1839             tsp->data[0] = 0;
1840         *newRootScpp = cm_RootSCachep(userp, reqp);
1841         cm_HoldSCache(*newRootScpp);
1842     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1843         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1844         {
1845             char * p = &linkp[len + 3];
1846             if (strnicmp(p, "all", 3) == 0)
1847                 p += 4;
1848
1849             StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1850             for (p = tsp->data; *p; p++) {
1851                 if (*p == '\\')
1852                     *p = '/';
1853             }
1854             *newRootScpp = cm_RootSCachep(userp, reqp);
1855             cm_HoldSCache(*newRootScpp);
1856         } else {
1857             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1858             StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1859             code = CM_ERROR_PATH_NOT_COVERED;
1860         }
1861     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1862                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1863         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1864         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1865         code = CM_ERROR_PATH_NOT_COVERED;
1866     } else if (*linkp == '\\' || *linkp == '/') {
1867 #if 0
1868         /* formerly, this was considered to be from the AFS root,
1869          * but this seems to create problems.  instead, we will just
1870          * reject the link */
1871         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1872         *newRootScpp = cm_RootSCachep(userp, reqp);
1873         cm_HoldSCache(*newRootScpp);
1874 #else
1875         /* we still copy the link data into the response so that
1876          * the user can see what the link points to
1877          */
1878         linkScp->fileType = CM_SCACHETYPE_INVALID;
1879         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1880         code = CM_ERROR_NOSUCHPATH;
1881 #endif
1882     } else {
1883         /* a relative link */
1884         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1885     }
1886     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1887         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1888         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1889     }
1890
1891     if (code == 0) {
1892         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1893         if (cpath != NULL) {
1894         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1895         free(cpath);
1896         *newSpaceBufferp = tsp;
1897     } else {
1898             code = CM_ERROR_NOSUCHPATH;
1899         }
1900     }
1901
1902     if (code != 0) {
1903         cm_FreeSpace(tsp);
1904
1905         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1906             cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1907         }
1908     }
1909
1910  done:
1911     lock_ReleaseWrite(&linkScp->rw);
1912     return code;
1913 }
1914 #ifdef DEBUG_REFCOUNT
1915 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1916                  cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1917                  cm_scache_t **outScpp,
1918                  char * file, long line)
1919 #else
1920 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1921               cm_user_t *userp, clientchar_t *tidPathp,
1922               cm_req_t *reqp, cm_scache_t **outScpp)
1923 #endif
1924 {
1925     long code;
1926     clientchar_t *tp;                   /* ptr moving through input buffer */
1927     clientchar_t tc;                    /* temp char */
1928     int haveComponent;          /* has new component started? */
1929     clientchar_t component[AFSPATHMAX]; /* this is the new component */
1930     clientchar_t *cp;                   /* component name being assembled */
1931     cm_scache_t *tscp;          /* current location in the hierarchy */
1932     cm_scache_t *nscp;          /* next dude down */
1933     cm_scache_t *dirScp;        /* last dir we searched */
1934     cm_scache_t *linkScp;       /* new root for the symlink we just
1935     * looked up */
1936     cm_space_t *psp;            /* space for current path, if we've hit
1937     * any symlinks */
1938     cm_space_t *tempsp;         /* temp vbl */
1939     clientchar_t *restp;                /* rest of the pathname to interpret */
1940     int symlinkCount;           /* count of # of symlinks traversed */
1941     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1942     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1943 #define MAX_FID_COUNT 512
1944     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1945     int fid_count = 0;          /* number of fids processed in this path walk */
1946     int i;
1947
1948     *outScpp = NULL;
1949
1950 #ifdef DEBUG_REFCOUNT
1951     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1952     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1953              rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1954              flags);
1955 #endif
1956
1957     tp = tidPathp;
1958     if (tp == NULL) {
1959         tp = pathp;
1960         phase = 2;
1961     }
1962     if (tp == NULL) {
1963         tp = _C("");
1964     }
1965     haveComponent = 0;
1966     psp = NULL;
1967     tscp = rootSCachep;
1968     cm_HoldSCache(tscp);
1969     symlinkCount = 0;
1970     dirScp = NULL;
1971
1972
1973     while (1) {
1974         tc = *tp++;
1975
1976         /* map Unix slashes into DOS ones so we can interpret Unix
1977          * symlinks properly
1978          */
1979         if (tc == '/')
1980             tc = '\\';
1981
1982         if (!haveComponent) {
1983             if (tc == '\\') {
1984                 continue;
1985             } else if (tc == 0) {
1986                 if (phase == 1) {
1987                     phase = 2;
1988                     tp = pathp;
1989                     continue;
1990                 }
1991                 code = 0;
1992                 break;
1993             } else {
1994                 haveComponent = 1;
1995                 cp = component;
1996                 *cp++ = tc;
1997             }
1998         } else {
1999             /* we have a component here */
2000             if (tc == 0 || tc == '\\') {
2001                 /* end of the component; we're at the last
2002                  * component if tc == 0.  However, if the last
2003                  * is a symlink, we have more to do.
2004                  */
2005                 *cp++ = 0;      /* add null termination */
2006                 extraFlag = 0;
2007                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2008                     extraFlag = CM_FLAG_NOMOUNTCHASE;
2009                 code = cm_Lookup(tscp, component,
2010                                  flags | extraFlag,
2011                                  userp, reqp, &nscp);
2012
2013                 if (code == 0) {
2014                     if (!cm_ClientStrCmp(component,_C("..")) ||
2015                         !cm_ClientStrCmp(component,_C("."))) {
2016                         /*
2017                          * roll back the fid list until we find the
2018                          * fid that matches where we are now.  Its not
2019                          * necessarily one or two fids because they
2020                          * might have been symlinks or mount points or
2021                          * both that were crossed.
2022                          */
2023                         for ( i=fid_count-1; i>=0; i--) {
2024                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
2025                                 break;
2026                         }
2027                         fid_count = i+1;
2028                     } else {
2029                         /* add the new fid to the list */
2030                         if (fid_count == MAX_FID_COUNT) {
2031                             code = CM_ERROR_TOO_MANY_SYMLINKS;
2032                             cm_ReleaseSCache(nscp);
2033                             nscp = NULL;
2034                             break;
2035                         }
2036                         fids[fid_count++] = nscp->fid;
2037                     }
2038                 }
2039
2040                 if (code) {
2041                     cm_ReleaseSCache(tscp);
2042                     if (dirScp)
2043                         cm_ReleaseSCache(dirScp);
2044                     if (psp)
2045                         cm_FreeSpace(psp);
2046                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2047                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2048                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2049                         return CM_ERROR_NOSUCHPATH;
2050                     } else {
2051                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2052                         return code;
2053                     }
2054                 }
2055
2056                 haveComponent = 0;      /* component done */
2057                 if (dirScp)
2058                     cm_ReleaseSCache(dirScp);
2059                 dirScp = tscp;          /* for some symlinks */
2060                 tscp = nscp;            /* already held */
2061                 nscp = NULL;
2062                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2063                     code = 0;
2064                     if (dirScp) {
2065                         cm_ReleaseSCache(dirScp);
2066                         dirScp = NULL;
2067                     }
2068                     break;
2069                 }
2070
2071                 /* now, if tscp is a symlink, we should follow it and
2072                  * assemble the path again.
2073                  */
2074                 lock_ObtainWrite(&tscp->rw);
2075                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2076                                   CM_SCACHESYNC_GETSTATUS
2077                                   | CM_SCACHESYNC_NEEDCALLBACK);
2078                 if (code) {
2079                     lock_ReleaseWrite(&tscp->rw);
2080                     cm_ReleaseSCache(tscp);
2081                     tscp = NULL;
2082                     if (dirScp) {
2083                         cm_ReleaseSCache(dirScp);
2084                         dirScp = NULL;
2085                     }
2086                     break;
2087                 }
2088                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2089
2090                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2091                     /* this is a symlink; assemble a new buffer */
2092                     lock_ReleaseWrite(&tscp->rw);
2093                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2094                         cm_ReleaseSCache(tscp);
2095                         tscp = NULL;
2096                         if (dirScp) {
2097                             cm_ReleaseSCache(dirScp);
2098                             dirScp = NULL;
2099                         }
2100                         if (psp)
2101                             cm_FreeSpace(psp);
2102                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2103                         return CM_ERROR_TOO_MANY_SYMLINKS;
2104                     }
2105                     if (tc == 0)
2106                         restp = _C("");
2107                     else
2108                         restp = tp;
2109
2110                     {
2111                         fschar_t * frestp;
2112
2113                         /* TODO: make this better */
2114                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2115                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2116                         free(frestp);
2117                     }
2118
2119                     if (code == 0 && linkScp != NULL) {
2120                         if (linkScp == cm_data.rootSCachep) {
2121                             fid_count = 0;
2122                             i = 0;
2123                         } else {
2124                             for ( i=0; i<fid_count; i++) {
2125                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2126                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2127                                     cm_ReleaseSCache(linkScp);
2128                                     nscp = NULL;
2129                                     break;
2130                                 }
2131                             }
2132                         }
2133                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2134                             fids[fid_count++] = linkScp->fid;
2135                         }
2136                     }
2137
2138                     if (code) {
2139                         /* something went wrong */
2140                         cm_ReleaseSCache(tscp);
2141                         tscp = NULL;
2142                         if (dirScp) {
2143                             cm_ReleaseSCache(dirScp);
2144                             dirScp = NULL;
2145                         }
2146                         break;
2147                     }
2148
2149                     /* otherwise, tempsp has the new path,
2150                      * and linkScp is the new root from
2151                      * which to interpret that path.
2152                      * Continue with the namei processing,
2153                      * also doing the bookkeeping for the
2154                      * space allocation and tracking the
2155                      * vnode reference counts.
2156                      */
2157                     if (psp)
2158                         cm_FreeSpace(psp);
2159                     psp = tempsp;
2160                     tp = psp->wdata;
2161                     cm_ReleaseSCache(tscp);
2162                     tscp = linkScp;
2163                     linkScp = NULL;
2164                     /* already held
2165                      * by AssembleLink
2166                      * now, if linkScp is null, that's
2167                      * AssembleLink's way of telling us that
2168                      * the sym link is relative to the dir
2169                      * containing the link.  We have a ref
2170                      * to it in dirScp, and we hold it now
2171                      * and reuse it as the new spot in the
2172                      * dir hierarchy.
2173                      */
2174                     if (tscp == NULL) {
2175                         tscp = dirScp;
2176                         dirScp = NULL;
2177                     }
2178                 } else {
2179                     /* not a symlink, we may be done */
2180                     lock_ReleaseWrite(&tscp->rw);
2181                     if (tc == 0) {
2182                         if (phase == 1) {
2183                             phase = 2;
2184                             tp = pathp;
2185                             continue;
2186                         }
2187                         if (dirScp) {
2188                             cm_ReleaseSCache(dirScp);
2189                             dirScp = NULL;
2190                         }
2191                         code = 0;
2192                         break;
2193                     }
2194                 }
2195                 if (dirScp) {
2196                     cm_ReleaseSCache(dirScp);
2197                     dirScp = NULL;
2198                 }
2199             } /* end of a component */
2200             else
2201                 *cp++ = tc;
2202         } /* we have a component */
2203     } /* big while loop over all components */
2204
2205     /* already held */
2206     if (dirScp)
2207         cm_ReleaseSCache(dirScp);
2208     if (psp)
2209         cm_FreeSpace(psp);
2210     if (code == 0)
2211         *outScpp = tscp;
2212     else if (tscp)
2213         cm_ReleaseSCache(tscp);
2214
2215 #ifdef DEBUG_REFCOUNT
2216     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2217 #endif
2218     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2219     return code;
2220 }
2221
2222 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2223  * We chase the link, and return a held pointer to the target, if it exists,
2224  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2225  * and do not hold or return a target vnode.
2226  *
2227  * This is very similar to calling cm_NameI with the last component of a name,
2228  * which happens to be a symlink, except that we've already passed by the name.
2229  *
2230  * This function is typically called by the directory listing functions, which
2231  * encounter symlinks but need to return the proper file length so programs
2232  * like "more" work properly when they make use of the attributes retrieved from
2233  * the dir listing.
2234  *
2235  * The input vnode should not be locked when this function is called.
2236  */
2237 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2238                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2239 {
2240     long code;
2241     cm_space_t *spacep;
2242     cm_scache_t *newRootScp;
2243
2244     *outScpp = NULL;
2245
2246     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2247
2248     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2249     if (code)
2250         return code;
2251
2252     /* now, if newRootScp is NULL, we're really being told that the symlink
2253      * is relative to the current directory (dscp).
2254      */
2255     if (newRootScp == NULL) {
2256         newRootScp = dscp;
2257         cm_HoldSCache(dscp);
2258     }
2259
2260     code = cm_NameI(newRootScp, spacep->wdata,
2261                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2262                     userp, NULL, reqp, outScpp);
2263
2264     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2265         code = CM_ERROR_NOSUCHPATH;
2266
2267     /* this stuff is allocated no matter what happened on the namei call,
2268      * so free it */
2269     cm_FreeSpace(spacep);
2270     cm_ReleaseSCache(newRootScp);
2271
2272     if (linkScp == *outScpp) {
2273         cm_ReleaseSCache(*outScpp);
2274         *outScpp = NULL;
2275         code = CM_ERROR_NOSUCHPATH;
2276     }
2277
2278     return code;
2279 }
2280
2281 /* for a given entry, make sure that it isn't in the stat cache, and then
2282  * add it to the list of file IDs to be obtained.
2283  *
2284  * Don't bother adding it if we already have a vnode.  Note that the dir
2285  * is locked, so we have to be careful checking the vnode we're thinking of
2286  * processing, to avoid deadlocks.
2287  */
2288 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2289                      osi_hyper_t *offp)
2290 {
2291     osi_hyper_t thyper;
2292     cm_bulkStat_t *bsp;
2293     int i;
2294     cm_scache_t *tscp;
2295     cm_fid_t tfid;
2296
2297     bsp = rockp;
2298
2299     /* Don't overflow bsp. */
2300     if (bsp->counter >= CM_BULKMAX)
2301         return CM_ERROR_STOPNOW;
2302
2303     thyper.LowPart = cm_data.buf_blockSize;
2304     thyper.HighPart = 0;
2305     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2306
2307     /* thyper is now the first byte past the end of the record we're
2308      * interested in, and bsp->bufOffset is the first byte of the record
2309      * we're interested in.
2310      * Skip data in the others.
2311      * Skip '.' and '..'
2312      */
2313     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2314         return 0;
2315     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2316         return CM_ERROR_STOPNOW;
2317     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2318         return 0;
2319
2320     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2321     tscp = cm_FindSCache(&tfid);
2322     if (tscp) {
2323         if (lock_TryWrite(&tscp->rw)) {
2324             /* we have an entry that we can look at */
2325             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2326                 /* we have a callback on it.  Don't bother
2327                  * fetching this stat entry, since we're happy
2328                  * with the info we have.
2329                  */
2330                 lock_ReleaseWrite(&tscp->rw);
2331                 cm_ReleaseSCache(tscp);
2332                 return 0;
2333             }
2334             lock_ReleaseWrite(&tscp->rw);
2335         }       /* got lock */
2336         cm_ReleaseSCache(tscp);
2337     }   /* found entry */
2338
2339 #ifdef AFS_FREELANCE_CLIENT
2340     // yj: if this is a mountpoint under root.afs then we don't want it
2341     // to be bulkstat-ed, instead, we call getSCache directly and under
2342     // getSCache, it is handled specially.
2343     if  ( cm_freelanceEnabled &&
2344           tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2345           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2346           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2347     {
2348         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2349         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2350     }
2351 #endif /* AFS_FREELANCE_CLIENT */
2352
2353     i = bsp->counter++;
2354     bsp->fids[i].Volume = scp->fid.volume;
2355     bsp->fids[i].Vnode = tfid.vnode;
2356     bsp->fids[i].Unique = tfid.unique;
2357     return 0;
2358 }
2359
2360 afs_int32
2361 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2362 {
2363     afs_int32 code = 0;
2364     AFSCBFids fidStruct;
2365     AFSBulkStats statStruct;
2366     cm_conn_t *connp;
2367     AFSCBs callbackStruct;
2368     long filex;
2369     AFSVolSync volSync;
2370     cm_callbackRequest_t cbReq;
2371     long filesThisCall;
2372     long i;
2373     long j;
2374     cm_scache_t *scp;
2375     cm_fid_t tfid;
2376     struct rx_connection * rxconnp;
2377     int inlinebulk;             /* Did we use InlineBulkStatus RPC or not? */
2378
2379     memset(&volSync, 0, sizeof(volSync));
2380
2381     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2382      * make the calls to create the entries.  Handle AFSCBMAX files at a
2383      * time.
2384      */
2385     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2386         filesThisCall = bbp->counter - filex;
2387         if (filesThisCall > AFSCBMAX)
2388             filesThisCall = AFSCBMAX;
2389
2390         fidStruct.AFSCBFids_len = filesThisCall;
2391         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2392         statStruct.AFSBulkStats_len = filesThisCall;
2393         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2394         callbackStruct.AFSCBs_len = filesThisCall;
2395         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2396         cm_StartCallbackGrantingCall(NULL, &cbReq);
2397         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2398
2399         /*
2400          * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2401          * be a FID provided.  However, the error code from RXAFS_BulkStatus
2402          * or RXAFS_InlinkBulkStatus does not apply to any FID.  Therefore,
2403          * we generate an invalid FID to match with the RPC error.
2404          */
2405         cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2406
2407         do {
2408             inlinebulk = 0;
2409
2410             code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2411             if (code)
2412                 continue;
2413
2414             rxconnp = cm_GetRxConn(connp);
2415             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2416                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2417                                               &statStruct, &callbackStruct, &volSync);
2418                 if (code == RXGEN_OPCODE) {
2419                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2420                 } else {
2421                     inlinebulk = 1;
2422                 }
2423             }
2424             if (!inlinebulk) {
2425                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2426                                         &statStruct, &callbackStruct, &volSync);
2427             }
2428             rx_PutConnection(rxconnp);
2429
2430             /*
2431              * If InlineBulk RPC was called and it succeeded,
2432              * then pull out the return code from the status info
2433              * and use it for cm_Analyze so that we can failover to other
2434              * .readonly volume instances.  But only do it for errors that
2435              * are volume global.
2436              */
2437             if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2438                 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2439                           (&bbp->stats[0])->errorCode);
2440                 switch ((&bbp->stats[0])->errorCode) {
2441                 case VBUSY:
2442                 case VRESTARTING:
2443                 case VNOVOL:
2444                 case VMOVED:
2445                 case VOFFLINE:
2446                 case VSALVAGE:
2447                 case VNOSERVICE:
2448                 case VIO:
2449                     code = (&bbp->stats[0])->errorCode;
2450                     break;
2451                 default:
2452                     /* Rx and Rxkad errors are volume global */
2453                     if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2454                          (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2455                         code = (&bbp->stats[0])->errorCode;
2456                 }
2457             }
2458         } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2459         code = cm_MapRPCError(code, reqp);
2460
2461         /*
2462          * might as well quit on an error, since we're not going to do
2463          * much better on the next immediate call, either.
2464          */
2465         if (code) {
2466             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2467                       inlinebulk ? "Inline" : "", code);
2468             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2469             break;
2470         }
2471
2472         /*
2473          * The bulk RPC has succeeded or at least not failed with a
2474          * volume global error result.  For items that have inlineBulk
2475          * errors we must call cm_Analyze in order to perform required
2476          * logging of errors.
2477          *
2478          * If the RPC was not inline bulk or the entry either has no error
2479          * the status must be merged.
2480          */
2481         osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2482
2483         for (i = 0; i<filesThisCall; i++) {
2484             j = filex + i;
2485             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2486
2487             if (inlinebulk && (&bbp->stats[j])->errorCode) {
2488                 cm_req_t treq = *reqp;
2489                 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2490             } else {
2491                 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2492                 if (code != 0)
2493                     continue;
2494
2495                 /*
2496                  * otherwise, if this entry has no callback info,
2497                  * merge in this.  If there is existing callback info
2498                  * we skip the merge because the existing data must be
2499                  * current (we have a callback) and the response from
2500                  * a non-inline bulk rpc might actually be wrong.
2501                  *
2502                  * now, we have to be extra paranoid on merging in this
2503                  * information, since we didn't use cm_SyncOp before
2504                  * starting the fetch to make sure that no bad races
2505                  * were occurring.  Specifically, we need to make sure
2506                  * we don't obliterate any newer information in the
2507                  * vnode than have here.
2508                  *
2509                  * Right now, be pretty conservative: if there's a
2510                  * callback or a pending call, skip it.
2511                  * However, if the prior attempt to obtain status
2512                  * was refused access or the volume is .readonly,
2513                  * take the data in any case since we have nothing
2514                  * better for the in flight directory enumeration that
2515                  * resulted in this function being called.
2516                  */
2517                 lock_ObtainRead(&scp->rw);
2518                 if ((scp->cbServerp == NULL &&
2519                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2520                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
2521                      (scp->flags & CM_SCACHEFLAG_EACCESS))
2522                 {
2523                     lock_ConvertRToW(&scp->rw);
2524                     cm_EndCallbackGrantingCall(scp, &cbReq,
2525                                                &bbp->callbacks[j],
2526                                                &volSync,
2527                                                CM_CALLBACK_MAINTAINCOUNT);
2528                     cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2529                     lock_ReleaseWrite(&scp->rw);
2530                 } else {
2531                     lock_ReleaseRead(&scp->rw);
2532                 }
2533                 cm_ReleaseSCache(scp);
2534             }
2535         } /* all files in the response */
2536         /* now tell it to drop the count,
2537          * after doing the vnode processing above */
2538         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2539     }   /* while there are still more files to process */
2540
2541     return code;
2542 }
2543
2544 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2545  * calls on all undeleted files in the page of the directory specified.
2546  */
2547 afs_int32
2548 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2549                cm_req_t *reqp)
2550 {
2551     afs_int32 code;
2552     cm_bulkStat_t *bbp;
2553
2554     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2555
2556     /* should be on a buffer boundary */
2557     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2558
2559     bbp = malloc(sizeof(cm_bulkStat_t));
2560     memset(bbp, 0, sizeof(cm_bulkStat_t));
2561     bbp->bufOffset = *offsetp;
2562
2563     lock_ReleaseWrite(&dscp->rw);
2564     /* first, assemble the file IDs we need to stat */
2565     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2566
2567     /* if we failed, bail out early */
2568     if (code && code != CM_ERROR_STOPNOW) {
2569         free(bbp);
2570         lock_ObtainWrite(&dscp->rw);
2571         return code;
2572     }
2573
2574     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2575     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2576
2577     lock_ObtainWrite(&dscp->rw);
2578     free(bbp);
2579     return 0;
2580 }
2581
2582 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2583 {
2584     long mask;
2585
2586     /* initialize store back mask as inexpensive local variable */
2587     mask = 0;
2588     memset(statusp, 0, sizeof(AFSStoreStatus));
2589
2590     /* copy out queued info from scache first, if scp passed in */
2591     if (scp) {
2592         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2593             statusp->ClientModTime = scp->clientModTime;
2594             mask |= AFS_SETMODTIME;
2595             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2596         }
2597     }
2598
2599     if (attrp) {
2600         /* now add in our locally generated request */
2601         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2602             statusp->ClientModTime = attrp->clientModTime;
2603             mask |= AFS_SETMODTIME;
2604         }
2605         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2606             statusp->UnixModeBits = attrp->unixModeBits;
2607             mask |= AFS_SETMODE;
2608         }
2609         if (attrp->mask & CM_ATTRMASK_OWNER) {
2610             statusp->Owner = attrp->owner;
2611             mask |= AFS_SETOWNER;
2612         }
2613         if (attrp->mask & CM_ATTRMASK_GROUP) {
2614             statusp->Group = attrp->group;
2615             mask |= AFS_SETGROUP;
2616         }
2617     }
2618     statusp->Mask = mask;
2619 }
2620
2621 /* set the file size, and make sure that all relevant buffers have been
2622  * truncated.  Ensure that any partially truncated buffers have been zeroed
2623  * to the end of the buffer.
2624  */
2625 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2626                    cm_req_t *reqp)
2627 {
2628     long code;
2629     int shrinking;
2630
2631     /* start by locking out buffer creation */
2632     lock_ObtainWrite(&scp->bufCreateLock);
2633
2634     /* verify that this is a file, not a dir or a symlink */
2635     lock_ObtainWrite(&scp->rw);
2636     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2637                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2638     if (code)
2639         goto done;
2640     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2641
2642     if (scp->fileType != CM_SCACHETYPE_FILE) {
2643         code = CM_ERROR_ISDIR;
2644         goto done;
2645     }
2646
2647   startover:
2648     if (LargeIntegerLessThan(*sizep, scp->length))
2649         shrinking = 1;
2650     else
2651         shrinking = 0;
2652
2653     lock_ReleaseWrite(&scp->rw);
2654
2655     /* can't hold scp->rw lock here, since we may wait for a storeback to
2656      * finish if the buffer package is cleaning a buffer by storing it to
2657      * the server.
2658      */
2659     if (shrinking)
2660         buf_Truncate(scp, userp, reqp, sizep);
2661
2662     /* now ensure that file length is short enough, and update truncPos */
2663     lock_ObtainWrite(&scp->rw);
2664
2665     /* make sure we have a callback (so we have the right value for the
2666      * length), and wait for it to be safe to do a truncate.
2667      */
2668     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2669                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2670                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2671
2672     /* If we only have 'i' bits, then we should still be able to set
2673        the size of a file we created. */
2674     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2675         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2676                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2677                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2678     }
2679
2680     if (code)
2681         goto done;
2682
2683     if (LargeIntegerLessThan(*sizep, scp->length)) {
2684         /* a real truncation.  If truncPos is not set yet, or is bigger
2685          * than where we're truncating the file, set truncPos to this
2686          * new value.
2687          */
2688         if (!shrinking)
2689             goto startover;
2690         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2691              || LargeIntegerLessThan(*sizep, scp->length)) {
2692             /* set trunc pos */
2693             scp->truncPos = *sizep;
2694             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2695         }
2696         /* in either case, the new file size has been changed */
2697         scp->length = *sizep;
2698         scp->mask |= CM_SCACHEMASK_LENGTH;
2699     }
2700     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2701         /* really extending the file */
2702         scp->length = *sizep;
2703         scp->mask |= CM_SCACHEMASK_LENGTH;
2704     }
2705
2706     /* done successfully */
2707     code = 0;
2708
2709     cm_SyncOpDone(scp, NULL,
2710                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2711                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2712
2713   done:
2714     lock_ReleaseWrite(&scp->rw);
2715     lock_ReleaseWrite(&scp->bufCreateLock);
2716
2717     return code;
2718 }
2719
2720 /* set the file size or other attributes (but not both at once) */
2721 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2722                 cm_req_t *reqp)
2723 {
2724     long code;
2725     AFSFetchStatus afsOutStatus;
2726     AFSVolSync volSync;
2727     cm_conn_t *connp;
2728     AFSFid tfid;
2729     AFSStoreStatus afsInStatus;
2730     struct rx_connection * rxconnp;
2731
2732     memset(&volSync, 0, sizeof(volSync));
2733
2734     /* handle file length setting */
2735     if (attrp->mask & CM_ATTRMASK_LENGTH)
2736         return cm_SetLength(scp, &attrp->length, userp, reqp);
2737
2738     lock_ObtainWrite(&scp->rw);
2739     /* Check for RO volume */
2740     if (scp->flags & CM_SCACHEFLAG_RO) {
2741         code = CM_ERROR_READONLY;
2742         lock_ReleaseWrite(&scp->rw);
2743         return code;
2744     }
2745
2746     /* otherwise, we have to make an RPC to get the status */
2747     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2748     if (code) {
2749         lock_ReleaseWrite(&scp->rw);
2750         return code;
2751     }
2752     lock_ConvertWToR(&scp->rw);
2753
2754     /* make the attr structure */
2755     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2756
2757     tfid.Volume = scp->fid.volume;
2758     tfid.Vnode = scp->fid.vnode;
2759     tfid.Unique = scp->fid.unique;
2760     lock_ReleaseRead(&scp->rw);
2761
2762     /* now make the RPC */
2763     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2764     do {
2765         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2766         if (code)
2767             continue;
2768
2769         rxconnp = cm_GetRxConn(connp);
2770         code = RXAFS_StoreStatus(rxconnp, &tfid,
2771                                   &afsInStatus, &afsOutStatus, &volSync);
2772         rx_PutConnection(rxconnp);
2773
2774     } while (cm_Analyze(connp, userp, reqp,
2775                          &scp->fid, &volSync, NULL, NULL, code));
2776     code = cm_MapRPCError(code, reqp);
2777
2778     if (code)
2779         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2780     else
2781         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2782
2783     lock_ObtainWrite(&scp->rw);
2784     if (code == 0)
2785         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2786                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2787     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2788
2789     /* if we're changing the mode bits, discard the ACL cache,
2790      * since we changed the mode bits.
2791      */
2792     if (afsInStatus.Mask & AFS_SETMODE)
2793         cm_FreeAllACLEnts(scp);
2794     lock_ReleaseWrite(&scp->rw);
2795     return code;
2796 }
2797
2798 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2799                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2800 {
2801     cm_conn_t *connp;
2802     long code;
2803     AFSFid dirAFSFid;
2804     cm_callbackRequest_t cbReq;
2805     AFSFid newAFSFid;
2806     cm_fid_t newFid;
2807     cm_scache_t *scp = NULL;
2808     int didEnd;
2809     AFSStoreStatus inStatus;
2810     AFSFetchStatus updatedDirStatus;
2811     AFSFetchStatus newFileStatus;
2812     AFSCallBack newFileCallback;
2813     AFSVolSync volSync;
2814     struct rx_connection * rxconnp;
2815     cm_dirOp_t dirop;
2816     fschar_t * fnamep = NULL;
2817
2818     memset(&volSync, 0, sizeof(volSync));
2819
2820     /* can't create names with @sys in them; must expand it manually first.
2821      * return "invalid request" if they try.
2822      */
2823     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2824         return CM_ERROR_ATSYS;
2825     }
2826
2827 #ifdef AFS_FREELANCE_CLIENT
2828     /* Freelance root volume does not hold files */
2829     if (cm_freelanceEnabled &&
2830         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2831         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2832     {
2833         return CM_ERROR_NOACCESS;
2834     }
2835 #endif /* AFS_FREELANCE_CLIENT */
2836
2837     /* Check for RO volume */
2838     if (dscp->flags & CM_SCACHEFLAG_RO)
2839         return CM_ERROR_READONLY;
2840
2841     /* before starting the RPC, mark that we're changing the file data, so
2842      * that someone who does a chmod will know to wait until our call
2843      * completes.
2844      */
2845     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2846                   &dirop);
2847     lock_ObtainWrite(&dscp->rw);
2848     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2849     lock_ReleaseWrite(&dscp->rw);
2850     if (code == 0) {
2851         cm_StartCallbackGrantingCall(NULL, &cbReq);
2852     } else {
2853         cm_EndDirOp(&dirop);
2854     }
2855     if (code) {
2856         return code;
2857     }
2858     didEnd = 0;
2859
2860     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2861
2862     cm_StatusFromAttr(&inStatus, NULL, attrp);
2863
2864     /* try the RPC now */
2865     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2866     do {
2867         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2868         if (code)
2869             continue;
2870
2871         dirAFSFid.Volume = dscp->fid.volume;
2872         dirAFSFid.Vnode = dscp->fid.vnode;
2873         dirAFSFid.Unique = dscp->fid.unique;
2874
2875         rxconnp = cm_GetRxConn(connp);
2876         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2877                                  &inStatus, &newAFSFid, &newFileStatus,
2878                                  &updatedDirStatus, &newFileCallback,
2879                                  &volSync);
2880         rx_PutConnection(rxconnp);
2881
2882     } while (cm_Analyze(connp, userp, reqp,
2883                          &dscp->fid, &volSync, NULL, &cbReq, code));
2884     code = cm_MapRPCError(code, reqp);
2885
2886     if (code)
2887         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2888     else
2889         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2890
2891     if (dirop.scp) {
2892         lock_ObtainWrite(&dirop.scp->dirlock);
2893         dirop.lockType = CM_DIRLOCK_WRITE;
2894     }
2895     lock_ObtainWrite(&dscp->rw);
2896     if (code == 0)
2897         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2898     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2899     lock_ReleaseWrite(&dscp->rw);
2900
2901     /* now try to create the file's entry, too, but be careful to
2902      * make sure that we don't merge in old info.  Since we weren't locking
2903      * out any requests during the file's creation, we may have pretty old
2904      * info.
2905      */
2906     if (code == 0) {
2907         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2908         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2909         if (code == 0) {
2910             lock_ObtainWrite(&scp->rw);
2911             scp->creator = userp;               /* remember who created it */
2912             if (!cm_HaveCallback(scp)) {
2913                 cm_EndCallbackGrantingCall(scp, &cbReq,
2914                                            &newFileCallback, &volSync, 0);
2915                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2916                                userp, reqp, 0);
2917                 didEnd = 1;
2918             }
2919             lock_ReleaseWrite(&scp->rw);
2920         }
2921     }
2922
2923     /* make sure we end things properly */
2924     if (!didEnd)
2925         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2926
2927     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2928         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2929 #ifdef USE_BPLUS
2930         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2931 #endif
2932     }
2933     cm_EndDirOp(&dirop);
2934
2935     if (fnamep)
2936         free(fnamep);
2937
2938     if (scp) {
2939         if (scpp)
2940             *scpp = scp;
2941         else
2942             cm_ReleaseSCache(scp);
2943     }
2944     return code;
2945 }
2946
2947 /*
2948  * locked if TRUE means write-locked
2949  * else the cm_scache_t rw must not be held
2950  */
2951 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2952 {
2953     long code;
2954
2955     if (locked)
2956         lock_ReleaseWrite(&scp->rw);
2957
2958     osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
2959
2960     code = buf_CleanVnode(scp, userp, reqp);
2961     if (code == 0) {
2962         lock_ObtainWrite(&scp->rw);
2963
2964         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2965                           | CM_SCACHEMASK_CLIENTMODTIME
2966                           | CM_SCACHEMASK_LENGTH))
2967             code = cm_StoreMini(scp, userp, reqp);
2968
2969         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2970             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2971             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2972         }
2973
2974         if (!locked)
2975             lock_ReleaseWrite(&scp->rw);
2976     } else if (locked) {
2977         lock_ObtainWrite(&scp->rw);
2978     }
2979     return code;
2980 }
2981
2982 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2983                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2984 {
2985     cm_conn_t *connp;
2986     long code;
2987     AFSFid dirAFSFid;
2988     cm_callbackRequest_t cbReq;
2989     AFSFid newAFSFid;
2990     cm_fid_t newFid;
2991     cm_scache_t *scp = NULL;
2992     int didEnd;
2993     AFSStoreStatus inStatus;
2994     AFSFetchStatus updatedDirStatus;
2995     AFSFetchStatus newDirStatus;
2996     AFSCallBack newDirCallback;
2997     AFSVolSync volSync;
2998     struct rx_connection * rxconnp;
2999     cm_dirOp_t dirop;
3000     fschar_t * fnamep = NULL;
3001
3002     memset(&volSync, 0, sizeof(volSync));
3003
3004     /* can't create names with @sys in them; must expand it manually first.
3005      * return "invalid request" if they try.
3006      */
3007     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3008         return CM_ERROR_ATSYS;
3009     }
3010
3011 #ifdef AFS_FREELANCE_CLIENT
3012     /* Freelance root volume does not hold subdirectories */
3013     if (cm_freelanceEnabled &&
3014         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3015         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3016     {
3017         return CM_ERROR_NOACCESS;
3018     }
3019 #endif /* AFS_FREELANCE_CLIENT */
3020
3021     /* Check for RO volume */
3022     if (dscp->flags & CM_SCACHEFLAG_RO)
3023         return CM_ERROR_READONLY;
3024
3025     /* before starting the RPC, mark that we're changing the directory
3026      * data, so that someone who does a chmod on the dir will wait until
3027      * our call completes.
3028      */
3029     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3030                   &dirop);
3031     lock_ObtainWrite(&dscp->rw);
3032     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3033     lock_ReleaseWrite(&dscp->rw);
3034     if (code == 0) {
3035         cm_StartCallbackGrantingCall(NULL, &cbReq);
3036     } else {
3037         cm_EndDirOp(&dirop);
3038     }
3039     if (code) {
3040         return code;
3041     }
3042     didEnd = 0;
3043
3044     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3045     cm_StatusFromAttr(&inStatus, NULL, attrp);
3046
3047     /* try the RPC now */
3048     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3049     do {
3050         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3051         if (code)
3052             continue;
3053
3054         dirAFSFid.Volume = dscp->fid.volume;
3055         dirAFSFid.Vnode = dscp->fid.vnode;
3056         dirAFSFid.Unique = dscp->fid.unique;
3057
3058         rxconnp = cm_GetRxConn(connp);
3059         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3060                               &inStatus, &newAFSFid, &newDirStatus,
3061                               &updatedDirStatus, &newDirCallback,
3062                               &volSync);
3063         rx_PutConnection(rxconnp);
3064
3065     } while (cm_Analyze(connp, userp, reqp,
3066                         &dscp->fid, &volSync, NULL, &cbReq, code));
3067     code = cm_MapRPCError(code, reqp);
3068
3069     if (code)
3070         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3071     else
3072         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3073
3074     if (dirop.scp) {
3075         lock_ObtainWrite(&dirop.scp->dirlock);
3076         dirop.lockType = CM_DIRLOCK_WRITE;
3077     }
3078     lock_ObtainWrite(&dscp->rw);
3079     if (code == 0)
3080         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3081     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3082     lock_ReleaseWrite(&dscp->rw);
3083
3084     /* now try to create the new dir's entry, too, but be careful to
3085      * make sure that we don't merge in old info.  Since we weren't locking
3086      * out any requests during the file's creation, we may have pretty old
3087      * info.
3088      */
3089     if (code == 0) {
3090         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3091         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3092         if (code == 0) {
3093             lock_ObtainWrite(&scp->rw);
3094             if (!cm_HaveCallback(scp)) {
3095                 cm_EndCallbackGrantingCall(scp, &cbReq,
3096                                             &newDirCallback, &volSync, 0);
3097                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3098                                 userp, reqp, 0);
3099                 didEnd = 1;
3100             }
3101             lock_ReleaseWrite(&scp->rw);
3102         }
3103     }
3104
3105     /* make sure we end things properly */
3106     if (!didEnd)
3107         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3108
3109     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3110         cm_DirCreateEntry(&dirop, fnamep, &newFid);
3111 #ifdef USE_BPLUS
3112         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3113 #endif
3114     }
3115     cm_EndDirOp(&dirop);
3116
3117     free(fnamep);
3118
3119     if (scp) {
3120         if (scpp)
3121             *scpp = scp;
3122         else
3123             cm_ReleaseSCache(scp);
3124     }
3125
3126     /* and return error code */
3127     return code;
3128 }
3129
3130 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3131              cm_user_t *userp, cm_req_t *reqp)
3132 {
3133     cm_conn_t *connp;
3134     long code = 0;
3135     AFSFid dirAFSFid;
3136     AFSFid existingAFSFid;
3137     AFSFetchStatus updatedDirStatus;
3138     AFSFetchStatus newLinkStatus;
3139     AFSVolSync volSync;
3140     struct rx_connection * rxconnp;
3141     cm_dirOp_t dirop;
3142     fschar_t * fnamep = NULL;
3143
3144     memset(&volSync, 0, sizeof(volSync));
3145
3146     if (dscp->fid.cell != sscp->fid.cell ||
3147         dscp->fid.volume != sscp->fid.volume) {
3148         return CM_ERROR_CROSSDEVLINK;
3149     }
3150
3151     /* Check for RO volume */
3152     if (dscp->flags & CM_SCACHEFLAG_RO)
3153         return CM_ERROR_READONLY;
3154
3155     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3156                   &dirop);
3157     lock_ObtainWrite(&dscp->rw);
3158     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3159     lock_ReleaseWrite(&dscp->rw);
3160     if (code != 0)
3161         cm_EndDirOp(&dirop);
3162
3163     if (code)
3164         return code;
3165
3166     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3167
3168     /* try the RPC now */
3169     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3170     do {
3171         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3172         if (code) continue;
3173
3174         dirAFSFid.Volume = dscp->fid.volume;
3175         dirAFSFid.Vnode = dscp->fid.vnode;
3176         dirAFSFid.Unique = dscp->fid.unique;
3177
3178         existingAFSFid.Volume = sscp->fid.volume;
3179         existingAFSFid.Vnode = sscp->fid.vnode;
3180         existingAFSFid.Unique = sscp->fid.unique;
3181
3182         rxconnp = cm_GetRxConn(connp);
3183         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3184             &newLinkStatus, &updatedDirStatus, &volSync);
3185         rx_PutConnection(rxconnp);
3186         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3187
3188     } while (cm_Analyze(connp, userp, reqp,
3189         &dscp->fid, &volSync, NULL, NULL, code));
3190
3191     code = cm_MapRPCError(code, reqp);
3192
3193     if (code)
3194         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3195     else
3196         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3197
3198     if (dirop.scp) {
3199         lock_ObtainWrite(&dirop.scp->dirlock);
3200         dirop.lockType = CM_DIRLOCK_WRITE;
3201     }
3202     lock_ObtainWrite(&dscp->rw);
3203     if (code == 0) {
3204         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3205         if (RDR_Initialized)
3206             RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3207                                  dscp->fid.unique, dscp->fid.hash,
3208                                  dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3209     }
3210     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3211     lock_ReleaseWrite(&dscp->rw);
3212
3213     if (code == 0) {
3214         if (cm_CheckDirOpForSingleChange(&dirop)) {
3215             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3216 #ifdef USE_BPLUS
3217             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3218 #endif
3219         }
3220     }
3221     cm_EndDirOp(&dirop);
3222
3223     /* Update the linked object status */
3224     if (code == 0) {
3225         lock_ObtainWrite(&sscp->rw);
3226         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3227         lock_ReleaseWrite(&sscp->rw);
3228     }
3229
3230     free(fnamep);
3231
3232     return code;
3233 }
3234
3235 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3236                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3237 {
3238     cm_conn_t *connp;
3239     long code;
3240     AFSFid dirAFSFid;
3241     AFSFid newAFSFid;
3242     cm_fid_t newFid;
3243     cm_scache_t *scp;
3244     AFSStoreStatus inStatus;
3245     AFSFetchStatus updatedDirStatus;
3246     AFSFetchStatus newLinkStatus;
3247     AFSVolSync volSync;
3248     struct rx_connection * rxconnp;
3249     cm_dirOp_t dirop;
3250     fschar_t *fnamep = NULL;
3251
3252     if (scpp)
3253         *scpp = NULL;
3254
3255     /* Check for RO volume */
3256     if (dscp->flags & CM_SCACHEFLAG_RO)
3257         return CM_ERROR_READONLY;
3258
3259     memset(&volSync, 0, sizeof(volSync));
3260
3261     /* before starting the RPC, mark that we're changing the directory data,
3262      * so that someone who does a chmod on the dir will wait until our
3263      * call completes.
3264      */
3265     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3266                   &dirop);
3267     lock_ObtainWrite(&dscp->rw);
3268     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3269     lock_ReleaseWrite(&dscp->rw);
3270     if (code != 0)
3271         cm_EndDirOp(&dirop);
3272     if (code) {
3273         return code;
3274     }
3275
3276     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3277
3278     cm_StatusFromAttr(&inStatus, NULL, attrp);
3279
3280     /* try the RPC now */
3281     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3282     do {
3283         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3284         if (code)
3285             continue;
3286
3287         dirAFSFid.Volume = dscp->fid.volume;
3288         dirAFSFid.Vnode = dscp->fid.vnode;
3289         dirAFSFid.Unique = dscp->fid.unique;
3290
3291         rxconnp = cm_GetRxConn(connp);
3292         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3293                               &inStatus, &newAFSFid, &newLinkStatus,
3294                               &updatedDirStatus, &volSync);
3295         rx_PutConnection(rxconnp);
3296
3297     } while (cm_Analyze(connp, userp, reqp,
3298                          &dscp->fid, &volSync, NULL, NULL, code));
3299     code = cm_MapRPCError(code, reqp);
3300
3301     if (code)
3302         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3303     else
3304         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3305
3306     if (dirop.scp) {
3307         lock_ObtainWrite(&dirop.scp->dirlock);
3308         dirop.lockType = CM_DIRLOCK_WRITE;
3309     }
3310     lock_ObtainWrite(&dscp->rw);
3311     if (code == 0)
3312         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3313     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3314     lock_ReleaseWrite(&dscp->rw);
3315
3316     if (code == 0) {
3317         if (cm_CheckDirOpForSingleChange(&dirop)) {
3318             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3319
3320             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3321 #ifdef USE_BPLUS
3322             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3323 #endif
3324         }
3325     }
3326     cm_EndDirOp(&dirop);
3327
3328     /* now try to create the new dir's entry, too, but be careful to
3329      * make sure that we don't merge in old info.  Since we weren't locking
3330      * out any requests during the file's creation, we may have pretty old
3331      * info.
3332      */
3333     if (code == 0) {
3334         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3335         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3336         if (code == 0) {
3337             lock_ObtainWrite(&scp->rw);
3338             if (!cm_HaveCallback(scp)) {
3339                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3340                                 userp, reqp, 0);
3341             }
3342             lock_ReleaseWrite(&scp->rw);
3343
3344             if (scpp) {
3345                 *scpp = scp;
3346             } else {
3347                 cm_ReleaseSCache(scp);
3348             }
3349         }
3350     }
3351
3352     free(fnamep);
3353
3354     /* and return error code */
3355     return code;
3356 }
3357
3358 /*! \brief Remove a directory
3359
3360   Encapsulates a call to RXAFS_RemoveDir().
3361
3362   \param[in] dscp cm_scache_t for the directory containing the
3363       directory to be removed.
3364
3365   \param[in] fnamep This will be the original name of the directory
3366       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3367       This parameter is optional.  If it is not provided the value
3368       will be looked up.
3369
3370   \param[in] cnamep Normalized name used to update the local
3371       directory caches.
3372
3373   \param[in] userp cm_user_t for the request.
3374
3375   \param[in] reqp Request tracker.
3376 */
3377 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3378 {
3379     cm_conn_t *connp;
3380     long code;
3381     AFSFid dirAFSFid;
3382     int didEnd;
3383     AFSFetchStatus updatedDirStatus;
3384     AFSVolSync volSync;
3385     struct rx_connection * rxconnp;
3386     cm_dirOp_t dirop;
3387     cm_scache_t *scp = NULL;
3388     int free_fnamep = FALSE;
3389
3390     memset(&volSync, 0, sizeof(volSync));
3391
3392     if (fnamep == NULL) {
3393         code = -1;
3394 #ifdef USE_BPLUS
3395         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3396                              CM_DIROP_FLAG_NONE, &dirop);
3397         if (code == 0) {
3398             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3399             if (code == 0)
3400                 free_fnamep = TRUE;
3401             cm_EndDirOp(&dirop);
3402         }
3403 #endif
3404         if (code)
3405             goto done;
3406     }
3407
3408     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3409     if (code)
3410         goto done;
3411
3412     /* Check for RO volume */
3413     if (dscp->flags & CM_SCACHEFLAG_RO) {
3414         code = CM_ERROR_READONLY;
3415         goto done;
3416     }
3417
3418     /* before starting the RPC, mark that we're changing the directory data,
3419      * so that someone who does a chmod on the dir will wait until our
3420      * call completes.
3421      */
3422     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3423                   &dirop);
3424     lock_ObtainWrite(&dscp->rw);
3425     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3426     lock_ReleaseWrite(&dscp->rw);
3427     if (code) {
3428         cm_EndDirOp(&dirop);
3429         goto done;
3430     }
3431     didEnd = 0;
3432
3433     /* try the RPC now */
3434     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3435     do {
3436         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3437         if (code)
3438             continue;
3439
3440         dirAFSFid.Volume = dscp->fid.volume;
3441         dirAFSFid.Vnode = dscp->fid.vnode;
3442         dirAFSFid.Unique = dscp->fid.unique;
3443
3444         rxconnp = cm_GetRxConn(connp);
3445         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3446                                &updatedDirStatus, &volSync);
3447         rx_PutConnection(rxconnp);
3448
3449     } while (cm_Analyze(connp, userp, reqp,
3450                         &dscp->fid, &volSync, NULL, NULL, code));
3451     code = cm_MapRPCErrorRmdir(code, reqp);
3452
3453     if (code)