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