a5c3d6ec1558bd47dd91fd1631ef1efcc75c55c2
[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, &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, &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
864         /* otherwise, we have to read it in */
865         offset.LowPart = offset.HighPart = 0;
866         code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
867         if (code)
868             goto done;
869
870         /*
871          * scp->length is the actual length of the mount point string.
872          * It is current because cm_GetData merged the most up to date
873          * status info into scp and has not dropped the rwlock since.
874          */
875         if (scp->length.LowPart > MOUNTPOINTLEN - 1) {
876             code = CM_ERROR_TOOBIG;
877             goto done;
878         }
879
880         if (scp->length.LowPart == 0) {
881             code = CM_ERROR_INVAL;
882             goto done;
883         }
884
885         /* convert the terminating dot to a NUL */
886         temp[scp->length.LowPart - 1] = 0;
887         memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
888         scp->mpDataVersion = scp->dataVersion;
889     }
890
891   done:
892     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_FETCHDATA);
893
894     return code;
895 }
896
897
898 /* called with a locked scp and chases the mount point, yielding outScpp.
899  * scp remains write locked, just for simplicity of describing the interface.
900  */
901 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
902                          cm_req_t *reqp, cm_scache_t **outScpp)
903 {
904     fschar_t *cellNamep = NULL;
905     fschar_t *volNamep = NULL;
906     afs_uint32 code;
907     fschar_t *cp;
908     fschar_t *mpNamep;
909     cm_volume_t *volp = NULL;
910     cm_cell_t *cellp;
911     fschar_t mtType;
912     cm_fid_t tfid;
913     size_t vnLength;
914     int targetType;
915
916     *outScpp = NULL;
917
918     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
919         tfid = scp->mountRootFid;
920         lock_ReleaseWrite(&scp->rw);
921         code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
922         lock_ObtainWrite(&scp->rw);
923         return code;
924     }
925
926     /* parse the volume name */
927     mpNamep = scp->mountPointStringp;
928     if (!mpNamep[0])
929         return CM_ERROR_NOSUCHPATH;
930     mtType = *scp->mountPointStringp;
931
932     cp = cm_FsStrChr(mpNamep, _FS(':'));
933     if (cp) {
934         /* cellular mount point */
935         cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
936         cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
937         volNamep = cm_FsStrDup(cp+1);
938
939         /* now look up the cell */
940         lock_ReleaseWrite(&scp->rw);
941         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE|CM_FLAG_NOPROBE);
942         lock_ObtainWrite(&scp->rw);
943     } else {
944         /* normal mt pt */
945         volNamep = cm_FsStrDup(mpNamep + 1);
946
947 #ifdef AFS_FREELANCE_CLIENT
948         /*
949          * Mount points in the Freelance cell should default
950          * to the workstation cell.
951          */
952         if (cm_freelanceEnabled &&
953              scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
954              scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
955         {
956             fschar_t rootCellName[256]="";
957             cm_GetRootCellName(rootCellName);
958             cellp = cm_GetCell(rootCellName, 0);
959         } else
960 #endif /* AFS_FREELANCE_CLIENT */
961             cellp = cm_FindCellByID(scp->fid.cell, 0);
962     }
963
964     if (!cellp) {
965         code = CM_ERROR_NOSUCHCELL;
966         goto done;
967     }
968
969     vnLength = cm_FsStrLen(volNamep);
970     if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
971         targetType = BACKVOL;
972     else if (vnLength >= 10
973              && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
974         targetType = ROVOL;
975     else
976         targetType = RWVOL;
977
978     /* check for backups within backups */
979     if (targetType == BACKVOL
980          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
981          == CM_SCACHEFLAG_RO) {
982         code = CM_ERROR_NOSUCHVOLUME;
983         goto done;
984     }
985
986     /* now we need to get the volume */
987     lock_ReleaseWrite(&scp->rw);
988     if (cm_VolNameIsID(volNamep)) {
989         code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
990                                 CM_GETVOL_FLAG_CREATE, &volp);
991     } else {
992         code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
993                                   CM_GETVOL_FLAG_CREATE, &volp);
994     }
995     lock_ObtainWrite(&scp->rw);
996
997     if (code == 0) {
998         afs_uint32 cell, volume;
999         cm_vol_state_t *statep;
1000
1001         cell = cellp->cellID;
1002
1003         /* if the mt pt originates in a .backup volume (not a .readonly)
1004          * and FollowBackupPath is active, and if there is a .backup
1005          * volume for the target, then use the .backup of the target
1006          * instead of the read-write.
1007          */
1008         if (cm_followBackupPath &&
1009             volp->vol[BACKVOL].ID != 0 &&
1010             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
1011             (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
1012             ) {
1013             targetType = BACKVOL;
1014         }
1015         /* if the mt pt is in a read-only volume (not just a
1016          * backup), and if there is a read-only volume for the
1017          * target, and if this is a targetType '#' mount point, use
1018          * the read-only, otherwise use the one specified.
1019          */
1020         else if (mtType == '#' && targetType == RWVOL &&
1021                  (scp->flags & CM_SCACHEFLAG_PURERO) &&
1022                  volp->vol[ROVOL].ID != 0) {
1023             targetType = ROVOL;
1024         }
1025
1026         lock_ObtainWrite(&volp->rw);
1027         statep = cm_VolumeStateByType(volp, targetType);
1028         volume = statep->ID;
1029         statep->dotdotFid = dscp->fid;
1030         lock_ReleaseWrite(&volp->rw);
1031
1032         /* the rest of the fid is a magic number */
1033         cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1034         scp->mountRootGen = cm_data.mountRootGen;
1035
1036         tfid = scp->mountRootFid;
1037         lock_ReleaseWrite(&scp->rw);
1038         code = cm_GetSCache(&tfid, NULL, outScpp, userp, reqp);
1039         lock_ObtainWrite(&scp->rw);
1040     }
1041
1042   done:
1043     if (volp)
1044         cm_PutVolume(volp);
1045     if (cellNamep)
1046         free(cellNamep);
1047     if (volNamep)
1048         free(volNamep);
1049     return code;
1050 }
1051
1052 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1053                        cm_req_t *reqp, cm_scache_t **outScpp)
1054 {
1055     long code;
1056     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1057     cm_scache_t *tscp = NULL;
1058     cm_scache_t *mountedScp;
1059     cm_lookupSearch_t rock;
1060     int getroot;
1061     normchar_t *nnamep = NULL;
1062     fschar_t *fnamep = NULL;
1063     size_t fnlen;
1064
1065     *outScpp = NULL;
1066
1067     memset(&rock, 0, sizeof(rock));
1068
1069     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1070         && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1071         if (dscp->dotdotFid.volume == 0)
1072             return CM_ERROR_NOSUCHVOLUME;
1073         rock.fid = dscp->dotdotFid;
1074         goto haveFid;
1075     } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1076         rock.fid = dscp->fid;
1077         goto haveFid;
1078     }
1079
1080     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1081     if (!nnamep) {
1082         code = CM_ERROR_NOSUCHFILE;
1083         goto done;
1084     }
1085     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1086     if (!fnamep) {
1087         code = CM_ERROR_NOSUCHFILE;
1088         goto done;
1089     }
1090
1091 retry_lookup:
1092     if (flags & CM_FLAG_NOMOUNTCHASE) {
1093         /* In this case, we should go and call cm_Dir* functions
1094            directly since the following cm_ApplyDir() function will
1095            not. */
1096
1097         cm_dirOp_t dirop;
1098 #ifdef USE_BPLUS
1099         int usedBplus = 0;
1100 #endif
1101
1102         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1103                              CM_DIROP_FLAG_NONE, &dirop);
1104         if (code == 0) {
1105 #ifdef USE_BPLUS
1106             code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1107             if (code != EINVAL)
1108                 usedBplus = 1;
1109             else
1110 #endif
1111                 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1112
1113             cm_EndDirOp(&dirop);
1114         }
1115
1116         if (code == 0) {
1117             /* found it */
1118             rock.found = TRUE;
1119             goto haveFid;
1120         }
1121 #ifdef USE_BPLUS
1122         if (usedBplus) {
1123             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1124                 /* found it */
1125                 code = 0;
1126                 rock.found = TRUE;
1127                 goto haveFid;
1128             }
1129
1130             code = CM_ERROR_BPLUS_NOMATCH;
1131             goto notfound;
1132         }
1133 #endif
1134     }
1135
1136     rock.fid.cell = dscp->fid.cell;
1137     rock.fid.volume = dscp->fid.volume;
1138     rock.searchNamep = fnamep;
1139     rock.nsearchNamep = nnamep;
1140     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1141     rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1142
1143     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1144     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1145                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1146
1147     /* code == 0 means we fell off the end of the dir, while stopnow means
1148      * that we stopped early, probably because we found the entry we're
1149      * looking for.  Any other non-zero code is an error.
1150      */
1151     if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1152         /* if the cm_scache_t we are searching in is not a directory
1153          * we must return path not found because the error
1154          * is to describe the final component not an intermediary
1155          */
1156         if (code == CM_ERROR_NOTDIR) {
1157             if (flags & CM_FLAG_CHECKPATH)
1158                 code = CM_ERROR_NOSUCHPATH;
1159             else
1160                 code = CM_ERROR_NOSUCHFILE;
1161         }
1162         goto done;
1163     }
1164
1165 notfound:
1166     getroot = (dscp==cm_data.rootSCachep) ;
1167     if (!rock.found) {
1168         if (!(cm_freelanceEnabled && cm_freelanceDiscovery) || !getroot) {
1169             if (flags & CM_FLAG_CHECKPATH)
1170                 code = CM_ERROR_NOSUCHPATH;
1171             else
1172                 code = CM_ERROR_NOSUCHFILE;
1173             goto done;
1174         }
1175         else if (!cm_ClientStrChr(cnamep, '#') &&
1176                  !cm_ClientStrChr(cnamep, '%') &&
1177                  cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1178                  cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1179                  cm_ClientStrCmpI(cnamep, _C("ipc$")))
1180         {
1181             /* nonexistent dir on freelance root, so add it */
1182             fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1183             int  found = 0;
1184             int  retry = 0;
1185
1186             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1187                      osi_LogSaveClientString(afsd_logp,cnamep));
1188
1189             /*
1190              * There is an ugly behavior where a share name "foo" will be searched
1191              * for as "fo".  If the searched for name differs by an already existing
1192              * symlink or mount point in the Freelance directory, do not add the
1193              * new value automatically.
1194              */
1195
1196             code = -1;
1197             fnlen = strlen(fnamep);
1198             if ( fnamep[fnlen-1] == '.') {
1199                 fnamep[fnlen-1] = '\0';
1200                 fnlen--;
1201                 retry = 1;
1202             }
1203
1204             if (cnamep[0] == '.') {
1205                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1206                     found = 1;
1207                     code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1208                     if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1209                         /*
1210                          * Do not permit symlinks that are one of:
1211                          *  . the cellname followed by a dot
1212                          *  . the cellname minus a single character
1213                          *  . a substring of the cellname that does not consist of full components
1214                          */
1215                         if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1216                              (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1217                         {
1218                             /* do not add; substitute fullname for the search */
1219                             free(fnamep);
1220                             fnamep = malloc(strlen(fullname)+2);
1221                             fnamep[0] = '.';
1222                             strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1223                             retry = 1;
1224                         } else {
1225                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1226                         }
1227                     }
1228                 }
1229             } else {
1230                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1231                     found = 1;
1232                     code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1233                     if ( cm_FsStrCmpI(fnamep, fullname)) {
1234                         /*
1235                          * Do not permit symlinks that are one of:
1236                          *  . the cellname followed by a dot
1237                          *  . the cellname minus a single character
1238                          *  . a substring of the cellname that does not consist of full components
1239                          */
1240                         if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1241                              (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1242                         {
1243                             /* do not add; substitute fullname for the search */
1244                                 free(fnamep);
1245                                 fnamep = strdup(fullname);
1246                                 code = 0;
1247                                 retry = 1;
1248                         } else {
1249                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1250                         }
1251                     }
1252                 }
1253             }
1254
1255             if (retry) {
1256                 if (nnamep)
1257                     free(nnamep);
1258                 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1259                 goto retry_lookup;
1260             }
1261
1262             if (!found || code) {   /* add mount point failed, so give up */
1263                 if (flags & CM_FLAG_CHECKPATH)
1264                     code = CM_ERROR_NOSUCHPATH;
1265                 else
1266                     code = CM_ERROR_NOSUCHFILE;
1267                 goto done;
1268             }
1269             tscp = NULL;   /* to force call of cm_GetSCache */
1270         } else {
1271             if (flags & CM_FLAG_CHECKPATH)
1272                 code = CM_ERROR_NOSUCHPATH;
1273             else
1274                 code = CM_ERROR_NOSUCHFILE;
1275             goto done;
1276         }
1277     }
1278
1279   haveFid:
1280     if ( !tscp )    /* we did not find it in the dnlc */
1281     {
1282         dnlcHit = 0;
1283         code = cm_GetSCache(&rock.fid, &dscp->fid, &tscp, userp, reqp);
1284         if (code)
1285             goto done;
1286     }
1287     /* tscp is now held */
1288
1289     lock_ObtainWrite(&tscp->rw);
1290
1291     /*
1292      * Do not get status if we do not already have a callback.
1293      * The process of reading the mount point string will obtain status information
1294      * in a single RPC.  No reason to add a second round trip.
1295      *
1296      * If we do have a callback, use cm_SyncOp to get status in case the
1297      * current cm_user_t is not the same as the one that obtained the
1298      * mount point string contents.
1299      */
1300     if (cm_HaveCallback(tscp)) {
1301         code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1302                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1303         if (code) {
1304             lock_ReleaseWrite(&tscp->rw);
1305             cm_ReleaseSCache(tscp);
1306             goto done;
1307         }
1308         cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1309     }
1310     /* tscp is now locked */
1311
1312     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1313          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1314         /* mount points are funny: they have a volume name to mount
1315          * the root of.
1316          */
1317         code = cm_ReadMountPoint(tscp, userp, reqp);
1318         if (code == 0)
1319             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1320                                        &mountedScp);
1321         lock_ReleaseWrite(&tscp->rw);
1322         cm_ReleaseSCache(tscp);
1323         if (code)
1324             goto done;
1325
1326         tscp = mountedScp;
1327     }
1328     else {
1329         lock_ReleaseWrite(&tscp->rw);
1330     }
1331
1332     /* copy back pointer */
1333     *outScpp = tscp;
1334
1335     /* insert scache in dnlc */
1336     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1337         /* lock the directory entry to prevent racing callback revokes */
1338         lock_ObtainRead(&dscp->rw);
1339         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1340             /* TODO: reuse nnamep from above */
1341             if (nnamep)
1342                 free(nnamep);
1343             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1344             if (nnamep)
1345                 cm_dnlcEnter(dscp, nnamep, tscp);
1346         }
1347         lock_ReleaseRead(&dscp->rw);
1348     }
1349
1350     /* and return */
1351   done:
1352     if (fnamep) {
1353         free (fnamep);
1354         fnamep = NULL;
1355     }
1356     if (nnamep) {
1357         free (nnamep);
1358         nnamep = NULL;
1359     }
1360
1361     return code;
1362 }
1363
1364 int cm_ExpandSysName(cm_req_t * reqp, clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1365 {
1366     clientchar_t *tp;
1367     int prefixCount;
1368 #ifdef _WIN64
1369     int use_sysname64 = 0;
1370
1371     if (cm_sysName64Count > 0 && reqp && !(reqp->flags & CM_REQ_WOW64) && (reqp->flags & CM_REQ_SOURCE_REDIR))
1372         use_sysname64 = 1;
1373 #endif
1374
1375     tp = cm_ClientStrRChr(inp, '@');
1376     if (tp == NULL)
1377         return 0;               /* no @sys */
1378
1379     if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1380         return 0;       /* no @sys */
1381
1382     /* caller just wants to know if this is a valid @sys type of name */
1383     if (outp == NULL)
1384         return 1;
1385
1386 #ifdef _WIN64
1387     if (use_sysname64 && index >= cm_sysName64Count)
1388         return -1;
1389     else
1390 #endif
1391     if (index >= cm_sysNameCount)
1392         return -1;
1393
1394     /* otherwise generate the properly expanded @sys name */
1395     prefixCount = (int)(tp - inp);
1396
1397     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);       /* copy out "a." from "a.@sys" */
1398     outp[prefixCount] = 0;                                      /* null terminate the "a." */
1399 #ifdef _WIN64
1400     if (use_sysname64)
1401         cm_ClientStrCat(outp, outSizeCch, cm_sysName64List[index]);
1402     else
1403 #endif
1404         cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);
1405
1406     return 1;
1407 }
1408
1409 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1410                                 cm_req_t *reqp, cm_scache_t ** outScpp)
1411 {
1412     afs_uint32    code = 0;
1413     fschar_t      cellName[CELL_MAXNAMELEN];
1414     fschar_t      volumeName[VL_MAXNAMELEN];
1415     size_t        len;
1416     fschar_t *        cp;
1417     fschar_t *        tp;
1418     fschar_t *        fnamep = NULL;
1419
1420     cm_cell_t *   cellp = NULL;
1421     cm_volume_t * volp = NULL;
1422     cm_fid_t      fid;
1423     afs_uint32    volume;
1424     int           volType;
1425     int           mountType = RWVOL;
1426
1427     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1428              osi_LogSaveClientString(afsd_logp, namep));
1429
1430     if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1431         goto _exit_invalid_path;
1432     }
1433
1434     /* namep is assumed to look like the following:
1435
1436        @vol:<cellname>%<volume>\0
1437        or
1438        @vol:<cellname>#<volume>\0
1439
1440      */
1441
1442     fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1443     cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1444     tp = cm_FsStrChr(cp, '%');
1445     if (tp == NULL)
1446         tp = cm_FsStrChr(cp, '#');
1447     if (tp == NULL ||
1448         (len = tp - cp) == 0 ||
1449         len > CELL_MAXNAMELEN)
1450         goto _exit_invalid_path;
1451     cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1452
1453     if (*tp == '#')
1454         mountType = ROVOL;
1455
1456     cp = tp+1;                  /* cp now points to volume, supposedly */
1457     cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1458
1459     /* OK, now we have the cell and the volume */
1460     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1461              osi_LogSaveFsString(afsd_logp, cellName),
1462              osi_LogSaveFsString(afsd_logp, volumeName));
1463
1464     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1465     if (cellp == NULL) {
1466         goto _exit_invalid_path;
1467     }
1468
1469     len = cm_FsStrLen(volumeName);
1470     if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1471         volType = BACKVOL;
1472     else if (len >= 10 &&
1473              cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1474         volType = ROVOL;
1475     else
1476         volType = RWVOL;
1477
1478     if (cm_VolNameIsID(volumeName)) {
1479         code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1480                                 CM_GETVOL_FLAG_CREATE, &volp);
1481     } else {
1482         code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1483                                   CM_GETVOL_FLAG_CREATE, &volp);
1484     }
1485
1486     if (code != 0)
1487         goto _exit_cleanup;
1488
1489     if (volType == BACKVOL)
1490         volume = volp->vol[BACKVOL].ID;
1491     else if (volType == ROVOL ||
1492              (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1493         volume = volp->vol[ROVOL].ID;
1494     else
1495         volume = volp->vol[RWVOL].ID;
1496
1497     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1498
1499     code = cm_GetSCache(&fid, NULL, outScpp, userp, reqp);
1500
1501   _exit_cleanup:
1502     if (fnamep)
1503         free(fnamep);
1504
1505     if (volp)
1506         cm_PutVolume(volp);
1507
1508     if (code == 0)
1509         return code;
1510
1511  _exit_invalid_path:
1512     if (flags & CM_FLAG_CHECKPATH)
1513         return CM_ERROR_NOSUCHPATH;
1514     else
1515         return CM_ERROR_NOSUCHFILE;
1516 }
1517
1518 #ifdef DEBUG_REFCOUNT
1519 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1520                cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1521 #else
1522 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1523                cm_req_t *reqp, cm_scache_t **outScpp)
1524 #endif
1525 {
1526     long code;
1527     clientchar_t tname[AFSPATHMAX];
1528     int sysNameIndex = 0;
1529     cm_scache_t *scp = NULL;
1530
1531 #ifdef DEBUG_REFCOUNT
1532     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1533     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1534 #endif
1535
1536     if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1537         if (flags & CM_FLAG_CHECKPATH)
1538             return CM_ERROR_NOSUCHPATH;
1539         else
1540             return CM_ERROR_NOSUCHFILE;
1541     }
1542
1543     if (dscp == cm_data.rootSCachep &&
1544         cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1545         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1546     }
1547
1548     if (cm_ExpandSysName(reqp, namep, NULL, 0, 0) > 0) {
1549         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1550             code = cm_ExpandSysName(reqp, namep, tname, lengthof(tname), sysNameIndex);
1551             if (code > 0) {
1552                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1553 #ifdef DEBUG_REFCOUNT
1554                 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);
1555                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1556 #endif
1557
1558                 if (code == 0) {
1559                     *outScpp = scp;
1560                     return 0;
1561                 }
1562                 if (scp) {
1563                     cm_ReleaseSCache(scp);
1564                     scp = NULL;
1565                 }
1566             } else {
1567                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1568 #ifdef DEBUG_REFCOUNT
1569                 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);
1570                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1571 #endif
1572                 *outScpp = scp;
1573                 return code;
1574             }
1575         }
1576     } else {
1577         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1578 #ifdef DEBUG_REFCOUNT
1579         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);
1580         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1581 #endif
1582         *outScpp = scp;
1583         return code;
1584     }
1585
1586     /* None of the possible sysName expansions could be found */
1587     if (flags & CM_FLAG_CHECKPATH)
1588         return CM_ERROR_NOSUCHPATH;
1589     else
1590         return CM_ERROR_NOSUCHFILE;
1591 }
1592
1593 /*! \brief Unlink a file name
1594
1595   Encapsulates a call to RXAFS_RemoveFile().
1596
1597   \param[in] dscp cm_scache_t pointing at the directory containing the
1598       name to be unlinked.
1599
1600   \param[in] fnamep Original name to be unlinked.  This is the
1601       name that will be passed into the RXAFS_RemoveFile() call.
1602       This parameter is optional.  If not provided, the value will
1603       be looked up.
1604
1605   \param[in] came Client name to be unlinked.  This name will be used
1606       to update the local directory caches.
1607
1608   \param[in] userp cm_user_t for the request.
1609
1610   \param[in] reqp Request tracker.
1611
1612  */
1613 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1614                cm_user_t *userp, cm_req_t *reqp)
1615 {
1616     long code;
1617     cm_conn_t *connp;
1618     AFSFid afsFid;
1619     int sflags;
1620     AFSFetchStatus newDirStatus;
1621     AFSVolSync volSync;
1622     struct rx_connection * rxconnp;
1623     cm_dirOp_t dirop;
1624     cm_scache_t *scp = NULL;
1625     int free_fnamep = FALSE;
1626     int invalidate = 0;
1627
1628     memset(&volSync, 0, sizeof(volSync));
1629
1630     if (fnamep == NULL) {
1631         code = -1;
1632 #ifdef USE_BPLUS
1633         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1634                              CM_DIROP_FLAG_NONE, &dirop);
1635         if (code == 0) {
1636             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1637             if (code == 0)
1638                 free_fnamep = TRUE;
1639             cm_EndDirOp(&dirop);
1640         }
1641 #endif
1642         if (code)
1643             goto done;
1644     }
1645
1646 #ifdef AFS_FREELANCE_CLIENT
1647     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1648         /* deleting a mount point from the root dir. */
1649         code = cm_FreelanceRemoveMount(fnamep);
1650         goto done;
1651     }
1652 #endif
1653
1654     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1655     if (code)
1656         goto done;
1657
1658     /* Check for RO volume */
1659     if (dscp->flags & CM_SCACHEFLAG_RO) {
1660         code = CM_ERROR_READONLY;
1661         goto done;
1662     }
1663
1664     /* make sure we don't screw up the dir status during the merge */
1665     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1666                          CM_DIROP_FLAG_NONE, &dirop);
1667
1668     lock_ObtainWrite(&dscp->rw);
1669     sflags = CM_SCACHESYNC_STOREDATA;
1670     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1671     lock_ReleaseWrite(&dscp->rw);
1672     if (code) {
1673         cm_EndDirOp(&dirop);
1674         goto done;
1675     }
1676
1677     /* make the RPC */
1678     InterlockedIncrement(&dscp->activeRPCs);
1679
1680     afsFid.Volume = dscp->fid.volume;
1681     afsFid.Vnode = dscp->fid.vnode;
1682     afsFid.Unique = dscp->fid.unique;
1683
1684     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1685     do {
1686         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1687         if (code)
1688             continue;
1689
1690         rxconnp = cm_GetRxConn(connp);
1691         code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1692                                 &newDirStatus, &volSync);
1693         rx_PutConnection(rxconnp);
1694
1695     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &newDirStatus, &volSync, NULL, NULL, code));
1696     code = cm_MapRPCError(code, reqp);
1697
1698     if (code)
1699         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1700     else
1701         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1702
1703     if (dirop.scp) {
1704         lock_ObtainWrite(&dirop.scp->dirlock);
1705         dirop.lockType = CM_DIRLOCK_WRITE;
1706     }
1707     lock_ObtainWrite(&dscp->rw);
1708     cm_dnlcRemove(dscp, cnamep);
1709     if (code == 0) {
1710         code = cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1711         invalidate = 1;
1712         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1713             lock_ReleaseWrite(&dscp->rw);
1714             cm_DirDeleteEntry(&dirop, fnamep);
1715 #ifdef USE_BPLUS
1716             cm_BPlusDirDeleteEntry(&dirop, cnamep);
1717 #endif
1718             lock_ObtainWrite(&dscp->rw);
1719         }
1720     } else {
1721         InterlockedDecrement(&scp->activeRPCs);
1722         if (code == CM_ERROR_NOSUCHFILE) {
1723             /* windows would not have allowed the request to delete the file
1724              * if it did not believe the file existed.  therefore, we must
1725              * have an inconsistent view of the world.
1726              */
1727             dscp->cbServerp = NULL;
1728         }
1729     }
1730
1731     cm_SyncOpDone(dscp, NULL, sflags);
1732     lock_ReleaseWrite(&dscp->rw);
1733
1734     cm_EndDirOp(&dirop);
1735
1736     if (invalidate && RDR_Initialized &&
1737         scp->fileType != CM_SCACHETYPE_FILE && scp->fileType != CM_SCACHETYPE_DIRECTORY)
1738         RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
1739                               dscp->fid.unique, dscp->fid.hash,
1740                               dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
1741
1742     if (scp) {
1743         if (code == 0) {
1744             lock_ObtainWrite(&scp->rw);
1745             if (--scp->linkCount == 0) {
1746                 scp->flags |= CM_SCACHEFLAG_DELETED;
1747                 lock_ObtainWrite(&cm_scacheLock);
1748                 cm_AdjustScacheLRU(scp);
1749                 cm_RemoveSCacheFromHashTable(scp);
1750                 lock_ReleaseWrite(&cm_scacheLock);
1751             }
1752             cm_DiscardSCache(scp);
1753             lock_ReleaseWrite(&scp->rw);
1754             if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
1755                 RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
1756                                      scp->fid.unique, scp->fid.hash,
1757                                      scp->fileType, AFS_INVALIDATE_DELETED);
1758         }
1759         cm_ReleaseSCache(scp);
1760     }
1761
1762   done:
1763     if (free_fnamep)
1764         free(fnamep);
1765
1766     return code;
1767 }
1768
1769 /* called with a write locked vnode, and fills in the link info.
1770  * returns this the vnode still write locked.
1771  */
1772 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1773 {
1774     long code = 0;
1775
1776     lock_AssertWrite(&linkScp->rw);
1777     if (!linkScp->mountPointStringp[0] ||
1778         linkScp->mpDataVersion != linkScp->dataVersion) {
1779
1780 #ifdef AFS_FREELANCE_CLIENT
1781         /* File servers do not have data for freelance entries */
1782         if (cm_freelanceEnabled &&
1783             linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1784             linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1785         {
1786             code = cm_FreelanceFetchMountPointString(linkScp);
1787         } else
1788 #endif /* AFS_FREELANCE_CLIENT */
1789         {
1790             char temp[MOUNTPOINTLEN];
1791             osi_hyper_t offset;
1792
1793             /* read the link data from the file server */
1794             offset.LowPart = offset.HighPart = 0;
1795             code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1796             if (code)
1797                 return code;
1798
1799             /*
1800              * linkScp->length is the actual length of the symlink target string.
1801              * It is current because cm_GetData merged the most up to date
1802              * status info into scp and has not dropped the rwlock since.
1803              */
1804             if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1805                 return CM_ERROR_TOOBIG;
1806             if (linkScp->length.LowPart == 0)
1807                 return CM_ERROR_INVAL;
1808
1809             /* make sure we are NUL terminated */
1810             temp[linkScp->length.LowPart] = 0;
1811             memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1812         }
1813
1814         if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1815             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1816
1817     }   /* don't have symlink contents cached */
1818
1819     return code;
1820 }
1821
1822 /* called with a held vnode and a path suffix, with the held vnode being a
1823  * symbolic link.  Our goal is to generate a new path to interpret, and return
1824  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1825  * other than the directory containing the symbolic link, then the new root is
1826  * returned in *newRootScpp, otherwise a null is returned there.
1827  */
1828 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1829                      cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1830                      cm_user_t *userp, cm_req_t *reqp)
1831 {
1832     long code = 0;
1833     long len;
1834     fschar_t *linkp;
1835     cm_space_t *tsp;
1836
1837     *newRootScpp = NULL;
1838     *newSpaceBufferp = NULL;
1839
1840     lock_ObtainWrite(&linkScp->rw);
1841     /*
1842      * Do not get status if we do not already have a callback.
1843      * The process of reading the symlink string will obtain status information
1844      * in a single RPC.  No reason to add a second round trip.
1845      *
1846      * If we do have a callback, use cm_SyncOp to get status in case the
1847      * current cm_user_t is not the same as the one that obtained the
1848      * symlink string contents.
1849      */
1850     if (cm_HaveCallback(linkScp)) {
1851         code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1852                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1853         if (code) {
1854             lock_ReleaseWrite(&linkScp->rw);
1855             cm_ReleaseSCache(linkScp);
1856             goto done;
1857         }
1858         cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1859     }
1860     code = cm_HandleLink(linkScp, userp, reqp);
1861     if (code)
1862         goto done;
1863
1864     /* if we may overflow the buffer, bail out; buffer is signficantly
1865      * bigger than max path length, so we don't really have to worry about
1866      * being a little conservative here.
1867      */
1868     if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1869         >= CM_UTILS_SPACESIZE) {
1870         code = CM_ERROR_TOOBIG;
1871         goto done;
1872     }
1873
1874     tsp = cm_GetSpace();
1875     linkp = linkScp->mountPointStringp;
1876     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1877         if (strlen(linkp) > cm_mountRootLen)
1878             StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1879         else
1880             tsp->data[0] = 0;
1881         *newRootScpp = cm_RootSCachep(userp, reqp);
1882         cm_HoldSCache(*newRootScpp);
1883     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1884         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1885         {
1886             char * p = &linkp[len + 3];
1887             if (strnicmp(p, "all", 3) == 0)
1888                 p += 4;
1889
1890             StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1891             for (p = tsp->data; *p; p++) {
1892                 if (*p == '\\')
1893                     *p = '/';
1894             }
1895             *newRootScpp = cm_RootSCachep(userp, reqp);
1896             cm_HoldSCache(*newRootScpp);
1897         } else {
1898             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1899             StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1900             code = CM_ERROR_PATH_NOT_COVERED;
1901         }
1902     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1903                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1904         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1905         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1906         code = CM_ERROR_PATH_NOT_COVERED;
1907     } else if (*linkp == '\\' || *linkp == '/') {
1908 #if 0
1909         /* formerly, this was considered to be from the AFS root,
1910          * but this seems to create problems.  instead, we will just
1911          * reject the link */
1912         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1913         *newRootScpp = cm_RootSCachep(userp, reqp);
1914         cm_HoldSCache(*newRootScpp);
1915 #else
1916         /* we still copy the link data into the response so that
1917          * the user can see what the link points to
1918          */
1919         linkScp->fileType = CM_SCACHETYPE_INVALID;
1920         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1921         code = CM_ERROR_NOSUCHPATH;
1922 #endif
1923     } else {
1924         /* a relative link */
1925         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1926     }
1927     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1928         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1929         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1930     }
1931
1932     if (code == 0) {
1933         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1934         if (cpath != NULL) {
1935         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1936         free(cpath);
1937         *newSpaceBufferp = tsp;
1938     } else {
1939             code = CM_ERROR_NOSUCHPATH;
1940         }
1941     }
1942
1943     if (code != 0) {
1944         cm_FreeSpace(tsp);
1945
1946         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1947             cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1948         }
1949     }
1950
1951  done:
1952     lock_ReleaseWrite(&linkScp->rw);
1953     return code;
1954 }
1955 #ifdef DEBUG_REFCOUNT
1956 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1957                  cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1958                  cm_scache_t **outScpp,
1959                  char * file, long line)
1960 #else
1961 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1962               cm_user_t *userp, clientchar_t *tidPathp,
1963               cm_req_t *reqp, cm_scache_t **outScpp)
1964 #endif
1965 {
1966     long code;
1967     clientchar_t *tp;                   /* ptr moving through input buffer */
1968     clientchar_t tc;                    /* temp char */
1969     int haveComponent;          /* has new component started? */
1970     clientchar_t component[AFSPATHMAX]; /* this is the new component */
1971     clientchar_t *cp;                   /* component name being assembled */
1972     cm_scache_t *tscp;          /* current location in the hierarchy */
1973     cm_scache_t *nscp;          /* next dude down */
1974     cm_scache_t *dirScp;        /* last dir we searched */
1975     cm_scache_t *linkScp;       /* new root for the symlink we just
1976     * looked up */
1977     cm_space_t *psp;            /* space for current path, if we've hit
1978     * any symlinks */
1979     cm_space_t *tempsp;         /* temp vbl */
1980     clientchar_t *restp;                /* rest of the pathname to interpret */
1981     int symlinkCount;           /* count of # of symlinks traversed */
1982     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1983     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1984     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1985     int fid_count = 0;          /* number of fids processed in this path walk */
1986     int i;
1987
1988     *outScpp = NULL;
1989
1990 #ifdef DEBUG_REFCOUNT
1991     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1992     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1993              rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1994              flags);
1995 #endif
1996
1997     tp = tidPathp;
1998     if (tp == NULL) {
1999         tp = pathp;
2000         phase = 2;
2001     }
2002     if (tp == NULL) {
2003         tp = _C("");
2004     }
2005     haveComponent = 0;
2006     psp = NULL;
2007     tscp = rootSCachep;
2008     cm_HoldSCache(tscp);
2009     symlinkCount = 0;
2010     dirScp = NULL;
2011
2012
2013     while (1) {
2014         tc = *tp++;
2015
2016         /* map Unix slashes into DOS ones so we can interpret Unix
2017          * symlinks properly
2018          */
2019         if (tc == '/')
2020             tc = '\\';
2021
2022         if (!haveComponent) {
2023             if (tc == '\\') {
2024                 continue;
2025             } else if (tc == 0) {
2026                 if (phase == 1) {
2027                     phase = 2;
2028                     tp = pathp;
2029                     continue;
2030                 }
2031                 code = 0;
2032                 break;
2033             } else {
2034                 haveComponent = 1;
2035                 cp = component;
2036                 *cp++ = tc;
2037             }
2038         } else {
2039             /* we have a component here */
2040             if (tc == 0 || tc == '\\') {
2041                 /* end of the component; we're at the last
2042                  * component if tc == 0.  However, if the last
2043                  * is a symlink, we have more to do.
2044                  */
2045                 *cp++ = 0;      /* add null termination */
2046                 extraFlag = 0;
2047                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2048                     extraFlag = CM_FLAG_NOMOUNTCHASE;
2049                 code = cm_Lookup(tscp, component,
2050                                  flags | extraFlag,
2051                                  userp, reqp, &nscp);
2052
2053                 if (code == 0) {
2054                     if (!cm_ClientStrCmp(component,_C("..")) ||
2055                         !cm_ClientStrCmp(component,_C("."))) {
2056                         /*
2057                          * roll back the fid list until we find the
2058                          * fid that matches where we are now.  Its not
2059                          * necessarily one or two fids because they
2060                          * might have been symlinks or mount points or
2061                          * both that were crossed.
2062                          */
2063                         for ( i=fid_count-1; i>=0; i--) {
2064                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
2065                                 break;
2066                         }
2067                         fid_count = i+1;
2068                     } else {
2069                         /* add the new fid to the list */
2070                         if (fid_count == MAX_FID_COUNT) {
2071                             code = CM_ERROR_TOO_MANY_SYMLINKS;
2072                             cm_ReleaseSCache(nscp);
2073                             nscp = NULL;
2074                             break;
2075                         }
2076                         fids[fid_count++] = nscp->fid;
2077                     }
2078                 }
2079
2080                 if (code) {
2081                     cm_ReleaseSCache(tscp);
2082                     if (dirScp)
2083                         cm_ReleaseSCache(dirScp);
2084                     if (psp)
2085                         cm_FreeSpace(psp);
2086                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2087                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2088                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2089                         return CM_ERROR_NOSUCHPATH;
2090                     } else {
2091                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2092                         return code;
2093                     }
2094                 }
2095
2096                 haveComponent = 0;      /* component done */
2097                 if (dirScp)
2098                     cm_ReleaseSCache(dirScp);
2099                 dirScp = tscp;          /* for some symlinks */
2100                 tscp = nscp;            /* already held */
2101                 nscp = NULL;
2102                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2103                     code = 0;
2104                     if (dirScp) {
2105                         cm_ReleaseSCache(dirScp);
2106                         dirScp = NULL;
2107                     }
2108                     break;
2109                 }
2110
2111                 /* now, if tscp is a symlink, we should follow it and
2112                  * assemble the path again.
2113                  */
2114                 lock_ObtainWrite(&tscp->rw);
2115                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2116                                   CM_SCACHESYNC_GETSTATUS
2117                                   | CM_SCACHESYNC_NEEDCALLBACK);
2118                 if (code) {
2119                     lock_ReleaseWrite(&tscp->rw);
2120                     cm_ReleaseSCache(tscp);
2121                     tscp = NULL;
2122                     if (dirScp) {
2123                         cm_ReleaseSCache(dirScp);
2124                         dirScp = NULL;
2125                     }
2126                     break;
2127                 }
2128                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2129
2130                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2131                     /* this is a symlink; assemble a new buffer */
2132                     lock_ReleaseWrite(&tscp->rw);
2133                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2134                         cm_ReleaseSCache(tscp);
2135                         tscp = NULL;
2136                         if (dirScp) {
2137                             cm_ReleaseSCache(dirScp);
2138                             dirScp = NULL;
2139                         }
2140                         if (psp)
2141                             cm_FreeSpace(psp);
2142                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2143                         return CM_ERROR_TOO_MANY_SYMLINKS;
2144                     }
2145                     if (tc == 0)
2146                         restp = _C("");
2147                     else
2148                         restp = tp;
2149
2150                     {
2151                         fschar_t * frestp;
2152
2153                         /* TODO: make this better */
2154                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2155                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2156                         free(frestp);
2157                     }
2158
2159                     if (code == 0 && linkScp != NULL) {
2160                         if (linkScp == cm_data.rootSCachep) {
2161                             fid_count = 0;
2162                             i = 0;
2163                         } else {
2164                             for ( i=0; i<fid_count; i++) {
2165                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2166                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2167                                     cm_ReleaseSCache(linkScp);
2168                                     nscp = NULL;
2169                                     break;
2170                                 }
2171                             }
2172                         }
2173                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2174                             fids[fid_count++] = linkScp->fid;
2175                         }
2176                     }
2177
2178                     if (code) {
2179                         /* something went wrong */
2180                         cm_ReleaseSCache(tscp);
2181                         tscp = NULL;
2182                         if (dirScp) {
2183                             cm_ReleaseSCache(dirScp);
2184                             dirScp = NULL;
2185                         }
2186                         break;
2187                     }
2188
2189                     /* otherwise, tempsp has the new path,
2190                      * and linkScp is the new root from
2191                      * which to interpret that path.
2192                      * Continue with the namei processing,
2193                      * also doing the bookkeeping for the
2194                      * space allocation and tracking the
2195                      * vnode reference counts.
2196                      */
2197                     if (psp)
2198                         cm_FreeSpace(psp);
2199                     psp = tempsp;
2200                     tp = psp->wdata;
2201                     cm_ReleaseSCache(tscp);
2202                     tscp = linkScp;
2203                     linkScp = NULL;
2204                     /* already held
2205                      * by AssembleLink
2206                      * now, if linkScp is null, that's
2207                      * AssembleLink's way of telling us that
2208                      * the sym link is relative to the dir
2209                      * containing the link.  We have a ref
2210                      * to it in dirScp, and we hold it now
2211                      * and reuse it as the new spot in the
2212                      * dir hierarchy.
2213                      */
2214                     if (tscp == NULL) {
2215                         tscp = dirScp;
2216                         dirScp = NULL;
2217                     }
2218                 } else {
2219                     /* not a symlink, we may be done */
2220                     lock_ReleaseWrite(&tscp->rw);
2221                     if (tc == 0) {
2222                         if (phase == 1) {
2223                             phase = 2;
2224                             tp = pathp;
2225                             continue;
2226                         }
2227                         if (dirScp) {
2228                             cm_ReleaseSCache(dirScp);
2229                             dirScp = NULL;
2230                         }
2231                         code = 0;
2232                         break;
2233                     }
2234                 }
2235                 if (dirScp) {
2236                     cm_ReleaseSCache(dirScp);
2237                     dirScp = NULL;
2238                 }
2239             } /* end of a component */
2240             else
2241                 *cp++ = tc;
2242         } /* we have a component */
2243     } /* big while loop over all components */
2244
2245     /* already held */
2246     if (dirScp)
2247         cm_ReleaseSCache(dirScp);
2248     if (psp)
2249         cm_FreeSpace(psp);
2250     if (code == 0)
2251         *outScpp = tscp;
2252     else if (tscp)
2253         cm_ReleaseSCache(tscp);
2254
2255 #ifdef DEBUG_REFCOUNT
2256     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2257 #endif
2258     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2259     return code;
2260 }
2261
2262 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2263  * We chase the link, and return a held pointer to the target, if it exists,
2264  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2265  * and do not hold or return a target vnode.
2266  *
2267  * This is very similar to calling cm_NameI with the last component of a name,
2268  * which happens to be a symlink, except that we've already passed by the name.
2269  *
2270  * This function is typically called by the directory listing functions, which
2271  * encounter symlinks but need to return the proper file length so programs
2272  * like "more" work properly when they make use of the attributes retrieved from
2273  * the dir listing.
2274  *
2275  * The input vnode should not be locked when this function is called.
2276  */
2277 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2278                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2279 {
2280     long code;
2281     cm_space_t *spacep;
2282     cm_scache_t *newRootScp;
2283
2284     *outScpp = NULL;
2285
2286     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2287
2288     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2289     if (code)
2290         return code;
2291
2292     /* now, if newRootScp is NULL, we're really being told that the symlink
2293      * is relative to the current directory (dscp).
2294      */
2295     if (newRootScp == NULL) {
2296         newRootScp = dscp;
2297         cm_HoldSCache(dscp);
2298     }
2299
2300     code = cm_NameI(newRootScp, spacep->wdata,
2301                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2302                     userp, NULL, reqp, outScpp);
2303
2304     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2305         code = CM_ERROR_NOSUCHPATH;
2306
2307     /* this stuff is allocated no matter what happened on the namei call,
2308      * so free it */
2309     cm_FreeSpace(spacep);
2310     cm_ReleaseSCache(newRootScp);
2311
2312     if (linkScp == *outScpp) {
2313         cm_ReleaseSCache(*outScpp);
2314         *outScpp = NULL;
2315         code = CM_ERROR_NOSUCHPATH;
2316     }
2317
2318     return code;
2319 }
2320
2321 /* for a given entry, make sure that it isn't in the stat cache, and then
2322  * add it to the list of file IDs to be obtained.
2323  *
2324  * Don't bother adding it if we already have a vnode.  Note that the dir
2325  * is locked, so we have to be careful checking the vnode we're thinking of
2326  * processing, to avoid deadlocks.
2327  */
2328 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2329                      osi_hyper_t *offp)
2330 {
2331     osi_hyper_t thyper;
2332     cm_bulkStat_t *bsp;
2333     int i;
2334     cm_scache_t *tscp;
2335     cm_fid_t tfid;
2336
2337     bsp = rockp;
2338
2339     /* Don't overflow bsp. */
2340     if (bsp->counter >= CM_BULKMAX)
2341         return CM_ERROR_STOPNOW;
2342
2343     thyper.LowPart = cm_data.buf_blockSize;
2344     thyper.HighPart = 0;
2345     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2346
2347     /* thyper is now the first byte past the end of the record we're
2348      * interested in, and bsp->bufOffset is the first byte of the record
2349      * we're interested in.
2350      * Skip data in the others.
2351      * Skip '.' and '..'
2352      */
2353     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2354         return 0;
2355     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2356         return CM_ERROR_STOPNOW;
2357     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2358         return 0;
2359
2360     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2361     tscp = cm_FindSCache(&tfid);
2362     if (tscp) {
2363         if (lock_TryWrite(&tscp->rw)) {
2364             /* we have an entry that we can look at */
2365             if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2366                 /* we have a callback on it.  Don't bother
2367                  * fetching this stat entry, since we're happy
2368                  * with the info we have.
2369                  */
2370                 lock_ReleaseWrite(&tscp->rw);
2371                 cm_ReleaseSCache(tscp);
2372                 return 0;
2373             }
2374             lock_ReleaseWrite(&tscp->rw);
2375         }       /* got lock */
2376         cm_ReleaseSCache(tscp);
2377     }   /* found entry */
2378
2379 #ifdef AFS_FREELANCE_CLIENT
2380     // yj: if this is a mountpoint under root.afs then we don't want it
2381     // to be bulkstat-ed, instead, we call getSCache directly and under
2382     // getSCache, it is handled specially.
2383     if  ( cm_freelanceEnabled &&
2384           tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2385           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2386           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2387     {
2388         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2389         return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2390     }
2391 #endif /* AFS_FREELANCE_CLIENT */
2392
2393     i = bsp->counter++;
2394     bsp->fids[i].Volume = scp->fid.volume;
2395     bsp->fids[i].Vnode = tfid.vnode;
2396     bsp->fids[i].Unique = tfid.unique;
2397     return 0;
2398 }
2399
2400 afs_int32
2401 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2402 {
2403     afs_int32 code = 0;
2404     AFSCBFids fidStruct;
2405     AFSBulkStats statStruct;
2406     cm_conn_t *connp;
2407     AFSCBs callbackStruct;
2408     long filex;
2409     AFSVolSync volSync;
2410     cm_callbackRequest_t cbReq;
2411     int lostRace;
2412     long filesThisCall;
2413     long i;
2414     long j;
2415     cm_scache_t *scp;
2416     cm_fid_t tfid;
2417     struct rx_connection * rxconnp;
2418     int inlinebulk;             /* Did we use InlineBulkStatus RPC or not? */
2419
2420     memset(&volSync, 0, sizeof(volSync));
2421
2422     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2423      * make the calls to create the entries.  Handle AFSCBMAX files at a
2424      * time.
2425      */
2426     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2427         filesThisCall = bbp->counter - filex;
2428         if (filesThisCall > AFSCBMAX)
2429             filesThisCall = AFSCBMAX;
2430
2431         fidStruct.AFSCBFids_len = filesThisCall;
2432         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2433         statStruct.AFSBulkStats_len = filesThisCall;
2434         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2435         callbackStruct.AFSCBs_len = filesThisCall;
2436         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2437         cm_StartCallbackGrantingCall(NULL, &cbReq);
2438         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2439
2440         /*
2441          * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2442          * be a FID provided.  However, the error code from RXAFS_BulkStatus
2443          * or RXAFS_InlinkBulkStatus does not apply to any FID.  Therefore,
2444          * we generate an invalid FID to match with the RPC error.
2445          */
2446         cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2447
2448         do {
2449             inlinebulk = 0;
2450
2451             code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2452             if (code)
2453                 continue;
2454
2455             rxconnp = cm_GetRxConn(connp);
2456             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2457                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2458                                               &statStruct, &callbackStruct, &volSync);
2459                 if (code == RXGEN_OPCODE) {
2460                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2461                 } else {
2462                     inlinebulk = 1;
2463                 }
2464             }
2465             if (!inlinebulk) {
2466                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2467                                         &statStruct, &callbackStruct, &volSync);
2468             }
2469             rx_PutConnection(rxconnp);
2470
2471             /*
2472              * If InlineBulk RPC was called and it succeeded,
2473              * then pull out the return code from the status info
2474              * and use it for cm_Analyze so that we can failover to other
2475              * .readonly volume instances.  But only do it for errors that
2476              * are volume global.
2477              */
2478             if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2479                 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2480                           (&bbp->stats[0])->errorCode);
2481                 switch ((&bbp->stats[0])->errorCode) {
2482                 case VBUSY:
2483                 case VRESTARTING:
2484                 case VNOVOL:
2485                 case VMOVED:
2486                 case VOFFLINE:
2487                 case VSALVAGE:
2488                 case VNOSERVICE:
2489                 case VIO:
2490                     code = (&bbp->stats[0])->errorCode;
2491                     break;
2492                 default:
2493                     /* Rx and Rxkad errors are volume global */
2494                     if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2495                          (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2496                         code = (&bbp->stats[0])->errorCode;
2497                 }
2498             }
2499         } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &bbp->stats[0], &volSync, NULL, &cbReq, code));
2500         code = cm_MapRPCError(code, reqp);
2501
2502         /*
2503          * might as well quit on an error, since we're not going to do
2504          * much better on the next immediate call, either.
2505          */
2506         if (code) {
2507             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2508                       inlinebulk ? "Inline" : "", code);
2509             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2510             break;
2511         }
2512
2513         /*
2514          * The bulk RPC has succeeded or at least not failed with a
2515          * volume global error result.  For items that have inlineBulk
2516          * errors we must call cm_Analyze in order to perform required
2517          * logging of errors.
2518          *
2519          * If the RPC was not inline bulk or the entry either has no error
2520          * the status must be merged.
2521          */
2522         osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2523
2524         for (i = 0; i<filesThisCall; i++) {
2525             j = filex + i;
2526             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2527
2528             if (inlinebulk && (&bbp->stats[j])->errorCode) {
2529                 cm_req_t treq = *reqp;
2530                 cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &bbp->stats[j], &volSync,
2531                            NULL, &cbReq, (&bbp->stats[j])->errorCode);
2532                 switch ((&bbp->stats[j])->errorCode) {
2533                 case EACCES:
2534                 case UAEACCES:
2535                 case EPERM:
2536                 case UAEPERM:
2537                     cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2538                 }
2539             } else {
2540                 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2541                 if (code != 0)
2542                     continue;
2543
2544                 /*
2545                  * otherwise, if this entry has no callback info,
2546                  * merge in this.  If there is existing callback info
2547                  * we skip the merge because the existing data must be
2548                  * current (we have a callback) and the response from
2549                  * a non-inline bulk rpc might actually be wrong.
2550                  *
2551                  * now, we have to be extra paranoid on merging in this
2552                  * information, since we didn't use cm_SyncOp before
2553                  * starting the fetch to make sure that no bad races
2554                  * were occurring.  Specifically, we need to make sure
2555                  * we don't obliterate any newer information in the
2556                  * vnode than have here.
2557                  *
2558                  * Right now, be pretty conservative: if there's a
2559                  * callback or a pending call, skip it.
2560                  * However, if the prior attempt to obtain status
2561                  * was refused access or the volume is .readonly,
2562                  * take the data in any case since we have nothing
2563                  * better for the in flight directory enumeration that
2564                  * resulted in this function being called.
2565                  */
2566                 lock_ObtainRead(&scp->rw);
2567                 if ((scp->cbServerp == NULL &&
2568                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2569                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
2570                      cm_EAccesFindEntry(userp, &scp->fid))
2571                 {
2572                     lock_ConvertRToW(&scp->rw);
2573                     lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2574                                                           &bbp->callbacks[j],
2575                                                           &volSync,
2576                                                           CM_CALLBACK_MAINTAINCOUNT|CM_CALLBACK_BULKSTAT);
2577                     InterlockedIncrement(&scp->activeRPCs);
2578                     if (!lostRace)
2579                         code = cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, CM_MERGEFLAG_BULKSTAT);
2580                     lock_ReleaseWrite(&scp->rw);
2581                 } else {
2582                     lock_ReleaseRead(&scp->rw);
2583                 }
2584                 cm_ReleaseSCache(scp);
2585             }
2586         } /* all files in the response */
2587         /* now tell it to drop the count,
2588          * after doing the vnode processing above */
2589         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2590     }   /* while there are still more files to process */
2591
2592     return code;
2593 }
2594
2595 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2596  * calls on all undeleted files in the page of the directory specified.
2597  */
2598 afs_int32
2599 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2600                cm_req_t *reqp)
2601 {
2602     afs_int32 code;
2603     cm_bulkStat_t *bbp;
2604
2605     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2606
2607     /* should be on a buffer boundary */
2608     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2609
2610     bbp = malloc(sizeof(cm_bulkStat_t));
2611     memset(bbp, 0, sizeof(cm_bulkStat_t));
2612     bbp->userp = userp;
2613     bbp->bufOffset = *offsetp;
2614
2615     lock_ReleaseWrite(&dscp->rw);
2616     /* first, assemble the file IDs we need to stat */
2617     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2618
2619     /* if we failed, bail out early */
2620     if (code && code != CM_ERROR_STOPNOW) {
2621         free(bbp);
2622         lock_ObtainWrite(&dscp->rw);
2623         return code;
2624     }
2625
2626     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2627     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2628
2629     lock_ObtainWrite(&dscp->rw);
2630     free(bbp);
2631     return 0;
2632 }
2633
2634 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2635 {
2636     long mask;
2637
2638     /* initialize store back mask as inexpensive local variable */
2639     mask = 0;
2640     memset(statusp, 0, sizeof(AFSStoreStatus));
2641
2642     /* copy out queued info from scache first, if scp passed in */
2643     if (scp) {
2644         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2645             statusp->ClientModTime = scp->clientModTime;
2646             mask |= AFS_SETMODTIME;
2647             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2648         }
2649     }
2650
2651     if (attrp) {
2652         /* now add in our locally generated request */
2653         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2654             statusp->ClientModTime = attrp->clientModTime;
2655             mask |= AFS_SETMODTIME;
2656         }
2657         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2658             statusp->UnixModeBits = attrp->unixModeBits;
2659             mask |= AFS_SETMODE;
2660         }
2661         if (attrp->mask & CM_ATTRMASK_OWNER) {
2662             statusp->Owner = attrp->owner;
2663             mask |= AFS_SETOWNER;
2664         }
2665         if (attrp->mask & CM_ATTRMASK_GROUP) {
2666             statusp->Group = attrp->group;
2667             mask |= AFS_SETGROUP;
2668         }
2669     }
2670     statusp->Mask = mask;
2671 }
2672
2673 int
2674 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2675 {
2676     int spaceAvail = 1;
2677     afs_uint32  code;
2678     cm_conn_t *connp;
2679     struct rx_connection * rxconnp;
2680     AFSFetchVolumeStatus volStat;
2681     cm_volume_t *volp = NULL;
2682     afs_uint32   volType;
2683     char *Name;
2684     char *OfflineMsg;
2685     char *MOTD;
2686     char volName[32]="(unknown)";
2687     char offLineMsg[256]="server temporarily inaccessible";
2688     char motd[256]="server temporarily inaccessible";
2689     osi_hyper_t freespace;
2690     cm_fid_t    vfid;
2691     cm_scache_t *vscp;
2692
2693     if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2694         fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2695     {
2696         goto _done;
2697     }
2698
2699     volp = cm_GetVolumeByFID(fidp);
2700     if (!volp) {
2701         spaceAvail = 0;
2702         goto _done;
2703     }
2704     volType = cm_VolumeType(volp, fidp->volume);
2705     if (volType == ROVOL || volType == BACKVOL) {
2706         spaceAvail = 0;
2707         goto _done;
2708     }
2709
2710     cm_SetFid(&vfid, fidp->cell, fidp->volume, 1, 1);
2711     code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
2712     if (code == 0) {
2713         lock_ObtainWrite(&vscp->rw);
2714         code = cm_SyncOp(vscp, NULL, userp, reqp, PRSFS_READ,
2715                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2716         lock_ReleaseWrite(&vscp->rw);
2717         if (code == 0) {
2718             Name = volName;
2719             OfflineMsg = offLineMsg;
2720             MOTD = motd;
2721
2722             do {
2723                 code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
2724                 if (code) continue;
2725
2726                 rxconnp = cm_GetRxConn(connp);
2727                 code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2728                                              &volStat, &Name, &OfflineMsg, &MOTD);
2729                 rx_PutConnection(rxconnp);
2730
2731             } while (cm_Analyze(connp, userp, reqp, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
2732             code = cm_MapRPCError(code, reqp);
2733         }
2734
2735         lock_ObtainWrite(&vscp->rw);
2736         cm_SyncOpDone(vscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2737         lock_ReleaseWrite(&vscp->rw);
2738         cm_ReleaseSCache(vscp);
2739     }
2740
2741     if (code == 0) {
2742         if (volStat.MaxQuota) {
2743             freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2744         } else {
2745             freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2746         }
2747         spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2748     }
2749     /* the rpc failed, assume there is space and we can fail it later. */
2750
2751   _done:
2752     if (volp)
2753         cm_PutVolume(volp);
2754
2755     return spaceAvail;
2756 }
2757
2758 /* set the file size, and make sure that all relevant buffers have been
2759  * truncated.  Ensure that any partially truncated buffers have been zeroed
2760  * to the end of the buffer.
2761  */
2762 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2763                    cm_req_t *reqp)
2764 {
2765     long code;
2766     int shrinking;
2767
2768     /* start by locking out buffer creation */
2769     lock_ObtainWrite(&scp->bufCreateLock);
2770
2771     /* verify that this is a file, not a dir or a symlink */
2772     lock_ObtainWrite(&scp->rw);
2773     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2774                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2775     if (code)
2776         goto done;
2777     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2778
2779     if (scp->fileType != CM_SCACHETYPE_FILE) {
2780         code = CM_ERROR_ISDIR;
2781         goto done;
2782     }
2783
2784   startover:
2785     if (LargeIntegerLessThan(*sizep, scp->length))
2786         shrinking = 1;
2787     else
2788         shrinking = 0;
2789
2790     lock_ReleaseWrite(&scp->rw);
2791
2792     /* can't hold scp->rw lock here, since we may wait for a storeback to
2793      * finish if the buffer package is cleaning a buffer by storing it to
2794      * the server.
2795      */
2796     if (shrinking)
2797         buf_Truncate(scp, userp, reqp, sizep);
2798
2799     /* now ensure that file length is short enough, and update truncPos */
2800     lock_ObtainWrite(&scp->rw);
2801
2802     /* make sure we have a callback (so we have the right value for the
2803      * length), and wait for it to be safe to do a truncate.
2804      */
2805     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2806                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2807                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2808
2809     /* If we only have 'i' bits, then we should still be able to set
2810        the size of a file we created. */
2811     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2812         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2813                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2814                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2815     }
2816
2817     if (code)
2818         goto done;
2819
2820     if (LargeIntegerLessThan(*sizep, scp->length)) {
2821         /* a real truncation.  If truncPos is not set yet, or is bigger
2822          * than where we're truncating the file, set truncPos to this
2823          * new value.
2824          */
2825         if (!shrinking)
2826             goto startover;
2827         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2828              || LargeIntegerLessThan(*sizep, scp->length)) {
2829             /* set trunc pos */
2830             scp->truncPos = *sizep;
2831             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2832         }
2833         /* in either case, the new file size has been changed */
2834         scp->length = *sizep;
2835         scp->mask |= CM_SCACHEMASK_LENGTH;
2836     }
2837     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2838         /* really extending the file */
2839         /* Check to see if we have sufficient quota */
2840         if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) {
2841             scp->length = *sizep;
2842             scp->mask |= CM_SCACHEMASK_LENGTH;
2843         } else {
2844             code = CM_ERROR_SPACE;
2845             goto syncopdone;
2846         }
2847     }
2848
2849     /* done successfully */
2850     code = 0;
2851
2852   syncopdone:
2853     cm_SyncOpDone(scp, NULL,
2854                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2855                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2856
2857   done:
2858     lock_ReleaseWrite(&scp->rw);
2859     lock_ReleaseWrite(&scp->bufCreateLock);
2860
2861     return code;
2862 }
2863
2864 /* set the file size or other attributes (but not both at once) */
2865 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2866                 cm_req_t *reqp)
2867 {
2868     long code;
2869     AFSFetchStatus afsOutStatus;
2870     AFSVolSync volSync;
2871     cm_conn_t *connp;
2872     AFSFid tfid;
2873     AFSStoreStatus afsInStatus;
2874     struct rx_connection * rxconnp;
2875
2876     memset(&volSync, 0, sizeof(volSync));
2877
2878     /* handle file length setting */
2879     if (attrp->mask & CM_ATTRMASK_LENGTH)
2880         return cm_SetLength(scp, &attrp->length, userp, reqp);
2881
2882     lock_ObtainWrite(&scp->rw);
2883     /* Check for RO volume */
2884     if (scp->flags & CM_SCACHEFLAG_RO) {
2885         code = CM_ERROR_READONLY;
2886         lock_ReleaseWrite(&scp->rw);
2887         return code;
2888     }
2889
2890     /* otherwise, we have to make an RPC to get the status */
2891     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2892     if (code) {
2893         lock_ReleaseWrite(&scp->rw);
2894         return code;
2895     }
2896     lock_ConvertWToR(&scp->rw);
2897
2898     /* make the attr structure */
2899     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2900
2901     tfid.Volume = scp->fid.volume;
2902     tfid.Vnode = scp->fid.vnode;
2903     tfid.Unique = scp->fid.unique;
2904     lock_ReleaseRead(&scp->rw);
2905
2906     /* now make the RPC */
2907     InterlockedIncrement(&scp->activeRPCs);
2908
2909     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2910     do {
2911         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2912         if (code)
2913             continue;
2914
2915         rxconnp = cm_GetRxConn(connp);
2916         code = RXAFS_StoreStatus(rxconnp, &tfid,
2917                                   &afsInStatus, &afsOutStatus, &volSync);
2918         rx_PutConnection(rxconnp);
2919
2920     } while (cm_Analyze(connp, userp, reqp,
2921                          &scp->fid, NULL, 1, &afsOutStatus, &volSync, NULL, NULL, code));
2922     code = cm_MapRPCError(code, reqp);
2923
2924     if (code)
2925         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2926     else
2927         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2928
2929     lock_ObtainWrite(&scp->rw);
2930     if (code == 0)
2931         code = cm_MergeStatus( NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2932                                CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2933     else
2934         InterlockedDecrement(&scp->activeRPCs);
2935     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2936
2937     /* if we're changing the mode bits, discard the ACL cache,
2938      * since we changed the mode bits.
2939      */
2940     if (afsInStatus.Mask & AFS_SETMODE)
2941         cm_FreeAllACLEnts(scp);
2942     lock_ReleaseWrite(&scp->rw);
2943     return code;
2944 }
2945
2946 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2947                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2948 {
2949     cm_conn_t *connp;
2950     long code;
2951     AFSFid dirAFSFid;
2952     cm_callbackRequest_t cbReq;
2953     AFSFid newAFSFid;
2954     cm_fid_t newFid;
2955     cm_scache_t *scp = NULL;
2956     int didEnd;
2957     int lostRace;
2958     AFSStoreStatus inStatus;
2959     AFSFetchStatus updatedDirStatus;
2960     AFSFetchStatus newFileStatus;
2961     AFSCallBack newFileCallback;
2962     AFSVolSync volSync;
2963     struct rx_connection * rxconnp;
2964     cm_dirOp_t dirop;
2965     fschar_t * fnamep = NULL;
2966
2967     memset(&volSync, 0, sizeof(volSync));
2968
2969     /* can't create names with @sys in them; must expand it manually first.
2970      * return "invalid request" if they try.
2971      */
2972     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2973         return CM_ERROR_ATSYS;
2974     }
2975
2976 #ifdef AFS_FREELANCE_CLIENT
2977     /* Freelance root volume does not hold files */
2978     if (cm_freelanceEnabled &&
2979         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2980         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2981     {
2982         return CM_ERROR_NOACCESS;
2983     }
2984 #endif /* AFS_FREELANCE_CLIENT */
2985
2986     /* Check for RO volume */
2987     if (dscp->flags & CM_SCACHEFLAG_RO)
2988         return CM_ERROR_READONLY;
2989
2990     /* before starting the RPC, mark that we're changing the file data, so
2991      * that someone who does a chmod will know to wait until our call
2992      * completes.
2993      */
2994     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2995                   &dirop);
2996     lock_ObtainWrite(&dscp->rw);
2997     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2998     lock_ReleaseWrite(&dscp->rw);
2999     if (code == 0) {
3000         cm_StartCallbackGrantingCall(NULL, &cbReq);
3001     } else {
3002         cm_EndDirOp(&dirop);
3003     }
3004     if (code) {
3005         return code;
3006     }
3007     didEnd = 0;
3008
3009     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3010
3011     cm_StatusFromAttr(&inStatus, NULL, attrp);
3012
3013     /* try the RPC now */
3014     InterlockedIncrement(&dscp->activeRPCs);
3015     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
3016     do {
3017         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3018         if (code)
3019             continue;
3020
3021         dirAFSFid.Volume = dscp->fid.volume;
3022         dirAFSFid.Vnode = dscp->fid.vnode;
3023         dirAFSFid.Unique = dscp->fid.unique;
3024
3025         rxconnp = cm_GetRxConn(connp);
3026         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
3027                                  &inStatus, &newAFSFid, &newFileStatus,
3028                                  &updatedDirStatus, &newFileCallback,
3029                                  &volSync);
3030         rx_PutConnection(rxconnp);
3031
3032     } while (cm_Analyze(connp, userp, reqp,
3033                          &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3034     code = cm_MapRPCError(code, reqp);
3035
3036     if (code)
3037         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3038     else
3039         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3040
3041     if (dirop.scp) {
3042         lock_ObtainWrite(&dirop.scp->dirlock);
3043         dirop.lockType = CM_DIRLOCK_WRITE;
3044     }
3045     lock_ObtainWrite(&dscp->rw);
3046     if (code == 0) {
3047         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3048         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3049         if (cm_CheckDirOpForSingleChange(&dirop)) {
3050             lock_ReleaseWrite(&dscp->rw);
3051             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3052 #ifdef USE_BPLUS
3053             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3054 #endif
3055             lock_ObtainWrite(&dscp->rw);
3056         }
3057     } else {
3058         InterlockedDecrement(&dscp->activeRPCs);
3059     }
3060     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3061     lock_ReleaseWrite(&dscp->rw);
3062
3063     /* now try to create the file's entry, too, but be careful to
3064      * make sure that we don't merge in old info.  Since we weren't locking
3065      * out any requests during the file's creation, we may have pretty old
3066      * info.
3067      */
3068     if (code == 0) {
3069         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3070         if (code == 0) {
3071             lock_ObtainWrite(&scp->rw);
3072             scp->creator = userp;               /* remember who created it */
3073             if (!cm_HaveCallback(scp)) {
3074                 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3075                                                       &newFileCallback, &volSync, 0);
3076                 InterlockedIncrement(&scp->activeRPCs);
3077                 if (!lostRace)
3078                     code = cm_MergeStatus( dscp, scp, &newFileStatus, &volSync,
3079                                            userp, reqp, 0);
3080                 didEnd = 1;
3081             }
3082             lock_ReleaseWrite(&scp->rw);
3083         }
3084     }
3085
3086     /* make sure we end things properly */
3087     if (!didEnd)
3088         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3089
3090     cm_EndDirOp(&dirop);
3091
3092     if (fnamep)
3093         free(fnamep);
3094
3095     if (scp) {
3096         if (scpp)
3097             *scpp = scp;
3098         else
3099             cm_ReleaseSCache(scp);
3100     }
3101     return code;
3102 }
3103
3104 /*
3105  * locked if TRUE means write-locked
3106  * else the cm_scache_t rw must not be held
3107  */
3108 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3109 {
3110     long code;
3111
3112     if (locked)
3113         lock_ReleaseWrite(&scp->rw);
3114
3115     osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3116
3117     code = buf_CleanVnode(scp, userp, reqp);
3118     if (code == 0) {
3119         lock_ObtainWrite(&scp->rw);
3120
3121         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3122                           | CM_SCACHEMASK_CLIENTMODTIME
3123                           | CM_SCACHEMASK_LENGTH))
3124             code = cm_StoreMini(scp, userp, reqp);
3125
3126         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3127             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3128             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3129         }
3130
3131         if (!locked)
3132             lock_ReleaseWrite(&scp->rw);
3133     } else if (locked) {
3134         lock_ObtainWrite(&scp->rw);
3135     }
3136     return code;
3137 }
3138
3139 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3140                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3141 {
3142     cm_conn_t *connp;
3143     long code;
3144     AFSFid dirAFSFid;
3145     cm_callbackRequest_t cbReq;
3146     AFSFid newAFSFid;
3147     cm_fid_t newFid;
3148     cm_scache_t *scp = NULL;
3149     int didEnd;
3150     int lostRace;
3151     AFSStoreStatus inStatus;
3152     AFSFetchStatus updatedDirStatus;
3153     AFSFetchStatus newDirStatus;
3154     AFSCallBack newDirCallback;
3155     AFSVolSync volSync;
3156     struct rx_connection * rxconnp;
3157     cm_dirOp_t dirop;
3158     fschar_t * fnamep = NULL;
3159
3160     memset(&volSync, 0, sizeof(volSync));
3161
3162     /* can't create names with @sys in them; must expand it manually first.
3163      * return "invalid request" if they try.
3164      */
3165     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3166         return CM_ERROR_ATSYS;
3167     }
3168
3169 #ifdef AFS_FREELANCE_CLIENT
3170     /* Freelance root volume does not hold subdirectories */
3171     if (cm_freelanceEnabled &&
3172         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3173         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3174     {
3175         return CM_ERROR_NOACCESS;
3176     }
3177 #endif /* AFS_FREELANCE_CLIENT */
3178
3179     /* Check for RO volume */
3180     if (dscp->flags & CM_SCACHEFLAG_RO)
3181         return CM_ERROR_READONLY;
3182
3183     /* before starting the RPC, mark that we're changing the directory
3184      * data, so that someone who does a chmod on the dir will wait until
3185      * our call completes.
3186      */
3187     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3188                   &dirop);
3189     lock_ObtainWrite(&dscp->rw);
3190     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3191     lock_ReleaseWrite(&dscp->rw);
3192     if (code == 0) {
3193         cm_StartCallbackGrantingCall(NULL, &cbReq);
3194     } else {
3195         cm_EndDirOp(&dirop);
3196     }
3197     if (code) {
3198         return code;
3199     }
3200     didEnd = 0;
3201
3202     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3203     cm_StatusFromAttr(&inStatus, NULL, attrp);
3204
3205     /* try the RPC now */
3206     InterlockedIncrement(&dscp->activeRPCs);
3207     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3208     do {
3209         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3210         if (code)
3211             continue;
3212
3213         dirAFSFid.Volume = dscp->fid.volume;
3214         dirAFSFid.Vnode = dscp->fid.vnode;
3215         dirAFSFid.Unique = dscp->fid.unique;
3216
3217         rxconnp = cm_GetRxConn(connp);
3218         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3219                               &inStatus, &newAFSFid, &newDirStatus,
3220                               &updatedDirStatus, &newDirCallback,
3221                               &volSync);
3222         rx_PutConnection(rxconnp);
3223
3224     } while (cm_Analyze(connp, userp, reqp,
3225                         &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3226     code = cm_MapRPCError(code, reqp);
3227
3228     if (code)
3229         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3230     else
3231         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3232
3233     if (dirop.scp) {
3234         lock_ObtainWrite(&dirop.scp->dirlock);
3235         dirop.lockType = CM_DIRLOCK_WRITE;
3236     }
3237     lock_ObtainWrite(&dscp->rw);
3238     if (code == 0) {
3239         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3240         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3241         if (cm_CheckDirOpForSingleChange(&dirop)) {
3242             lock_ReleaseWrite(&dscp->rw);
3243             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3244 #ifdef USE_BPLUS
3245             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3246 #endif
3247             lock_ObtainWrite(&dscp->rw);
3248         }
3249     } else {
3250         InterlockedDecrement(&dscp->activeRPCs);
3251     }
3252     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3253     lock_ReleaseWrite(&dscp->rw);
3254
3255     /* now try to create the new dir's entry, too, but be careful to
3256      * make sure that we don't merge in old info.  Since we weren't locking
3257      * out any requests during the file's creation, we may have pretty old
3258      * info.
3259      */
3260     if (code == 0) {
3261         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3262         if (code == 0) {
3263             lock_ObtainWrite(&scp->rw);
3264             if (!cm_HaveCallback(scp)) {
3265                 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3266                                                       &newDirCallback, &volSync, 0);
3267                 InterlockedIncrement(&scp->activeRPCs);
3268                 if (!lostRace)
3269                     code = cm_MergeStatus( dscp, scp, &newDirStatus, &volSync,
3270                                            userp, reqp, 0);
3271                 didEnd = 1;
3272             }
3273             lock_ReleaseWrite(&scp->rw);
3274         }
3275     }
3276
3277     /* make sure we end things properly */
3278     if (!didEnd)
3279         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3280
3281     cm_EndDirOp(&dirop);
3282
3283     free(fnamep);
3284
3285     if (scp) {
3286         if (scpp)
3287             *scpp = scp;
3288         else
3289             cm_ReleaseSCache(scp);
3290     }
3291
3292     /* and return error code */
3293     return code;
3294 }
3295
3296 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3297              cm_user_t *userp, cm_req_t *reqp)
3298 {
3299     cm_conn_t *connp;
3300     long code = 0;
3301     AFSFid dirAFSFid;
3302     AFSFid existingAFSFid;
3303     AFSFetchStatus updatedDirStatus;
3304     AFSFetchStatus newLinkStatus;
3305     AFSVolSync volSync;
3306     struct rx_connection * rxconnp;
3307     cm_dirOp_t dirop;
3308     fschar_t * fnamep = NULL;
3309     int invalidate = 0;
3310
3311     memset(&volSync, 0, sizeof(volSync));
3312
3313     if (dscp->fid.cell != sscp->fid.cell ||
3314         dscp->fid.volume != sscp->fid.volume) {
3315         return CM_ERROR_CROSSDEVLINK;
3316     }
3317
3318     /* Check for RO volume */
3319     if (dscp->flags & CM_SCACHEFLAG_RO)
3320         return CM_ERROR_READONLY;
3321
3322     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3323                   &dirop);
3324     lock_ObtainWrite(&dscp->rw);
3325     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3326     lock_ReleaseWrite(&dscp->rw);
3327     if (code != 0)
3328         cm_EndDirOp(&dirop);
3329
3330     if (code)
3331         return code;
3332
3333     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3334
3335     /* try the RPC now */
3336     InterlockedIncrement(&dscp->activeRPCs);
3337     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3338     do {
3339         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3340         if (code) continue;
3341
3342         dirAFSFid.Volume = dscp->fid.volume;
3343         dirAFSFid.Vnode = dscp->fid.vnode;
3344         dirAFSFid.Unique = dscp->fid.unique;
3345
3346         existingAFSFid.Volume = sscp->fid.volume;
3347         existingAFSFid.Vnode = sscp->fid.vnode;
3348         existingAFSFid.Unique = sscp->fid.unique;
3349
3350         rxconnp = cm_GetRxConn(connp);
3351         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3352             &newLinkStatus, &updatedDirStatus, &volSync);
3353         rx_PutConnection(rxconnp);
3354         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3355
3356     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3357
3358     code = cm_MapRPCError(code, reqp);
3359
3360     if (code)
3361         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3362     else
3363         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3364
3365     if (dirop.scp) {
3366         lock_ObtainWrite(&dirop.scp->dirlock);
3367         dirop.lockType = CM_DIRLOCK_WRITE;
3368     }
3369     lock_ObtainWrite(&dscp->rw);
3370     if (code == 0) {
3371         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3372         invalidate = 1;
3373
3374         if (cm_CheckDirOpForSingleChange(&dirop)) {
3375             lock_ReleaseWrite(&dscp->rw);
3376             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3377 #ifdef USE_BPLUS
3378             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3379 #endif
3380             lock_ObtainWrite(&dscp->rw);
3381         }
3382     } else {
3383         InterlockedDecrement(&dscp->activeRPCs);
3384     }
3385     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3386     lock_ReleaseWrite(&dscp->rw);
3387
3388     cm_EndDirOp(&dirop);
3389
3390     if (invalidate && RDR_Initialized)
3391         RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3392                              dscp->fid.unique, dscp->fid.hash,
3393                              dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3394
3395     /* Update the linked object status */
3396     if (code == 0) {
3397         lock_ObtainWrite(&sscp->rw);
3398         InterlockedIncrement(&sscp->activeRPCs);
3399         code = cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3400         lock_ReleaseWrite(&sscp->rw);
3401     }
3402
3403     free(fnamep);
3404
3405     return code;
3406 }
3407
3408 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3409                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3410 {
3411     cm_conn_t *connp;
3412     long code;
3413     AFSFid dirAFSFid;
3414     AFSFid newAFSFid;
3415     cm_fid_t newFid;
3416     cm_scache_t *scp;
3417     AFSStoreStatus inStatus;
3418     AFSFetchStatus updatedDirStatus;
3419     AFSFetchStatus newLinkStatus;
3420     AFSVolSync volSync;
3421     struct rx_connection * rxconnp;
3422     cm_dirOp_t dirop;
3423     fschar_t *fnamep = NULL;
3424
3425     if (scpp)
3426         *scpp = NULL;
3427
3428     /* Check for RO volume */
3429     if (dscp->flags & CM_SCACHEFLAG_RO)
3430         return CM_ERROR_READONLY;
3431
3432     memset(&volSync, 0, sizeof(volSync));
3433
3434     /* before starting the RPC, mark that we're changing the directory data,
3435      * so that someone who does a chmod on the dir will wait until our
3436      * call completes.
3437      */
3438     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3439                   &dirop);
3440     lock_ObtainWrite(&dscp->rw);
3441     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3442     lock_ReleaseWrite(&dscp->rw);
3443     if (code != 0)
3444         cm_EndDirOp(&dirop);
3445     if (code) {
3446         return code;
3447     }
3448
3449     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3450
3451     cm_StatusFromAttr(&inStatus, NULL, attrp);
3452
3453     /* try the RPC now */
3454     InterlockedIncrement(&dscp->activeRPCs);
3455     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3456     do {
3457         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3458         if (code)
3459             continue;
3460
3461         dirAFSFid.Volume = dscp->fid.volume;
3462         dirAFSFid.Vnode = dscp->fid.vnode;
3463         dirAFSFid.Unique = dscp->fid.unique;
3464
3465         rxconnp = cm_GetRxConn(connp);
3466         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3467                               &inStatus, &newAFSFid, &newLinkStatus,
3468                               &updatedDirStatus, &volSync);
3469         rx_PutConnection(rxconnp);
3470
3471     } while (cm_Analyze(connp, userp, reqp,
3472                          &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3473     code = cm_MapRPCError(code, reqp);
3474
3475     if (code)
3476         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3477     else
3478         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3479
3480     if (dirop.scp) {
3481         lock_ObtainWrite(&dirop.scp->dirlock);
3482         dirop.lockType = CM_DIRLOCK_WRITE;
3483     }
3484     lock_ObtainWrite(&dscp->rw);
3485     if (code == 0) {
3486         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3487         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3488         if (cm_CheckDirOpForSingleChange(&dirop)) {
3489             lock_ReleaseWrite(&dscp->rw);
3490             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3491
3492             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3493 #ifdef USE_BPLUS
3494             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3495 #endif
3496             lock_ObtainWrite(&dscp->rw);
3497         }
3498     } else {
3499         InterlockedDecrement(&dscp->activeRPCs);
3500     }
3501     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3502     lock_ReleaseWrite(&dscp->rw);
3503
3504     cm_EndDirOp(&dirop);
3505
3506     /* now try to create the new dir's entry, too, but be careful to
3507      * make sure that we don't merge in old info.  Since we weren't locking
3508      * out any requests during the file's creation, we may have pretty old
3509      * info.
3510      */
3511     if (code == 0) {
3512         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3513         if (code == 0) {
3514             lock_ObtainWrite(&scp->rw);
3515             if (!cm_HaveCallback(scp)) {
3516                 InterlockedIncrement(&scp->activeRPCs);
3517                 code = cm_MergeStatus( dscp, scp, &newLinkStatus, &volSync,
3518                                        userp, reqp, 0);
3519             }
3520             lock_ReleaseWrite(&scp->rw);
3521
3522             if (scpp) {
3523                 *scpp = scp;
3524             } else {
3525                 cm_ReleaseSCache(scp);
3526             }
3527         }
3528     }
3529
3530     free(fnamep);
3531
3532     /* and return error code */
3533     return code;
3534 }
3535
3536 /*! \brief Remove a directory
3537
3538   Encapsulates a call to RXAFS_RemoveDir().
3539
3540   \param[in] dscp cm_scache_t for the directory containing the
3541       directory to be removed.
3542
3543   \param[in] fnamep This will be the original name of the directory
3544       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3545       This parameter is optional.  If it is not provided the value
3546       will be looked up.
3547
3548   \param[in] cnamep Normalized name used to update the local
3549       directory caches.
3550
3551   \param[in] userp cm_user_t for the request.
3552
3553   \param[in] reqp Request tracker.
3554 */
3555 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3556 {
3557     cm_conn_t *connp;
3558     long code;
3559     AFSFid dirAFSFid;
3560     int didEnd;
3561     AFSFetchStatus updatedDirStatus;
3562     AFSVolSync volSync;
3563     struct rx_connection * rxconnp;
3564     cm_dirOp_t dirop;
3565     cm_scache_t *scp = NULL;
3566     int free_fnamep = FALSE;
3567
3568     memset(&volSync, 0, sizeof(volSync));
3569
3570     if (fnamep == NULL) {
3571         code = -1;
3572 #ifdef USE_BPLUS
3573         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3574                              CM_DIROP_FLAG_NONE, &dirop);
3575         if (code == 0) {
3576             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3577             if (code == 0)
3578                 free_fnamep = TRUE;
3579             cm_EndDirOp(&dirop);
3580         }
3581 #endif
3582         if (code)
3583             goto done;
3584     }
3585
3586     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3587     if (code)
3588         goto done;
3589
3590     /* Check for RO volume */
3591     if (dscp->flags & CM_SCACHEFLAG_RO) {
3592         code = CM_ERROR_READONLY;
3593         goto done;
3594     }
3595
3596     /* before starting the RPC, mark that we're changing the directory data,
3597      * so that someone who does a chmod on the dir will wait until our
3598      * call completes.
3599      */
3600     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3601                   &dirop);
3602     lock_ObtainWrite(&dscp->rw);
3603     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3604     lock_ReleaseWrite(&dscp->rw);
3605     if (code) {
3606         cm_EndDirOp(&dirop);
3607         goto done;
3608     }
3609     didEnd = 0;
3610
3611     /* try the RPC now */
3612     InterlockedIncrement(&dscp->activeRPCs);
3613     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3614     do {
3615         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3616         if (code)
3617             continue;
3618
3619         dirAFSFid.Volume = dscp->fid.volume;
3620         dirAFSFid.Vnode = dscp->fid.vnode;
3621         dirAFSFid.Unique = dscp->fid.unique;
3622
3623         rxconnp = cm_GetRxConn(connp);
3624         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3625                                &updatedDirStatus, &volSync);
3626         rx_PutConnection(rxconnp);
3627
3628     } while (cm_Analyze(connp, userp, reqp,
3629                         &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3630     code = cm_MapRPCErrorRmdir(code, reqp);
3631
3632     if (code)
3633         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3634     else
3635         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3636
3637     if (dirop.scp) {
3638         lock_ObtainWrite(&dirop.scp->dirlock);
3639         dirop.lockType = CM_DIRLOCK_WRITE;
3640     }
3641     lock_ObtainWrite(&dscp->rw);
3642     if (code == 0) {
3643         cm_dnlcRemove(dscp, cnamep);
3644         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3645         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3646             lock_ReleaseWrite(&dscp->rw);
3647             cm_DirDeleteEntry(&dirop, fnamep);
3648 #ifdef USE_BPLUS
3649             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3650 #endif
3651             lock_ObtainWrite(&dscp->rw);
3652         }
3653     } else {
3654         InterlockedDecrement(&dscp->activeRPCs);
3655     }
3656     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3657     lock_ReleaseWrite(&dscp->rw);
3658
3659     cm_EndDirOp(&dirop);
3660
3661     if (scp) {
3662         if (code == 0) {
3663             lock_ObtainWrite(&scp->rw);
3664             scp->flags |= CM_SCACHEFLAG_DELETED;
3665             lock_ObtainWrite(&cm_scacheLock);
3666             cm_AdjustScacheLRU(scp);
3667             cm_RemoveSCacheFromHashTable(scp);
3668             lock_ReleaseWrite(&cm_scacheLock);
3669             lock_ReleaseWrite(&scp->rw);
3670             if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
3671                 RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3672                                      scp->fid.unique, scp->fid.hash,
3673                                      scp->fileType, AFS_INVALIDATE_DELETED);
3674         }
3675         cm_ReleaseSCache(scp);
3676     }
3677
3678   done:
3679     if (free_fnamep)
3680         free(fnamep);
3681
3682     /* and return error code */
3683     return code;
3684 }
3685
3686 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3687 {
3688     /* grab mutex on contents */
3689     lock_ObtainWrite(&scp->rw);
3690
3691     /* reset the prefetch info */
3692     scp->prefetch.base.LowPart = 0;             /* base */
3693     scp->prefetch.base.HighPart = 0;
3694     scp->prefetch.end.LowPart = 0;              /* and end */
3695     scp->prefetch.end.HighPart = 0;
3696
3697     /* release mutex on contents */
3698     lock_ReleaseWrite(&scp->rw);
3699
3700     /* we're done */
3701     return 0;
3702 }
3703
3704 /*! \brief Rename a file or directory
3705
3706   Encapsulates a RXAFS_Rename() call.
3707
3708   \param[in] oldDscp cm_scache_t for the directory containing the old
3709       name.
3710
3711   \param[in] oldNamep The original old name known to the file server.
3712       This is the name that will be passed into the RXAFS_Rename().
3713       If it is not provided, it will be looked up.
3714
3715   \param[in] normalizedOldNamep Normalized old name.  This is used for
3716   updating local directory caches.
3717
3718   \param[in] newDscp cm_scache_t for the directory containing the new
3719   name.
3720
3721   \param[in] newNamep New name. Normalized.
3722
3723   \param[in] userp cm_user_t for the request.
3724
3725   \param[in,out] reqp Request tracker.
3726
3727 */
3728 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3729                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3730                cm_req_t *reqp)
3731 {
3732     cm_conn_t *connp;
3733     long code = 0;
3734     AFSFid oldDirAFSFid;
3735     AFSFid newDirAFSFid;
3736     AFSFetchStatus updatedOldDirStatus;
3737     AFSFetchStatus updatedNewDirStatus;
3738     AFSVolSync volSync;
3739     int oneDir = 0;
3740     int bTargetExists = 0;
3741     struct rx_connection * rxconnp;
3742     cm_dirOp_t oldDirOp;
3743     cm_fid_t   fileFid;
3744     int        diropCode = -1;
3745     cm_dirOp_t newDirOp;
3746     fschar_t * newNamep = NULL;
3747     int free_oldNamep = FALSE;
3748     cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3749     int rpc_skipped = 0;
3750
3751     memset(&volSync, 0, sizeof(volSync));
3752
3753     if (cOldNamep == NULL || cNewNamep == NULL ||
3754         cm_ClientStrLen(cOldNamep) == 0 ||
3755         cm_ClientStrLen(cNewNamep) == 0)
3756         return CM_ERROR_INVAL;
3757
3758     /* check for identical names */
3759     if (oldDscp == newDscp &&
3760         cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3761         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3762                   oldDscp, newDscp);
3763         return CM_ERROR_RENAME_IDENTICAL;
3764     }
3765
3766     /* Check for RO volume */
3767     if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3768         return CM_ERROR_READONLY;
3769     }
3770
3771     if (oldNamep == NULL) {
3772         code = -1;
3773 #ifdef USE_BPLUS
3774         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3775                              CM_DIROP_FLAG_NONE, &oldDirOp);
3776         if (code == 0) {
3777             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3778             if (code == 0)
3779                 free_oldNamep = TRUE;
3780             cm_EndDirOp(&oldDirOp);
3781         }
3782 #endif
3783         if (code) {
3784             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3785                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3786             goto done;
3787         }
3788     }
3789
3790     /* before starting the RPC, mark that we're changing the directory data,
3791      * so that someone who does a chmod on the dir will wait until our call
3792      * completes.  We do this in vnode order so that we don't deadlock,
3793      * which makes the code a little verbose.
3794      */
3795     if (oldDscp == newDscp) {
3796         oneDir = 1;
3797         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3798                       CM_DIROP_FLAG_NONE, &oldDirOp);
3799         lock_ObtainWrite(&oldDscp->rw);
3800         cm_dnlcRemove(oldDscp, cOldNamep);
3801         cm_dnlcRemove(oldDscp, cNewNamep);
3802         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3803                           CM_SCACHESYNC_STOREDATA);
3804         lock_ReleaseWrite(&oldDscp->rw);
3805         if (code != 0) {
3806             cm_EndDirOp(&oldDirOp);
3807         }
3808     }
3809     else {
3810         /* two distinct dir vnodes */
3811         oneDir = 0;
3812         if (oldDscp->fid.cell != newDscp->fid.cell ||
3813              oldDscp->fid.volume != newDscp->fid.volume) {
3814             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3815                       oldDscp, newDscp);
3816             code = CM_ERROR_CROSSDEVLINK;
3817             goto done;
3818         }
3819
3820         /* shouldn't happen that we have distinct vnodes for two
3821          * different files, but could due to deliberate attack, or
3822          * stale info.  Avoid deadlocks and quit now.
3823          */
3824         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3825             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3826                       oldDscp, newDscp);
3827             code = CM_ERROR_CROSSDEVLINK;
3828             goto done;
3829         }
3830
3831         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3832             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3833                           CM_DIROP_FLAG_NONE, &oldDirOp);
3834             lock_ObtainWrite(&oldDscp->rw);
3835             cm_dnlcRemove(oldDscp, cOldNamep);
3836             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3837                              CM_SCACHESYNC_STOREDATA);
3838             lock_ReleaseWrite(&oldDscp->rw);
3839             if (code != 0)
3840                 cm_EndDirOp(&oldDirOp);
3841             if (code == 0) {
3842                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3843                               CM_DIROP_FLAG_NONE, &newDirOp);
3844                 lock_ObtainWrite(&newDscp->rw);
3845                 cm_dnlcRemove(newDscp, cNewNamep);
3846                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3847                                  CM_SCACHESYNC_STOREDATA);
3848                 lock_ReleaseWrite(&newDscp->rw);
3849                 if (code) {
3850                     cm_EndDirOp(&newDirOp);
3851
3852                     /* cleanup first one */
3853                     lock_ObtainWrite(&oldDscp->rw);
3854                     cm_SyncOpDone(oldDscp, NULL,
3855                                    CM_SCACHESYNC_STOREDATA);
3856                     lock_ReleaseWrite(&oldDscp->rw);
3857                     cm_EndDirOp(&oldDirOp);
3858                 }
3859             }
3860         }
3861         else {
3862             /* lock the new vnode entry first */
3863             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3864                           CM_DIROP_FLAG_NONE, &newDirOp);
3865             lock_ObtainWrite(&newDscp->rw);
3866             cm_dnlcRemove(newDscp, cNewNamep);
3867             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3868                               CM_SCACHESYNC_STOREDATA);
3869             lock_ReleaseWrite(&newDscp->rw);
3870             if (code != 0)
3871                 cm_EndDirOp(&newDirOp);
3872             if (code == 0) {
3873                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3874                               CM_DIROP_FLAG_NONE, &oldDirOp);
3875                 lock_ObtainWrite(&oldDscp->rw);
3876                 cm_dnlcRemove(oldDscp, cOldNamep);
3877                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3878                                   CM_SCACHESYNC_STOREDATA);
3879                 lock_ReleaseWrite(&oldDscp->rw);
3880                 if (code != 0)
3881                     cm_EndDirOp(&oldDirOp);
3882                 if (code) {
3883                     /* cleanup first one */
3884                     lock_ObtainWrite(&newDscp->rw);
3885                     cm_SyncOpDone(newDscp, NULL,
3886                                    CM_SCACHESYNC_STOREDATA);
3887                     lock_ReleaseWrite(&newDscp->rw);
3888                     cm_EndDirOp(&newDirOp);
3889                 }
3890             }
3891         }
3892     }   /* two distinct vnodes */
3893
3894     if (code)
3895         goto done;
3896
3897     /*
3898      * The source and destination directories are now locked and no other local
3899      * changes can occur.
3900      *
3901      * Before we permit the operation, make sure that we do not already have
3902      * an object in the destination directory that has a case-insensitive match
3903      * for this name UNLESS the matching object is the object we are renaming.
3904      */
3905     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3906     if (code) {
3907         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3908                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3909         rpc_skipped = 1;
3910         goto post_rpc;
3911     }
3912
3913     /* Case sensitive lookup.  If this succeeds we are done. */
3914     code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3915     if (code) {
3916         /*
3917          * Case insensitive lookup.  If this succeeds, it could have found the
3918          * same file with a name that differs only by case or it could be a
3919          * different file entirely.
3920          */
3921         code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3922         if (code == 0) {
3923             /* found a matching object with the new name */
3924             if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
3925                 /* and they don't match so return an error */
3926                 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3927                           newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3928                 code = CM_ERROR_EXISTS;
3929             }
3930             cm_ReleaseSCache(oldTargetScp);
3931             oldTargetScp = NULL;
3932         } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3933             code = CM_ERROR_EXISTS;
3934         } else {
3935             /* The target does not exist.  Clear the error and perform the rename. */
3936             code = 0;
3937         }
3938     } else {
3939         bTargetExists = 1;
3940     }
3941
3942     if (code) {
3943         rpc_skipped = 1;
3944         goto post_rpc;
3945     }
3946
3947     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3948
3949     /* try the RPC now */
3950     InterlockedIncrement(&oldDscp->activeRPCs);
3951     if (!oneDir)
3952         InterlockedIncrement(&newDscp->activeRPCs);
3953     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3954               oldDscp, newDscp);
3955     do {
3956         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3957         if (code)
3958             continue;
3959
3960         oldDirAFSFid.Volume = oldDscp->fid.volume;
3961         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3962         oldDirAFSFid.Unique = oldDscp->fid.unique;
3963         newDirAFSFid.Volume = newDscp->fid.volume;
3964         newDirAFSFid.Vnode = newDscp->fid.vnode;
3965         newDirAFSFid.Unique = newDscp->fid.unique;
3966
3967         rxconnp = cm_GetRxConn(connp);
3968         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3969                             &newDirAFSFid, newNamep,
3970                             &updatedOldDirStatus, &updatedNewDirStatus,
3971                             &volSync);
3972         rx_PutConnection(rxconnp);
3973
3974     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
3975                         &updatedOldDirStatus, &volSync, NULL, NULL, code));
3976     code = cm_MapRPCError(code, reqp);
3977
3978     if (code)
3979         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3980     else
3981         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3982
3983   post_rpc:
3984     /* update the individual stat cache entries for the directories */
3985     if (oldDirOp.scp) {
3986         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3987         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3988     }
3989
3990     lock_ObtainWrite(&oldDscp->rw);
3991     if (code == 0) {
3992         code = cm_MergeStatus( NULL, oldDscp, &updatedOldDirStatus, &volSync,
3993                                userp, reqp, CM_MERGEFLAG_DIROP);
3994         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3995             lock_ReleaseWrite(&oldDscp->rw);
3996             if (bTargetExists && oneDir) {
3997                 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
3998 #ifdef USE_BPLUS
3999                 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
4000 #endif
4001             }
4002
4003 #ifdef USE_BPLUS
4004             diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
4005             if (diropCode == CM_ERROR_INEXACT_MATCH)
4006                 diropCode = 0;
4007             else if (diropCode == EINVAL)
4008 #endif
4009                 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
4010
4011             if (diropCode == 0) {
4012                 if (oneDir) {
4013                     diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
4014 #ifdef USE_BPLUS
4015                     cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
4016 #endif
4017                 }
4018
4019                 if (diropCode == 0) {
4020                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
4021 #ifdef USE_BPLUS
4022                     cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
4023 #endif
4024                 }
4025             }
4026             lock_ObtainWrite(&oldDscp->rw);
4027         }
4028     } else {
4029         if (!rpc_skipped)
4030             InterlockedDecrement(&oldDscp->activeRPCs);
4031     }
4032     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4033     lock_ReleaseWrite(&oldDscp->rw);
4034
4035     cm_EndDirOp(&oldDirOp);
4036
4037     /* and update it for the new one, too, if necessary */