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