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