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