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