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