Windows: Do not release locks on deleted files
[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                 switch (code) {
163                 case CM_ERROR_ALLOFFLINE:
164                 case CM_ERROR_ALLDOWN:
165                 case CM_ERROR_ALLBUSY:
166                 case CM_ERROR_TIMEDOUT:
167                 case CM_ERROR_RETRY:
168                 case CM_ERROR_WOULDBLOCK:
169                     break;
170                 default:
171                     code = CM_ERROR_SHARING_VIOLATION;
172                 }
173             }
174         }
175
176     } else if (code != 0) {
177         goto _done;
178     }
179
180     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
181
182  _done:
183
184     lock_ReleaseWrite(&scp->rw);
185
186     return code;
187 }
188
189 /* return success if we can open this file in this mode */
190 long cm_CheckNTOpen(cm_scache_t *scp, unsigned int desiredAccess,
191                     unsigned int createDisp, cm_user_t *userp, cm_req_t *reqp,
192                     cm_lock_data_t **ldpp)
193 {
194     long rights;
195     long code = 0;
196
197     osi_assertx(ldpp != NULL, "null cm_lock_data_t");
198     *ldpp = NULL;
199
200     /* Always allow delete; the RPC will tell us if it's OK */
201     rights = 0;
202
203     if (desiredAccess == DELETE)
204         goto done_2;
205
206     if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
207         rights |= (scp->fileType == CM_SCACHETYPE_DIRECTORY ? PRSFS_LOOKUP : PRSFS_READ);
208
209     /* We used to require PRSFS_WRITE if createDisp was 4
210        (OPEN_ALWAYS) even if AFS_ACCESS_WRITE was not requested.
211        However, we don't need to do that since the existence of the
212        scp implies that we don't need to create it. */
213     if (desiredAccess & AFS_ACCESS_WRITE)
214         rights |= PRSFS_WRITE;
215
216     if (desiredAccess & DELETE)
217         rights |= PRSFS_DELETE;
218
219     lock_ObtainWrite(&scp->rw);
220
221     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
222                       CM_SCACHESYNC_GETSTATUS
223                      | CM_SCACHESYNC_NEEDCALLBACK
224                      | CM_SCACHESYNC_LOCK);
225
226     /*
227      * If the open will fail because the volume is readonly, then we will
228      * return an access denied error instead.  This is to help brain-dead
229      * apps run correctly on replicated volumes.
230      * See defect 10007 for more information.
231      */
232     if (code == CM_ERROR_READONLY)
233         code = CM_ERROR_NOACCESS;
234
235     if (code == 0 &&
236              ((rights & PRSFS_WRITE) || (rights & PRSFS_READ)) &&
237              scp->fileType == CM_SCACHETYPE_FILE) {
238         cm_key_t key;
239         unsigned int sLockType;
240         LARGE_INTEGER LOffset, LLength;
241
242         /* Check if there's some sort of lock on the file at the
243            moment. */
244
245         key = cm_GenerateKey(CM_SESSION_CMINT,0,0);
246         if (rights & PRSFS_WRITE)
247             sLockType = 0;
248         else
249             sLockType = LOCKING_ANDX_SHARED_LOCK;
250
251         /* single byte lock at offset 0x0100 0000 0000 0000 */
252         LOffset.HighPart = CM_FLSHARE_OFFSET_HIGH;
253         LOffset.LowPart  = CM_FLSHARE_OFFSET_LOW;
254         LLength.HighPart = CM_FLSHARE_LENGTH_HIGH;
255         LLength.LowPart  = CM_FLSHARE_LENGTH_LOW;
256
257         code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, reqp, NULL);
258
259         if (code == 0) {
260             (*ldpp) = (cm_lock_data_t *)malloc(sizeof(cm_lock_data_t));
261             if (!*ldpp) {
262                 code = ENOMEM;
263                 goto _done;
264             }
265
266             (*ldpp)->key = key;
267             (*ldpp)->sLockType = sLockType;
268             (*ldpp)->LOffset.HighPart = LOffset.HighPart;
269             (*ldpp)->LOffset.LowPart = LOffset.LowPart;
270             (*ldpp)->LLength.HighPart = LLength.HighPart;
271             (*ldpp)->LLength.LowPart = LLength.LowPart;
272         } else {
273             /* In this case, we allow the file open to go through even
274                though we can't enforce mandatory locking on the
275                file. */
276             if (code == CM_ERROR_NOACCESS &&
277                 !(rights & PRSFS_WRITE))
278                 code = 0;
279             else {
280                 switch (code) {
281                 case CM_ERROR_ALLOFFLINE:
282                 case CM_ERROR_ALLDOWN:
283                 case CM_ERROR_ALLBUSY:
284                 case CM_ERROR_TIMEDOUT:
285                 case CM_ERROR_RETRY:
286                 case CM_ERROR_WOULDBLOCK:
287                     break;
288                 default:
289                     code = CM_ERROR_SHARING_VIOLATION;
290                 }
291             }
292         }
293     } else if (code != 0) {
294         goto _done;
295     }
296
297  _done:
298     lock_ReleaseWrite(&scp->rw);
299
300  done_2:
301     osi_Log3(afsd_logp,"cm_CheckNTOpen scp 0x%p ldp 0x%p code 0x%x", scp, *ldpp, code);
302     return code;
303 }
304
305 extern long cm_CheckNTOpenDone(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp,
306                                cm_lock_data_t ** ldpp)
307 {
308     osi_Log2(afsd_logp,"cm_CheckNTOpenDone scp 0x%p ldp 0x%p", scp, *ldpp);
309     lock_ObtainWrite(&scp->rw);
310     if (*ldpp) {
311         cm_Unlock(scp, (*ldpp)->sLockType, (*ldpp)->LOffset, (*ldpp)->LLength,
312                   (*ldpp)->key, 0, userp, reqp);
313         free(*ldpp);
314         *ldpp = NULL;
315     }
316     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
317     lock_ReleaseWrite(&scp->rw);
318     return 0;
319 }
320 /*
321  * When CAP_NT_SMBS has been negotiated, deletion (of files or directories) is
322  * done in three steps:
323  * (1) open for deletion (NT_CREATE_AND_X)
324  * (2) set for deletion on close (NT_TRANSACTION2, SET_FILE_INFO)
325  * (3) close (CLOSE)
326  * We must not do the RPC until step 3.  But if we are going to return an error
327  * code (e.g. directory not empty), we must return it by step 2, otherwise most
328  * clients will not notice it.  So we do a preliminary check.  For deleting
329  * files, this is almost free, since we have already done the RPC to get the
330  * parent directory's status bits.  But for deleting directories, we must do an
331  * additional RPC to get the directory's data to check if it is empty.  Sigh.
332  */
333 long cm_CheckNTDelete(cm_scache_t *dscp, cm_scache_t *scp, cm_user_t *userp,
334         cm_req_t *reqp)
335 {
336     long code;
337     osi_hyper_t thyper;
338     cm_buf_t *bufferp;
339     cm_dirEntry_t *dep = 0;
340     unsigned short *hashTable;
341     unsigned int i, idx;
342     int BeyondPage = 0, HaveDot = 0, HaveDotDot = 0;
343     int releaseLock = 0;
344
345     /* First check permissions */
346     lock_ObtainWrite(&scp->rw);
347     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_DELETE,
348                       CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
349     if (!code)
350         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
351     lock_ReleaseWrite(&scp->rw);
352     if (code)
353         return code;
354
355     /* If deleting directory, must be empty */
356
357     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
358         return code;
359
360     thyper.HighPart = 0; thyper.LowPart = 0;
361     code = buf_Get(scp, &thyper, reqp, &bufferp);
362     if (code)
363         return code;
364
365     lock_ObtainMutex(&bufferp->mx);
366     lock_ObtainWrite(&scp->rw);
367     releaseLock = 1;
368     while (1) {
369         code = cm_SyncOp(scp, bufferp, userp, reqp, 0,
370                           CM_SCACHESYNC_NEEDCALLBACK
371                           | CM_SCACHESYNC_READ
372                           | CM_SCACHESYNC_BUFLOCKED);
373         if (code)
374             goto done;
375
376         if (cm_HaveBuffer(scp, bufferp, 1))
377             break;
378
379         /* otherwise, load the buffer and try again */
380         lock_ReleaseMutex(&bufferp->mx);
381         code = cm_GetBuffer(scp, bufferp, NULL, userp, reqp);
382         lock_ReleaseWrite(&scp->rw);
383         lock_ObtainMutex(&bufferp->mx);
384         lock_ObtainWrite(&scp->rw);
385         cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
386         if (code)
387             goto done;
388     }
389
390     lock_ReleaseWrite(&scp->rw);
391     releaseLock = 0;
392
393     /* We try to determine emptiness without looking beyond the first page,
394      * and without assuming "." and ".." are present and are on the first
395      * page (though these assumptions might, after all, be reasonable).
396      */
397     hashTable = (unsigned short *)(bufferp->datap + (32 * 5));
398     for (i=0; i<128; i++) {
399         idx = ntohs(hashTable[i]);
400         while (idx) {
401             if (idx >= 64) {
402                 BeyondPage = 1;
403                 break;
404             }
405             dep = (cm_dirEntry_t *)(bufferp->datap + (32 * idx));
406             if (strcmp(dep->name, ".") == 0)
407                 HaveDot = 1;
408             else if (strcmp(dep->name, "..") == 0)
409                 HaveDotDot = 1;
410             else {
411                 code = CM_ERROR_NOTEMPTY;
412                 goto done;
413             }
414             idx = ntohs(dep->next);
415         }
416     }
417     if (BeyondPage && HaveDot && HaveDotDot)
418         code = CM_ERROR_NOTEMPTY;
419     else
420         code = 0;
421   done:
422     lock_ReleaseMutex(&bufferp->mx);
423     buf_Release(bufferp);
424     if (releaseLock)
425         lock_ReleaseWrite(&scp->rw);
426     return code;
427 }
428
429 /*
430  * Iterate through all entries in a directory.
431  * When the function funcp is called, the buffer is locked but the
432  * directory vnode is not.
433  *
434  * If the retscp parameter is not NULL, the parmp must be a
435  * cm_lookupSearch_t object.
436  */
437 long cm_ApplyDir(cm_scache_t *scp, cm_DirFuncp_t funcp, void *parmp,
438                  osi_hyper_t *startOffsetp, cm_user_t *userp, cm_req_t *reqp,
439                  cm_scache_t **retscp)
440 {
441     char *tp;
442     long code;
443     cm_dirEntry_t *dep = 0;
444     cm_buf_t *bufferp;
445     long temp;
446     osi_hyper_t dirLength;
447     osi_hyper_t bufferOffset;
448     osi_hyper_t curOffset;
449     osi_hyper_t thyper;
450     long entryInDir;
451     long entryInBuffer;
452     cm_pageHeader_t *pageHeaderp;
453     int slotInPage;
454     long nextEntryCookie;
455     int numDirChunks;   /* # of 32 byte dir chunks in this entry */
456
457     /* get the directory size */
458     lock_ObtainWrite(&scp->rw);
459     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_LOOKUP,
460                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
461     lock_ReleaseWrite(&scp->rw);
462     if (code)
463         return code;
464
465     if (scp->fileType != CM_SCACHETYPE_DIRECTORY)
466         return CM_ERROR_NOTDIR;
467
468     if (retscp)                         /* if this is a lookup call */
469     {
470         cm_lookupSearch_t*      sp = parmp;
471
472         if (
473 #ifdef AFS_FREELANCE_CLIENT
474         /* Freelance entries never end up in the DNLC because they
475          * do not have an associated cm_server_t
476          */
477             !(cm_freelanceEnabled &&
478             sp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
479               sp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
480 #else /* !AFS_FREELANCE_CLIENT */
481             TRUE
482 #endif
483             )
484         {
485             int casefold = sp->caseFold;
486             sp->caseFold = 0; /* we have a strong preference for exact matches */
487             if ( *retscp = cm_dnlcLookup(scp, sp))      /* dnlc hit */
488             {
489                 sp->caseFold = casefold;
490                 return 0;
491             }
492             sp->caseFold = casefold;
493         }
494
495         /*
496          * see if we can find it using the directory hash tables.
497          * we can only do exact matches, since the hash is case
498          * sensitive.
499          */
500         if (funcp != (cm_DirFuncp_t)cm_BPlusDirFoo)
501         {
502             cm_dirOp_t dirop;
503 #ifdef USE_BPLUS
504             int usedBplus = 0;
505 #endif
506
507             code = ENOENT;
508
509             code = cm_BeginDirOp(scp, userp, reqp, CM_DIRLOCK_READ,
510                                  CM_DIROP_FLAG_NONE, &dirop);
511             if (code == 0) {
512
513 #ifdef USE_BPLUS
514                 code = cm_BPlusDirLookup(&dirop, sp->nsearchNamep, &sp->fid);
515                 if (code != EINVAL)
516                     usedBplus = 1;
517                 else
518 #endif
519                     code = cm_DirLookup(&dirop, sp->searchNamep, &sp->fid);
520
521                 cm_EndDirOp(&dirop);
522             }
523
524             if (code == 0) {
525                 /* found it */
526                 sp->found = TRUE;
527                 sp->ExactFound = TRUE;
528                 *retscp = NULL; /* force caller to call cm_GetSCache() */
529                 return 0;
530             }
531 #ifdef USE_BPLUS
532             if (usedBplus) {
533                 if (sp->caseFold && code == CM_ERROR_INEXACT_MATCH) {
534                     /* found it */
535                     sp->found = TRUE;
536                     sp->ExactFound = FALSE;
537                     *retscp = NULL; /* force caller to call cm_GetSCache() */
538                     return 0;
539                 }
540
541                 return CM_ERROR_BPLUS_NOMATCH;
542             }
543 #endif
544         }
545     }
546
547     /*
548      * XXX We only get the length once.  It might change when we drop the
549      * lock.
550      */
551     dirLength = scp->length;
552
553     bufferp = NULL;
554     bufferOffset.LowPart = bufferOffset.HighPart = 0;
555     if (startOffsetp)
556         curOffset = *startOffsetp;
557     else {
558         curOffset.HighPart = 0;
559         curOffset.LowPart = 0;
560     }
561
562     while (1) {
563         /* make sure that curOffset.LowPart doesn't point to the first
564          * 32 bytes in the 2nd through last dir page, and that it
565          * doesn't point at the first 13 32-byte chunks in the first
566          * dir page, since those are dir and page headers, and don't
567          * contain useful information.
568          */
569         temp = curOffset.LowPart & (2048-1);
570         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
571             /* we're in the first page */
572             if (temp < 13*32) temp = 13*32;
573         }
574         else {
575             /* we're in a later dir page */
576             if (temp < 32) temp = 32;
577         }
578
579         /* make sure the low order 5 bits are zero */
580         temp &= ~(32-1);
581
582         /* now put temp bits back ito curOffset.LowPart */
583         curOffset.LowPart &= ~(2048-1);
584         curOffset.LowPart |= temp;
585
586         /* check if we've passed the dir's EOF */
587         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength))
588             break;
589
590         /* see if we can use the bufferp we have now; compute in which
591          * page the current offset would be, and check whether that's
592          * the offset of the buffer we have.  If not, get the buffer.
593          */
594         thyper.HighPart = curOffset.HighPart;
595         thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
596         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
597             /* wrong buffer */
598             if (bufferp) {
599                 lock_ReleaseMutex(&bufferp->mx);
600                 buf_Release(bufferp);
601                 bufferp = NULL;
602             }
603
604             code = buf_Get(scp, &thyper, reqp, &bufferp);
605             if (code) {
606                 /* if buf_Get() fails we do not have a buffer object to lock */
607                 bufferp = NULL;
608                 break;
609             }
610
611             lock_ObtainMutex(&bufferp->mx);
612             bufferOffset = thyper;
613
614             /* now get the data in the cache */
615             while (1) {
616                 lock_ObtainWrite(&scp->rw);
617                 code = cm_SyncOp(scp, bufferp, userp, reqp,
618                                   PRSFS_LOOKUP,
619                                   CM_SCACHESYNC_NEEDCALLBACK
620                                   | CM_SCACHESYNC_READ
621                                   | CM_SCACHESYNC_BUFLOCKED);
622                 if (code) {
623                     lock_ReleaseWrite(&scp->rw);
624                     break;
625                 }
626                 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ | CM_SCACHESYNC_BUFLOCKED);
627
628                 if (cm_HaveBuffer(scp, bufferp, 1)) {
629                     lock_ReleaseWrite(&scp->rw);
630                     break;
631                 }
632
633                 /* otherwise, load the buffer and try again */
634                 lock_ReleaseMutex(&bufferp->mx);
635                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
636                                     reqp);
637                 lock_ReleaseWrite(&scp->rw);
638                 lock_ObtainMutex(&bufferp->mx);
639                 if (code)
640                     break;
641             }
642             if (code) {
643                 lock_ReleaseMutex(&bufferp->mx);
644                 buf_Release(bufferp);
645                 bufferp = NULL;
646                 break;
647             }
648         }       /* if (wrong buffer) ... */
649
650         /* now we have the buffer containing the entry we're interested
651          * in; copy it out if it represents a non-deleted entry.
652          */
653         entryInDir = curOffset.LowPart & (2048-1);
654         entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
655
656         /* page header will help tell us which entries are free.  Page
657          * header can change more often than once per buffer, since
658          * AFS 3 dir page size may be less than (but not more than) a
659          * buffer package buffer.
660          */
661         /* only look intra-buffer */
662         temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
663         temp &= ~(2048 - 1);    /* turn off intra-page bits */
664         pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
665
666         /* now determine which entry we're looking at in the page.  If
667          * it is free (there's a free bitmap at the start of the dir),
668          * we should skip these 32 bytes.
669          */
670         slotInPage = (entryInDir & 0x7e0) >> 5;
671         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
672                & (1 << (slotInPage & 0x7)))) {
673             /* this entry is free */
674             numDirChunks = 1;   /* only skip this guy */
675             goto nextEntry;
676         }
677
678         tp = bufferp->datap + entryInBuffer;
679         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
680
681         /*
682          * here are some consistency checks
683          */
684         if (dep->flag != CM_DIR_FFIRST ||
685             strlen(dep->name) > 256) {
686             code = CM_ERROR_INVAL;
687             osi_Log2(afsd_logp,
688                      "cm_ApplyDir invalid directory entry for scp %p bufp %p",
689                      scp, bufferp);
690             osi_Log4(afsd_logp,"... cell %u vol %u vnode %u uniq %u",
691                      scp->fid.cell, scp->fid.volume, scp->fid.vnode, scp->fid.unique);
692             bufferp->dataVersion = CM_BUF_VERSION_BAD;
693             break;
694         }
695
696         /* while we're here, compute the next entry's location, too,
697          * since we'll need it when writing out the cookie into the
698          * dir listing stream.
699          */
700         numDirChunks = cm_NameEntries(dep->name, NULL);
701
702         /* compute the offset of the cookie representing the next entry */
703         nextEntryCookie = curOffset.LowPart
704             + (CM_DIR_CHUNKSIZE * numDirChunks);
705
706         if (dep->fid.vnode != 0) {
707             /* this is one of the entries to use: it is not deleted */
708             code = (*funcp)(scp, dep, parmp, &curOffset);
709             if (code)
710                 break;
711         }       /* if we're including this name */
712
713       nextEntry:
714         /* and adjust curOffset to be where the new cookie is */
715         thyper.HighPart = 0;
716         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
717         curOffset = LargeIntegerAdd(thyper, curOffset);
718     }           /* while copying data for dir listing */
719
720     /* release the mutex */
721     if (bufferp) {
722         lock_ReleaseMutex(&bufferp->mx);
723         buf_Release(bufferp);
724     }
725     return code;
726 }
727
728 int cm_NoneUpper(normchar_t *s)
729 {
730     normchar_t c;
731     while (c = *s++)
732         if (c >= 'A' && c <= 'Z')
733             return 0;
734     return 1;
735 }
736
737 int cm_NoneLower(normchar_t *s)
738 {
739     normchar_t c;
740     while (c = *s++)
741         if (c >= 'a' && c <= 'z')
742             return 0;
743     return 1;
744 }
745
746 long cm_LookupSearchProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
747                          osi_hyper_t *offp)
748 {
749     cm_lookupSearch_t *sp;
750     int match;
751     normchar_t matchName[MAX_PATH];
752     int looking_for_short_name = FALSE;
753
754     sp = (cm_lookupSearch_t *) rockp;
755
756     if (cm_FsStringToNormString(dep->name, -1, matchName, lengthof(matchName)) == 0) {
757         /* Can't normalize FS string. */
758         return 0;
759     }
760
761     if (sp->caseFold)
762         match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
763     else
764         match = cm_NormStrCmp(matchName, sp->nsearchNamep);
765
766     if (match != 0
767         && sp->hasTilde
768         && !cm_Is8Dot3(matchName)) {
769
770         cm_Gen8Dot3NameInt(dep->name, &dep->fid, matchName, NULL);
771         if (sp->caseFold)
772             match = cm_NormStrCmpI(matchName, sp->nsearchNamep);
773         else
774             match = cm_NormStrCmp(matchName, sp->nsearchNamep);
775         looking_for_short_name = TRUE;
776     }
777
778     if (match != 0)
779         return 0;
780
781     sp->found = 1;
782     if (!sp->caseFold)
783         sp->ExactFound = 1;
784
785     if (!sp->caseFold || looking_for_short_name) {
786         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
787         return CM_ERROR_STOPNOW;
788     }
789
790     /*
791      * If we get here, we are doing a case-insensitive search, and we
792      * have found a match.  Now we determine what kind of match it is:
793      * exact, lower-case, upper-case, or none of the above.  This is done
794      * in order to choose among matches, if there are more than one.
795      */
796
797     /* Exact matches are the best. */
798     match = cm_NormStrCmp(matchName, sp->nsearchNamep);
799     if (match == 0) {
800         sp->ExactFound = 1;
801         cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
802         return CM_ERROR_STOPNOW;
803     }
804
805     /* Lower-case matches are next. */
806     if (sp->LCfound)
807         return 0;
808     if (cm_NoneUpper(matchName)) {
809         sp->LCfound = 1;
810         goto inexact;
811     }
812
813     /* Upper-case matches are next. */
814     if (sp->UCfound)
815         return 0;
816     if (cm_NoneLower(matchName)) {
817         sp->UCfound = 1;
818         goto inexact;
819     }
820
821     /* General matches are last. */
822     if (sp->NCfound)
823         return 0;
824     sp->NCfound = 1;
825
826   inexact:
827     cm_SetFid(&sp->fid, sp->fid.cell, sp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
828     return 0;
829 }
830
831 /* read the contents of a mount point into the appropriate string.
832  * called with write locked scp, and returns with locked scp.
833  */
834 long cm_ReadMountPoint(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp)
835 {
836     long code;
837
838     if (scp->mountPointStringp[0])
839         return 0;
840
841 #ifdef AFS_FREELANCE_CLIENT
842     /* File servers do not have data for freelance entries */
843     if (cm_freelanceEnabled &&
844         scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
845         scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
846     {
847         code = cm_FreelanceFetchMountPointString(scp);
848     } else
849 #endif /* AFS_FREELANCE_CLIENT */
850     {
851         char temp[MOUNTPOINTLEN];
852         osi_hyper_t offset;
853
854         /* otherwise, we have to read it in */
855         offset.LowPart = offset.HighPart = 0;
856         code = cm_GetData(scp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
857         if (code)
858             return code;
859
860         /*
861          * scp->length is the actual length of the mount point string.
862          * It is current because cm_GetData merged the most up to date
863          * status info into scp and has not dropped the rwlock since.
864          */
865         if (scp->length.LowPart > MOUNTPOINTLEN - 1)
866             return CM_ERROR_TOOBIG;
867         if (scp->length.LowPart == 0)
868             return CM_ERROR_INVAL;
869
870         /* convert the terminating dot to a NUL */
871         temp[scp->length.LowPart - 1] = 0;
872         memcpy(scp->mountPointStringp, temp, scp->length.LowPart);
873     }
874
875     return code;
876 }
877
878
879 /* called with a locked scp and chases the mount point, yielding outScpp.
880  * scp remains write locked, just for simplicity of describing the interface.
881  */
882 long cm_FollowMountPoint(cm_scache_t *scp, cm_scache_t *dscp, cm_user_t *userp,
883                          cm_req_t *reqp, cm_scache_t **outScpp)
884 {
885     fschar_t *cellNamep = NULL;
886     fschar_t *volNamep = NULL;
887     afs_uint32 code;
888     fschar_t *cp;
889     fschar_t *mpNamep;
890     cm_volume_t *volp = NULL;
891     cm_cell_t *cellp;
892     fschar_t mtType;
893     cm_fid_t tfid;
894     size_t vnLength;
895     int targetType;
896
897     *outScpp = NULL;
898
899     if (scp->mountRootFid.cell != 0 && scp->mountRootGen >= cm_data.mountRootGen) {
900         tfid = scp->mountRootFid;
901         lock_ReleaseWrite(&scp->rw);
902         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
903         lock_ObtainWrite(&scp->rw);
904         return code;
905     }
906
907     /* parse the volume name */
908     mpNamep = scp->mountPointStringp;
909     if (!mpNamep[0])
910         return CM_ERROR_NOSUCHPATH;
911     mtType = *scp->mountPointStringp;
912
913     cp = cm_FsStrChr(mpNamep, _FS(':'));
914     if (cp) {
915         /* cellular mount point */
916         cellNamep = (fschar_t *)malloc((cp - mpNamep) * sizeof(fschar_t));
917         cm_FsStrCpyN(cellNamep, cp - mpNamep, mpNamep + 1, cp - mpNamep - 1);
918         volNamep = cm_FsStrDup(cp+1);
919
920         /* now look up the cell */
921         lock_ReleaseWrite(&scp->rw);
922         cellp = cm_GetCell(cellNamep, CM_FLAG_CREATE);
923         lock_ObtainWrite(&scp->rw);
924     } else {
925         /* normal mt pt */
926         volNamep = cm_FsStrDup(mpNamep + 1);
927
928 #ifdef AFS_FREELANCE_CLIENT
929         /*
930          * Mount points in the Freelance cell should default
931          * to the workstation cell.
932          */
933         if (cm_freelanceEnabled &&
934              scp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
935              scp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
936         {
937             fschar_t rootCellName[256]="";
938             cm_GetRootCellName(rootCellName);
939             cellp = cm_GetCell(rootCellName, 0);
940         } else
941 #endif /* AFS_FREELANCE_CLIENT */
942             cellp = cm_FindCellByID(scp->fid.cell, 0);
943     }
944
945     if (!cellp) {
946         code = CM_ERROR_NOSUCHCELL;
947         goto done;
948     }
949
950     vnLength = cm_FsStrLen(volNamep);
951     if (vnLength >= 8 && cm_FsStrCmp(volNamep + vnLength - 7, ".backup") == 0)
952         targetType = BACKVOL;
953     else if (vnLength >= 10
954              && cm_FsStrCmp(volNamep + vnLength - 9, ".readonly") == 0)
955         targetType = ROVOL;
956     else
957         targetType = RWVOL;
958
959     /* check for backups within backups */
960     if (targetType == BACKVOL
961          && (scp->flags & (CM_SCACHEFLAG_RO | CM_SCACHEFLAG_PURERO))
962          == CM_SCACHEFLAG_RO) {
963         code = CM_ERROR_NOSUCHVOLUME;
964         goto done;
965     }
966
967     /* now we need to get the volume */
968     lock_ReleaseWrite(&scp->rw);
969     if (cm_VolNameIsID(volNamep)) {
970         code = cm_FindVolumeByID(cellp, atoi(volNamep), userp, reqp,
971                                 CM_GETVOL_FLAG_CREATE, &volp);
972     } else {
973         code = cm_FindVolumeByName(cellp, volNamep, userp, reqp,
974                                   CM_GETVOL_FLAG_CREATE, &volp);
975     }
976     lock_ObtainWrite(&scp->rw);
977
978     if (code == 0) {
979         afs_uint32 cell, volume;
980         cm_vol_state_t *statep;
981
982         cell = cellp->cellID;
983
984         /* if the mt pt originates in a .backup volume (not a .readonly)
985          * and FollowBackupPath is active, and if there is a .backup
986          * volume for the target, then use the .backup of the target
987          * instead of the read-write.
988          */
989         if (cm_followBackupPath &&
990             volp->vol[BACKVOL].ID != 0 &&
991             (dscp->flags & (CM_SCACHEFLAG_RO|CM_SCACHEFLAG_PURERO)) == CM_SCACHEFLAG_RO &&
992             (targetType == RWVOL || targetType == ROVOL && volp->vol[ROVOL].ID == 0)
993             ) {
994             targetType = BACKVOL;
995         }
996         /* if the mt pt is in a read-only volume (not just a
997          * backup), and if there is a read-only volume for the
998          * target, and if this is a targetType '#' mount point, use
999          * the read-only, otherwise use the one specified.
1000          */
1001         else if (mtType == '#' && targetType == RWVOL &&
1002                  (scp->flags & CM_SCACHEFLAG_PURERO) &&
1003                  volp->vol[ROVOL].ID != 0) {
1004             targetType = ROVOL;
1005         }
1006
1007         lock_ObtainWrite(&volp->rw);
1008         statep = cm_VolumeStateByType(volp, targetType);
1009         volume = statep->ID;
1010         statep->dotdotFid = dscp->fid;
1011         lock_ReleaseWrite(&volp->rw);
1012
1013         /* the rest of the fid is a magic number */
1014         cm_SetFid(&scp->mountRootFid, cell, volume, 1, 1);
1015         scp->mountRootGen = cm_data.mountRootGen;
1016
1017         tfid = scp->mountRootFid;
1018         lock_ReleaseWrite(&scp->rw);
1019         code = cm_GetSCache(&tfid, outScpp, userp, reqp);
1020         lock_ObtainWrite(&scp->rw);
1021     }
1022
1023   done:
1024     if (volp)
1025         cm_PutVolume(volp);
1026     if (cellNamep)
1027         free(cellNamep);
1028     if (volNamep)
1029         free(volNamep);
1030     return code;
1031 }
1032
1033 long cm_LookupInternal(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_user_t *userp,
1034                        cm_req_t *reqp, cm_scache_t **outScpp)
1035 {
1036     long code;
1037     int dnlcHit = 1;    /* did we hit in the dnlc? yes, we did */
1038     cm_scache_t *tscp = NULL;
1039     cm_scache_t *mountedScp;
1040     cm_lookupSearch_t rock;
1041     int getroot;
1042     normchar_t *nnamep = NULL;
1043     fschar_t *fnamep = NULL;
1044     size_t fnlen;
1045
1046     *outScpp = NULL;
1047
1048     memset(&rock, 0, sizeof(rock));
1049
1050     if (dscp->fid.vnode == 1 && dscp->fid.unique == 1
1051         && cm_ClientStrCmp(cnamep, _C("..")) == 0) {
1052         if (dscp->dotdotFid.volume == 0)
1053             return CM_ERROR_NOSUCHVOLUME;
1054         rock.fid = dscp->dotdotFid;
1055         goto haveFid;
1056     } else if (cm_ClientStrCmp(cnamep, _C(".")) == 0) {
1057         rock.fid = dscp->fid;
1058         goto haveFid;
1059     }
1060
1061     nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1062     if (!nnamep) {
1063         code = CM_ERROR_NOSUCHFILE;
1064         goto done;
1065     }
1066     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
1067     if (!fnamep) {
1068         code = CM_ERROR_NOSUCHFILE;
1069         goto done;
1070     }
1071
1072 retry_lookup:
1073     if (flags & CM_FLAG_NOMOUNTCHASE) {
1074         /* In this case, we should go and call cm_Dir* functions
1075            directly since the following cm_ApplyDir() function will
1076            not. */
1077
1078         cm_dirOp_t dirop;
1079 #ifdef USE_BPLUS
1080         int usedBplus = 0;
1081 #endif
1082
1083         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1084                              CM_DIROP_FLAG_NONE, &dirop);
1085         if (code == 0) {
1086 #ifdef USE_BPLUS
1087             code = cm_BPlusDirLookup(&dirop, nnamep, &rock.fid);
1088             if (code != EINVAL)
1089                 usedBplus = 1;
1090             else
1091 #endif
1092                 code = cm_DirLookup(&dirop, fnamep, &rock.fid);
1093
1094             cm_EndDirOp(&dirop);
1095         }
1096
1097         if (code == 0) {
1098             /* found it */
1099             rock.found = TRUE;
1100             goto haveFid;
1101         }
1102 #ifdef USE_BPLUS
1103         if (usedBplus) {
1104             if (code == CM_ERROR_INEXACT_MATCH && (flags & CM_FLAG_CASEFOLD)) {
1105                 /* found it */
1106                 code = 0;
1107                 rock.found = TRUE;
1108                 goto haveFid;
1109             }
1110
1111             code = CM_ERROR_BPLUS_NOMATCH;
1112             goto notfound;
1113         }
1114 #endif
1115     }
1116
1117     rock.fid.cell = dscp->fid.cell;
1118     rock.fid.volume = dscp->fid.volume;
1119     rock.searchNamep = fnamep;
1120     rock.nsearchNamep = nnamep;
1121     rock.caseFold = (flags & CM_FLAG_CASEFOLD);
1122     rock.hasTilde = ((cm_ClientStrChr(cnamep, '~') != NULL) ? 1 : 0);
1123
1124     /* If NOMOUNTCHASE, bypass DNLC by passing NULL scp pointer */
1125     code = cm_ApplyDir(dscp, cm_LookupSearchProc, &rock, NULL, userp, reqp,
1126                        (flags & CM_FLAG_NOMOUNTCHASE) ? NULL : &tscp);
1127
1128     /* code == 0 means we fell off the end of the dir, while stopnow means
1129      * that we stopped early, probably because we found the entry we're
1130      * looking for.  Any other non-zero code is an error.
1131      */
1132     if (code && code != CM_ERROR_STOPNOW && code != CM_ERROR_BPLUS_NOMATCH) {
1133         /* if the cm_scache_t we are searching in is not a directory
1134          * we must return path not found because the error
1135          * is to describe the final component not an intermediary
1136          */
1137         if (code == CM_ERROR_NOTDIR) {
1138             if (flags & CM_FLAG_CHECKPATH)
1139                 code = CM_ERROR_NOSUCHPATH;
1140             else
1141                 code = CM_ERROR_NOSUCHFILE;
1142         }
1143         goto done;
1144     }
1145
1146 notfound:
1147     getroot = (dscp==cm_data.rootSCachep) ;
1148     if (!rock.found) {
1149         if (!cm_freelanceEnabled || !getroot) {
1150             if (flags & CM_FLAG_CHECKPATH)
1151                 code = CM_ERROR_NOSUCHPATH;
1152             else
1153                 code = CM_ERROR_NOSUCHFILE;
1154             goto done;
1155         }
1156         else if (!cm_ClientStrChr(cnamep, '#') &&
1157                  !cm_ClientStrChr(cnamep, '%') &&
1158                  cm_ClientStrCmpI(cnamep, _C("srvsvc")) &&
1159                  cm_ClientStrCmpI(cnamep, _C("wkssvc")) &&
1160                  cm_ClientStrCmpI(cnamep, _C("ipc$")))
1161         {
1162             /* nonexistent dir on freelance root, so add it */
1163             fschar_t fullname[CELL_MAXNAMELEN + 1] = ".";  /* +1 so that when we skip the . the size is still CELL_MAXNAMELEN */
1164             int  found = 0;
1165             int  retry = 0;
1166
1167             osi_Log1(afsd_logp,"cm_Lookup adding mount for non-existent directory: %S",
1168                      osi_LogSaveClientString(afsd_logp,cnamep));
1169
1170             /*
1171              * There is an ugly behavior where a share name "foo" will be searched
1172              * for as "fo".  If the searched for name differs by an already existing
1173              * symlink or mount point in the Freelance directory, do not add the
1174              * new value automatically.
1175              */
1176
1177             code = -1;
1178             fnlen = strlen(fnamep);
1179             if ( fnamep[fnlen-1] == '.') {
1180                 fnamep[fnlen-1] = '\0';
1181                 fnlen--;
1182                 retry = 1;
1183             }
1184
1185             if (cnamep[0] == '.') {
1186                 if (cm_GetCell_Gen(&fnamep[1], &fullname[1], CM_FLAG_CREATE)) {
1187                     found = 1;
1188                     code = cm_FreelanceAddMount(fullname, &fullname[1], "root.cell", 1, &rock.fid);
1189                     if ( cm_FsStrCmpI(&fnamep[1], &fullname[1])) {
1190                         /*
1191                          * Do not permit symlinks that are one of:
1192                          *  . the cellname followed by a dot
1193                          *  . the cellname minus a single character
1194                          *  . a substring of the cellname that does not consist of full components
1195                          */
1196                         if ( cm_strnicmp_utf8(&fnamep[1], fullname, (int)fnlen-1) == 0 &&
1197                              (fnlen-1 == strlen(fullname)-1 || fullname[fnlen-1] != '.'))
1198                         {
1199                             /* do not add; substitute fullname for the search */
1200                             free(fnamep);
1201                             fnamep = malloc(strlen(fullname)+2);
1202                             fnamep[0] = '.';
1203                             strncpy(&fnamep[1], fullname, strlen(fullname)+1);
1204                             retry = 1;
1205                         } else {
1206                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1207                         }
1208                     }
1209                 }
1210             } else {
1211                 if (cm_GetCell_Gen(fnamep, fullname, CM_FLAG_CREATE)) {
1212                     found = 1;
1213                     code = cm_FreelanceAddMount(fullname, fullname, "root.cell", 0, &rock.fid);
1214                     if ( cm_FsStrCmpI(fnamep, fullname)) {
1215                         /*
1216                          * Do not permit symlinks that are one of:
1217                          *  . the cellname followed by a dot
1218                          *  . the cellname minus a single character
1219                          *  . a substring of the cellname that does not consist of full components
1220                          */
1221                         if ( cm_strnicmp_utf8(fnamep, fullname, (int)fnlen-1) == 0 &&
1222                              (fnlen == strlen(fullname)-1 || fullname[fnlen] != '.'))
1223                         {
1224                             /* do not add; substitute fullname for the search */
1225                                 free(fnamep);
1226                                 fnamep = strdup(fullname);
1227                                 code = 0;
1228                                 retry = 1;
1229                         } else {
1230                             code = cm_FreelanceAddSymlink(fnamep, fullname, &rock.fid);
1231                         }
1232                     }
1233                 }
1234             }
1235
1236             if (retry) {
1237                 if (nnamep)
1238                     free(nnamep);
1239                 nnamep = cm_FsStringToNormStringAlloc(fnamep, -1, NULL);
1240                 goto retry_lookup;
1241             }
1242
1243             if (!found || code) {   /* add mount point failed, so give up */
1244                 if (flags & CM_FLAG_CHECKPATH)
1245                     code = CM_ERROR_NOSUCHPATH;
1246                 else
1247                     code = CM_ERROR_NOSUCHFILE;
1248                 goto done;
1249             }
1250             tscp = NULL;   /* to force call of cm_GetSCache */
1251         } else {
1252             if (flags & CM_FLAG_CHECKPATH)
1253                 code = CM_ERROR_NOSUCHPATH;
1254             else
1255                 code = CM_ERROR_NOSUCHFILE;
1256             goto done;
1257         }
1258     }
1259
1260   haveFid:
1261     if ( !tscp )    /* we did not find it in the dnlc */
1262     {
1263         dnlcHit = 0;
1264         code = cm_GetSCache(&rock.fid, &tscp, userp, reqp);
1265         if (code)
1266             goto done;
1267     }
1268     /* tscp is now held */
1269
1270     lock_ObtainWrite(&tscp->rw);
1271
1272     /*
1273      * Do not get status if we do not already have a callback.
1274      * The process of reading the mount point string will obtain status information
1275      * in a single RPC.  No reason to add a second round trip.
1276      *
1277      * If we do have a callback, use cm_SyncOp to get status in case the
1278      * current cm_user_t is not the same as the one that obtained the
1279      * mount point string contents.
1280      */
1281     if (cm_HaveCallback(tscp)) {
1282         code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
1283                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1284         if (code) {
1285             lock_ReleaseWrite(&tscp->rw);
1286             cm_ReleaseSCache(tscp);
1287             goto done;
1288         }
1289         cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1290     }
1291     /* tscp is now locked */
1292
1293     if (!(flags & CM_FLAG_NOMOUNTCHASE)
1294          && tscp->fileType == CM_SCACHETYPE_MOUNTPOINT) {
1295         /* mount points are funny: they have a volume name to mount
1296          * the root of.
1297          */
1298         code = cm_ReadMountPoint(tscp, userp, reqp);
1299         if (code == 0)
1300             code = cm_FollowMountPoint(tscp, dscp, userp, reqp,
1301                                        &mountedScp);
1302         lock_ReleaseWrite(&tscp->rw);
1303         cm_ReleaseSCache(tscp);
1304         if (code)
1305             goto done;
1306
1307         tscp = mountedScp;
1308     }
1309     else {
1310         lock_ReleaseWrite(&tscp->rw);
1311     }
1312
1313     /* copy back pointer */
1314     *outScpp = tscp;
1315
1316     /* insert scache in dnlc */
1317     if ( !dnlcHit && !(flags & CM_FLAG_NOMOUNTCHASE) && rock.ExactFound ) {
1318         /* lock the directory entry to prevent racing callback revokes */
1319         lock_ObtainRead(&dscp->rw);
1320         if ( dscp->cbServerp != NULL && dscp->cbExpires > 0 ) {
1321             /* TODO: reuse nnamep from above */
1322             if (nnamep)
1323                 free(nnamep);
1324             nnamep = cm_ClientStringToNormStringAlloc(cnamep, -1, NULL);
1325             if (nnamep)
1326                 cm_dnlcEnter(dscp, nnamep, tscp);
1327         }
1328         lock_ReleaseRead(&dscp->rw);
1329     }
1330
1331     /* and return */
1332   done:
1333     if (fnamep) {
1334         free (fnamep);
1335         fnamep = NULL;
1336     }
1337     if (nnamep) {
1338         free (nnamep);
1339         nnamep = NULL;
1340     }
1341
1342     return code;
1343 }
1344
1345 int cm_ExpandSysName(clientchar_t *inp, clientchar_t *outp, long outSizeCch, unsigned int index)
1346 {
1347     clientchar_t *tp;
1348     int prefixCount;
1349
1350     tp = cm_ClientStrRChr(inp, '@');
1351     if (tp == NULL)
1352         return 0;               /* no @sys */
1353
1354     if (cm_ClientStrCmp(tp, _C("@sys")) != 0)
1355         return 0;       /* no @sys */
1356
1357     /* caller just wants to know if this is a valid @sys type of name */
1358     if (outp == NULL)
1359         return 1;
1360
1361     if (index >= cm_sysNameCount)
1362         return -1;
1363
1364     /* otherwise generate the properly expanded @sys name */
1365     prefixCount = (int)(tp - inp);
1366
1367     cm_ClientStrCpyN(outp, outSizeCch, inp, prefixCount);       /* copy out "a." from "a.@sys" */
1368     outp[prefixCount] = 0;              /* null terminate the "a." */
1369     cm_ClientStrCat(outp, outSizeCch, cm_sysNameList[index]);/* append i386_nt40 */
1370     return 1;
1371 }
1372
1373 long cm_EvaluateVolumeReference(clientchar_t * namep, long flags, cm_user_t * userp,
1374                                 cm_req_t *reqp, cm_scache_t ** outScpp)
1375 {
1376     afs_uint32    code = 0;
1377     fschar_t      cellName[CELL_MAXNAMELEN];
1378     fschar_t      volumeName[VL_MAXNAMELEN];
1379     size_t        len;
1380     fschar_t *        cp;
1381     fschar_t *        tp;
1382     fschar_t *        fnamep = NULL;
1383
1384     cm_cell_t *   cellp = NULL;
1385     cm_volume_t * volp = NULL;
1386     cm_fid_t      fid;
1387     afs_uint32    volume;
1388     int           volType;
1389     int           mountType = RWVOL;
1390
1391     osi_Log1(afsd_logp, "cm_EvaluateVolumeReference for string [%S]",
1392              osi_LogSaveClientString(afsd_logp, namep));
1393
1394     if (cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) != 0) {
1395         goto _exit_invalid_path;
1396     }
1397
1398     /* namep is assumed to look like the following:
1399
1400        @vol:<cellname>%<volume>\0
1401        or
1402        @vol:<cellname>#<volume>\0
1403
1404      */
1405
1406     fnamep = cm_ClientStringToFsStringAlloc(namep, -1, NULL);
1407     cp = fnamep + CM_PREFIX_VOL_CCH; /* cp points to cell name, hopefully */
1408     tp = cm_FsStrChr(cp, '%');
1409     if (tp == NULL)
1410         tp = cm_FsStrChr(cp, '#');
1411     if (tp == NULL ||
1412         (len = tp - cp) == 0 ||
1413         len > CELL_MAXNAMELEN)
1414         goto _exit_invalid_path;
1415     cm_FsStrCpyN(cellName, lengthof(cellName), cp, len);
1416
1417     if (*tp == '#')
1418         mountType = ROVOL;
1419
1420     cp = tp+1;                  /* cp now points to volume, supposedly */
1421     cm_FsStrCpy(volumeName, lengthof(volumeName), cp);
1422
1423     /* OK, now we have the cell and the volume */
1424     osi_Log2(afsd_logp, "   Found cell [%s] and volume [%s]",
1425              osi_LogSaveFsString(afsd_logp, cellName),
1426              osi_LogSaveFsString(afsd_logp, volumeName));
1427
1428     cellp = cm_GetCell(cellName, CM_FLAG_CREATE);
1429     if (cellp == NULL) {
1430         goto _exit_invalid_path;
1431     }
1432
1433     len = cm_FsStrLen(volumeName);
1434     if (len >= 8 && cm_FsStrCmp(volumeName + len - 7, ".backup") == 0)
1435         volType = BACKVOL;
1436     else if (len >= 10 &&
1437              cm_FsStrCmp(volumeName + len - 9, ".readonly") == 0)
1438         volType = ROVOL;
1439     else
1440         volType = RWVOL;
1441
1442     if (cm_VolNameIsID(volumeName)) {
1443         code = cm_FindVolumeByID(cellp, atoi(volumeName), userp, reqp,
1444                                 CM_GETVOL_FLAG_CREATE, &volp);
1445     } else {
1446         code = cm_FindVolumeByName(cellp, volumeName, userp, reqp,
1447                                   CM_GETVOL_FLAG_CREATE, &volp);
1448     }
1449
1450     if (code != 0)
1451         goto _exit_cleanup;
1452
1453     if (volType == BACKVOL)
1454         volume = volp->vol[BACKVOL].ID;
1455     else if (volType == ROVOL ||
1456              (volType == RWVOL && mountType == ROVOL && volp->vol[ROVOL].ID != 0))
1457         volume = volp->vol[ROVOL].ID;
1458     else
1459         volume = volp->vol[RWVOL].ID;
1460
1461     cm_SetFid(&fid, cellp->cellID, volume, 1, 1);
1462
1463     code = cm_GetSCache(&fid, outScpp, userp, reqp);
1464
1465   _exit_cleanup:
1466     if (fnamep)
1467         free(fnamep);
1468
1469     if (volp)
1470         cm_PutVolume(volp);
1471
1472     if (code == 0)
1473         return code;
1474
1475  _exit_invalid_path:
1476     if (flags & CM_FLAG_CHECKPATH)
1477         return CM_ERROR_NOSUCHPATH;
1478     else
1479         return CM_ERROR_NOSUCHFILE;
1480 }
1481
1482 #ifdef DEBUG_REFCOUNT
1483 long cm_LookupDbg(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1484                cm_req_t *reqp, cm_scache_t **outScpp, char * file, long line)
1485 #else
1486 long cm_Lookup(cm_scache_t *dscp, clientchar_t *namep, long flags, cm_user_t *userp,
1487                cm_req_t *reqp, cm_scache_t **outScpp)
1488 #endif
1489 {
1490     long code;
1491     clientchar_t tname[AFSPATHMAX];
1492     int sysNameIndex = 0;
1493     cm_scache_t *scp = NULL;
1494
1495 #ifdef DEBUG_REFCOUNT
1496     afsi_log("%s:%d cm_Lookup dscp 0x%p ref %d", file, line, dscp, dscp->refCount, file, line);
1497     osi_Log2(afsd_logp, "cm_Lookup dscp 0x%p ref %d", dscp, dscp->refCount);
1498 #endif
1499
1500     if ( cm_ClientStrCmpI(namep,_C(SMB_IOCTL_FILENAME_NOSLASH)) == 0 ) {
1501         if (flags & CM_FLAG_CHECKPATH)
1502             return CM_ERROR_NOSUCHPATH;
1503         else
1504             return CM_ERROR_NOSUCHFILE;
1505     }
1506
1507     if (dscp == cm_data.rootSCachep &&
1508         cm_ClientStrCmpNI(namep, _C(CM_PREFIX_VOL), CM_PREFIX_VOL_CCH) == 0) {
1509         return cm_EvaluateVolumeReference(namep, flags, userp, reqp, outScpp);
1510     }
1511
1512     if (cm_ExpandSysName(namep, NULL, 0, 0) > 0) {
1513         for ( sysNameIndex = 0; sysNameIndex < MAXNUMSYSNAMES; sysNameIndex++) {
1514             code = cm_ExpandSysName(namep, tname, lengthof(tname), sysNameIndex);
1515             if (code > 0) {
1516                 code = cm_LookupInternal(dscp, tname, flags, userp, reqp, &scp);
1517 #ifdef DEBUG_REFCOUNT
1518                 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);
1519                 osi_Log3(afsd_logp, "cm_LookupInternal (1) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1520 #endif
1521
1522                 if (code == 0) {
1523                     *outScpp = scp;
1524                     return 0;
1525                 }
1526                 if (scp) {
1527                     cm_ReleaseSCache(scp);
1528                     scp = NULL;
1529                 }
1530             } else {
1531                 code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1532 #ifdef DEBUG_REFCOUNT
1533                 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);
1534                 osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1535 #endif
1536                 *outScpp = scp;
1537                 return code;
1538             }
1539         }
1540     } else {
1541         code = cm_LookupInternal(dscp, namep, flags, userp, reqp, &scp);
1542 #ifdef DEBUG_REFCOUNT
1543         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);
1544         osi_Log3(afsd_logp, "cm_LookupInternal (2) code 0x%x dscp 0x%p scp 0x%p", code, dscp, scp);
1545 #endif
1546         *outScpp = scp;
1547         return code;
1548     }
1549
1550     /* None of the possible sysName expansions could be found */
1551     if (flags & CM_FLAG_CHECKPATH)
1552         return CM_ERROR_NOSUCHPATH;
1553     else
1554         return CM_ERROR_NOSUCHFILE;
1555 }
1556
1557 /*! \brief Unlink a file name
1558
1559   Encapsulates a call to RXAFS_RemoveFile().
1560
1561   \param[in] dscp cm_scache_t pointing at the directory containing the
1562       name to be unlinked.
1563
1564   \param[in] fnamep Original name to be unlinked.  This is the
1565       name that will be passed into the RXAFS_RemoveFile() call.
1566       This parameter is optional.  If not provided, the value will
1567       be looked up.
1568
1569   \param[in] came Client name to be unlinked.  This name will be used
1570       to update the local directory caches.
1571
1572   \param[in] userp cm_user_t for the request.
1573
1574   \param[in] reqp Request tracker.
1575
1576  */
1577 long cm_Unlink(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t * cnamep,
1578                cm_user_t *userp, cm_req_t *reqp)
1579 {
1580     long code;
1581     cm_conn_t *connp;
1582     AFSFid afsFid;
1583     int sflags;
1584     AFSFetchStatus newDirStatus;
1585     AFSVolSync volSync;
1586     struct rx_connection * rxconnp;
1587     cm_dirOp_t dirop;
1588     cm_scache_t *scp = NULL;
1589     int free_fnamep = FALSE;
1590
1591     memset(&volSync, 0, sizeof(volSync));
1592
1593     if (fnamep == NULL) {
1594         code = -1;
1595 #ifdef USE_BPLUS
1596         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
1597                              CM_DIROP_FLAG_NONE, &dirop);
1598         if (code == 0) {
1599             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
1600             if (code == 0)
1601                 free_fnamep = TRUE;
1602             cm_EndDirOp(&dirop);
1603         }
1604 #endif
1605         if (code)
1606             goto done;
1607     }
1608
1609 #ifdef AFS_FREELANCE_CLIENT
1610     if (cm_freelanceEnabled && dscp == cm_data.rootSCachep) {
1611         /* deleting a mount point from the root dir. */
1612         code = cm_FreelanceRemoveMount(fnamep);
1613         goto done;
1614     }
1615 #endif
1616
1617     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
1618     if (code)
1619         goto done;
1620
1621     /* Check for RO volume */
1622     if (dscp->flags & CM_SCACHEFLAG_RO) {
1623         code = CM_ERROR_READONLY;
1624         goto done;
1625     }
1626
1627     /* make sure we don't screw up the dir status during the merge */
1628     code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE,
1629                          CM_DIROP_FLAG_NONE, &dirop);
1630
1631     lock_ObtainWrite(&dscp->rw);
1632     sflags = CM_SCACHESYNC_STOREDATA;
1633     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, sflags);
1634     lock_ReleaseWrite(&dscp->rw);
1635     if (code) {
1636         cm_EndDirOp(&dirop);
1637         goto done;
1638     }
1639
1640     /* make the RPC */
1641     afsFid.Volume = dscp->fid.volume;
1642     afsFid.Vnode = dscp->fid.vnode;
1643     afsFid.Unique = dscp->fid.unique;
1644
1645     osi_Log1(afsd_logp, "CALL RemoveFile scp 0x%p", dscp);
1646     do {
1647         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
1648         if (code)
1649             continue;
1650
1651         rxconnp = cm_GetRxConn(connp);
1652         code = RXAFS_RemoveFile(rxconnp, &afsFid, fnamep,
1653                                 &newDirStatus, &volSync);
1654         rx_PutConnection(rxconnp);
1655
1656     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, &volSync, NULL, NULL, code));
1657     code = cm_MapRPCError(code, reqp);
1658
1659     if (code)
1660         osi_Log1(afsd_logp, "CALL RemoveFile FAILURE, code 0x%x", code);
1661     else
1662         osi_Log0(afsd_logp, "CALL RemoveFile SUCCESS");
1663
1664     if (dirop.scp) {
1665         lock_ObtainWrite(&dirop.scp->dirlock);
1666         dirop.lockType = CM_DIRLOCK_WRITE;
1667     }
1668     lock_ObtainWrite(&dscp->rw);
1669     cm_dnlcRemove(dscp, cnamep);
1670     if (code == 0) {
1671         cm_MergeStatus(NULL, dscp, &newDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
1672     } else if (code == CM_ERROR_NOSUCHFILE) {
1673         /* windows would not have allowed the request to delete the file
1674          * if it did not believe the file existed.  therefore, we must
1675          * have an inconsistent view of the world.
1676          */
1677         dscp->cbServerp = NULL;
1678     }
1679     cm_SyncOpDone(dscp, NULL, sflags);
1680     lock_ReleaseWrite(&dscp->rw);
1681
1682     if (code == 0 && cm_CheckDirOpForSingleChange(&dirop) && cnamep) {
1683         cm_DirDeleteEntry(&dirop, fnamep);
1684 #ifdef USE_BPLUS
1685         cm_BPlusDirDeleteEntry(&dirop, cnamep);
1686 #endif
1687     }
1688     cm_EndDirOp(&dirop);
1689
1690     if (scp) {
1691         cm_ReleaseSCache(scp);
1692         if (code == 0) {
1693             lock_ObtainWrite(&scp->rw);
1694             if (--scp->linkCount == 0)
1695                 scp->flags |= CM_SCACHEFLAG_DELETED;
1696             cm_DiscardSCache(scp);
1697             lock_ReleaseWrite(&scp->rw);
1698         }
1699     }
1700
1701   done:
1702     if (free_fnamep)
1703         free(fnamep);
1704
1705     return code;
1706 }
1707
1708 /* called with a write locked vnode, and fills in the link info.
1709  * returns this the vnode still write locked.
1710  */
1711 long cm_HandleLink(cm_scache_t *linkScp, cm_user_t *userp, cm_req_t *reqp)
1712 {
1713     long code = 0;
1714
1715     lock_AssertWrite(&linkScp->rw);
1716     if (!linkScp->mountPointStringp[0]) {
1717
1718 #ifdef AFS_FREELANCE_CLIENT
1719         /* File servers do not have data for freelance entries */
1720         if (cm_freelanceEnabled &&
1721             linkScp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
1722             linkScp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
1723         {
1724             code = cm_FreelanceFetchMountPointString(linkScp);
1725         } else
1726 #endif /* AFS_FREELANCE_CLIENT */
1727         {
1728             char temp[MOUNTPOINTLEN];
1729             osi_hyper_t offset;
1730
1731             /* read the link data from the file server */
1732             offset.LowPart = offset.HighPart = 0;
1733             code = cm_GetData(linkScp, &offset, temp, MOUNTPOINTLEN, userp, reqp);
1734             if (code)
1735                 return code;
1736
1737             /*
1738              * linkScp->length is the actual length of the symlink target string.
1739              * It is current because cm_GetData merged the most up to date
1740              * status info into scp and has not dropped the rwlock since.
1741              */
1742             if (linkScp->length.LowPart > MOUNTPOINTLEN - 1)
1743                 return CM_ERROR_TOOBIG;
1744             if (linkScp->length.LowPart == 0)
1745                 return CM_ERROR_INVAL;
1746
1747             /* make sure we are NUL terminated */
1748             temp[linkScp->length.LowPart] = 0;
1749             memcpy(linkScp->mountPointStringp, temp, linkScp->length.LowPart + 1);
1750         }
1751
1752         if ( !strnicmp(linkScp->mountPointStringp, "msdfs:", strlen("msdfs:")) )
1753             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1754
1755     }   /* don't have symlink contents cached */
1756
1757     return code;
1758 }
1759
1760 /* called with a held vnode and a path suffix, with the held vnode being a
1761  * symbolic link.  Our goal is to generate a new path to interpret, and return
1762  * this new path in newSpaceBufferp.  If the new vnode is relative to a dir
1763  * other than the directory containing the symbolic link, then the new root is
1764  * returned in *newRootScpp, otherwise a null is returned there.
1765  */
1766 long cm_AssembleLink(cm_scache_t *linkScp, fschar_t *pathSuffixp,
1767                      cm_scache_t **newRootScpp, cm_space_t **newSpaceBufferp,
1768                      cm_user_t *userp, cm_req_t *reqp)
1769 {
1770     long code = 0;
1771     long len;
1772     fschar_t *linkp;
1773     cm_space_t *tsp;
1774
1775     *newRootScpp = NULL;
1776     *newSpaceBufferp = NULL;
1777
1778     lock_ObtainWrite(&linkScp->rw);
1779     /*
1780      * Do not get status if we do not already have a callback.
1781      * The process of reading the symlink string will obtain status information
1782      * in a single RPC.  No reason to add a second round trip.
1783      *
1784      * If we do have a callback, use cm_SyncOp to get status in case the
1785      * current cm_user_t is not the same as the one that obtained the
1786      * symlink string contents.
1787      */
1788     if (cm_HaveCallback(linkScp)) {
1789         code = cm_SyncOp(linkScp, NULL, userp, reqp, 0,
1790                           CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
1791         if (code) {
1792             lock_ReleaseWrite(&linkScp->rw);
1793             cm_ReleaseSCache(linkScp);
1794             goto done;
1795         }
1796         cm_SyncOpDone(linkScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1797     }
1798     code = cm_HandleLink(linkScp, userp, reqp);
1799     if (code)
1800         goto done;
1801
1802     /* if we may overflow the buffer, bail out; buffer is signficantly
1803      * bigger than max path length, so we don't really have to worry about
1804      * being a little conservative here.
1805      */
1806     if (cm_FsStrLen(linkScp->mountPointStringp) + cm_FsStrLen(pathSuffixp) + 2
1807         >= CM_UTILS_SPACESIZE) {
1808         code = CM_ERROR_TOOBIG;
1809         goto done;
1810     }
1811
1812     tsp = cm_GetSpace();
1813     linkp = linkScp->mountPointStringp;
1814     if (strncmp(linkp, cm_mountRoot, cm_mountRootLen) == 0) {
1815         if (strlen(linkp) > cm_mountRootLen)
1816             StringCbCopyA((char *) tsp->data, sizeof(tsp->data), linkp+cm_mountRootLen+1);
1817         else
1818             tsp->data[0] = 0;
1819         *newRootScpp = cm_RootSCachep(userp, reqp);
1820         cm_HoldSCache(*newRootScpp);
1821     } else if (linkp[0] == '\\' && linkp[1] == '\\') {
1822         if (!strnicmp(&linkp[2], cm_NetbiosName, (len = (long)strlen(cm_NetbiosName))))
1823         {
1824             char * p = &linkp[len + 3];
1825             if (strnicmp(p, "all", 3) == 0)
1826                 p += 4;
1827
1828             StringCbCopyA(tsp->data, sizeof(tsp->data), p);
1829             for (p = tsp->data; *p; p++) {
1830                 if (*p == '\\')
1831                     *p = '/';
1832             }
1833             *newRootScpp = cm_RootSCachep(userp, reqp);
1834             cm_HoldSCache(*newRootScpp);
1835         } else {
1836             linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1837             StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1838             code = CM_ERROR_PATH_NOT_COVERED;
1839         }
1840     } else if ( linkScp->fileType == CM_SCACHETYPE_DFSLINK ||
1841                 !strnicmp(linkp, "msdfs:", (len = (long)strlen("msdfs:"))) ) {
1842         linkScp->fileType = CM_SCACHETYPE_DFSLINK;
1843         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1844         code = CM_ERROR_PATH_NOT_COVERED;
1845     } else if (*linkp == '\\' || *linkp == '/') {
1846 #if 0
1847         /* formerly, this was considered to be from the AFS root,
1848          * but this seems to create problems.  instead, we will just
1849          * reject the link */
1850         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp+1);
1851         *newRootScpp = cm_RootSCachep(userp, reqp);
1852         cm_HoldSCache(*newRootScpp);
1853 #else
1854         /* we still copy the link data into the response so that
1855          * the user can see what the link points to
1856          */
1857         linkScp->fileType = CM_SCACHETYPE_INVALID;
1858         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1859         code = CM_ERROR_NOSUCHPATH;
1860 #endif
1861     } else {
1862         /* a relative link */
1863         StringCchCopyA(tsp->data,lengthof(tsp->data), linkp);
1864     }
1865     if (pathSuffixp[0] != 0) {  /* if suffix string is non-null */
1866         StringCchCatA(tsp->data,lengthof(tsp->data), "\\");
1867         StringCchCatA(tsp->data,lengthof(tsp->data), pathSuffixp);
1868     }
1869
1870     if (code == 0) {
1871         clientchar_t * cpath = cm_FsStringToClientStringAlloc(tsp->data, -1, NULL);
1872         if (cpath != NULL) {
1873         cm_ClientStrCpy(tsp->wdata, lengthof(tsp->wdata), cpath);
1874         free(cpath);
1875         *newSpaceBufferp = tsp;
1876     } else {
1877             code = CM_ERROR_NOSUCHPATH;
1878         }
1879     }
1880
1881     if (code != 0) {
1882         cm_FreeSpace(tsp);
1883
1884         if (code == CM_ERROR_PATH_NOT_COVERED && reqp->tidPathp && reqp->relPathp) {
1885             cm_VolStatus_Notify_DFS_Mapping(linkScp, reqp->tidPathp, reqp->relPathp);
1886         }
1887     }
1888
1889  done:
1890     lock_ReleaseWrite(&linkScp->rw);
1891     return code;
1892 }
1893 #ifdef DEBUG_REFCOUNT
1894 long cm_NameIDbg(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1895                  cm_user_t *userp, clientchar_t *tidPathp, cm_req_t *reqp,
1896                  cm_scache_t **outScpp,
1897                  char * file, long line)
1898 #else
1899 long cm_NameI(cm_scache_t *rootSCachep, clientchar_t *pathp, long flags,
1900               cm_user_t *userp, clientchar_t *tidPathp,
1901               cm_req_t *reqp, cm_scache_t **outScpp)
1902 #endif
1903 {
1904     long code;
1905     clientchar_t *tp;                   /* ptr moving through input buffer */
1906     clientchar_t tc;                    /* temp char */
1907     int haveComponent;          /* has new component started? */
1908     clientchar_t component[AFSPATHMAX]; /* this is the new component */
1909     clientchar_t *cp;                   /* component name being assembled */
1910     cm_scache_t *tscp;          /* current location in the hierarchy */
1911     cm_scache_t *nscp;          /* next dude down */
1912     cm_scache_t *dirScp;        /* last dir we searched */
1913     cm_scache_t *linkScp;       /* new root for the symlink we just
1914     * looked up */
1915     cm_space_t *psp;            /* space for current path, if we've hit
1916     * any symlinks */
1917     cm_space_t *tempsp;         /* temp vbl */
1918     clientchar_t *restp;                /* rest of the pathname to interpret */
1919     int symlinkCount;           /* count of # of symlinks traversed */
1920     int extraFlag;              /* avoid chasing mt pts for dir cmd */
1921     int phase = 1;              /* 1 = tidPathp, 2 = pathp */
1922 #define MAX_FID_COUNT 512
1923     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1924     int fid_count = 0;          /* number of fids processed in this path walk */
1925     int i;
1926
1927     *outScpp = NULL;
1928
1929 #ifdef DEBUG_REFCOUNT
1930     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1931     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1932              rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1933              flags);
1934 #endif
1935
1936     tp = tidPathp;
1937     if (tp == NULL) {
1938         tp = pathp;
1939         phase = 2;
1940     }
1941     if (tp == NULL) {
1942         tp = _C("");
1943     }
1944     haveComponent = 0;
1945     psp = NULL;
1946     tscp = rootSCachep;
1947     cm_HoldSCache(tscp);
1948     symlinkCount = 0;
1949     dirScp = NULL;
1950
1951
1952     while (1) {
1953         tc = *tp++;
1954
1955         /* map Unix slashes into DOS ones so we can interpret Unix
1956          * symlinks properly
1957          */
1958         if (tc == '/')
1959             tc = '\\';
1960
1961         if (!haveComponent) {
1962             if (tc == '\\') {
1963                 continue;
1964             } else if (tc == 0) {
1965                 if (phase == 1) {
1966                     phase = 2;
1967                     tp = pathp;
1968                     continue;
1969                 }
1970                 code = 0;
1971                 break;
1972             } else {
1973                 haveComponent = 1;
1974                 cp = component;
1975                 *cp++ = tc;
1976             }
1977         } else {
1978             /* we have a component here */
1979             if (tc == 0 || tc == '\\') {
1980                 /* end of the component; we're at the last
1981                  * component if tc == 0.  However, if the last
1982                  * is a symlink, we have more to do.
1983                  */
1984                 *cp++ = 0;      /* add null termination */
1985                 extraFlag = 0;
1986                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
1987                     extraFlag = CM_FLAG_NOMOUNTCHASE;
1988                 code = cm_Lookup(tscp, component,
1989                                  flags | extraFlag,
1990                                  userp, reqp, &nscp);
1991
1992                 if (code == 0) {
1993                     if (!cm_ClientStrCmp(component,_C("..")) ||
1994                         !cm_ClientStrCmp(component,_C("."))) {
1995                         /*
1996                          * roll back the fid list until we find the
1997                          * fid that matches where we are now.  Its not
1998                          * necessarily one or two fids because they
1999                          * might have been symlinks or mount points or
2000                          * both that were crossed.
2001                          */
2002                         for ( i=fid_count-1; i>=0; i--) {
2003                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
2004                                 break;
2005                         }
2006                         fid_count = i+1;
2007                     } else {
2008                         /* add the new fid to the list */
2009                         if (fid_count == MAX_FID_COUNT) {
2010                             code = CM_ERROR_TOO_MANY_SYMLINKS;
2011                             cm_ReleaseSCache(nscp);
2012                             nscp = NULL;
2013                             break;
2014                         }
2015                         fids[fid_count++] = nscp->fid;
2016                     }
2017                 }
2018
2019                 if (code) {
2020                     cm_ReleaseSCache(tscp);
2021                     if (dirScp)
2022                         cm_ReleaseSCache(dirScp);
2023                     if (psp)
2024                         cm_FreeSpace(psp);
2025                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2026                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2027                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2028                         return CM_ERROR_NOSUCHPATH;
2029                     } else {
2030                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2031                         return code;
2032                     }
2033                 }
2034
2035                 haveComponent = 0;      /* component done */
2036                 if (dirScp)
2037                     cm_ReleaseSCache(dirScp);
2038                 dirScp = tscp;          /* for some symlinks */
2039                 tscp = nscp;            /* already held */
2040                 nscp = NULL;
2041                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2042                     code = 0;
2043                     if (dirScp) {
2044                         cm_ReleaseSCache(dirScp);
2045                         dirScp = NULL;
2046                     }
2047                     break;
2048                 }
2049
2050                 /* now, if tscp is a symlink, we should follow it and
2051                  * assemble the path again.
2052                  */
2053                 lock_ObtainWrite(&tscp->rw);
2054                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2055                                   CM_SCACHESYNC_GETSTATUS
2056                                   | CM_SCACHESYNC_NEEDCALLBACK);
2057                 if (code) {
2058                     lock_ReleaseWrite(&tscp->rw);
2059                     cm_ReleaseSCache(tscp);
2060                     tscp = NULL;
2061                     if (dirScp) {
2062                         cm_ReleaseSCache(dirScp);
2063                         dirScp = NULL;
2064                     }
2065                     break;
2066                 }
2067                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2068
2069                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2070                     /* this is a symlink; assemble a new buffer */
2071                     lock_ReleaseWrite(&tscp->rw);
2072                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2073                         cm_ReleaseSCache(tscp);
2074                         tscp = NULL;
2075                         if (dirScp) {
2076                             cm_ReleaseSCache(dirScp);
2077                             dirScp = NULL;
2078                         }
2079                         if (psp)
2080                             cm_FreeSpace(psp);
2081                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2082                         return CM_ERROR_TOO_MANY_SYMLINKS;
2083                     }
2084                     if (tc == 0)
2085                         restp = _C("");
2086                     else
2087                         restp = tp;
2088
2089                     {
2090                         fschar_t * frestp;
2091
2092                         /* TODO: make this better */
2093                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2094                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2095                         free(frestp);
2096                     }
2097
2098                     if (code == 0 && linkScp != NULL) {
2099                         if (linkScp == cm_data.rootSCachep) {
2100                             fid_count = 0;
2101                             i = 0;
2102                         } else {
2103                             for ( i=0; i<fid_count; i++) {
2104                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2105                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2106                                     cm_ReleaseSCache(linkScp);
2107                                     nscp = NULL;
2108                                     break;
2109                                 }
2110                             }
2111                         }
2112                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2113                             fids[fid_count++] = linkScp->fid;
2114                         }
2115                     }
2116
2117                     if (code) {
2118                         /* something went wrong */
2119                         cm_ReleaseSCache(tscp);
2120                         tscp = NULL;
2121                         if (dirScp) {
2122                             cm_ReleaseSCache(dirScp);
2123                             dirScp = NULL;
2124                         }
2125                         break;
2126                     }
2127
2128                     /* otherwise, tempsp has the new path,
2129                      * and linkScp is the new root from
2130                      * which to interpret that path.
2131                      * Continue with the namei processing,
2132                      * also doing the bookkeeping for the
2133                      * space allocation and tracking the
2134                      * vnode reference counts.
2135                      */
2136                     if (psp)
2137                         cm_FreeSpace(psp);
2138                     psp = tempsp;
2139                     tp = psp->wdata;
2140                     cm_ReleaseSCache(tscp);
2141                     tscp = linkScp;
2142                     linkScp = NULL;
2143                     /* already held
2144                      * by AssembleLink
2145                      * now, if linkScp is null, that's
2146                      * AssembleLink's way of telling us that
2147                      * the sym link is relative to the dir
2148                      * containing the link.  We have a ref
2149                      * to it in dirScp, and we hold it now
2150                      * and reuse it as the new spot in the
2151                      * dir hierarchy.
2152                      */
2153                     if (tscp == NULL) {
2154                         tscp = dirScp;
2155                         dirScp = NULL;
2156                     }
2157                 } else {
2158                     /* not a symlink, we may be done */
2159                     lock_ReleaseWrite(&tscp->rw);
2160                     if (tc == 0) {
2161                         if (phase == 1) {
2162                             phase = 2;
2163                             tp = pathp;
2164                             continue;
2165                         }
2166                         if (dirScp) {
2167                             cm_ReleaseSCache(dirScp);
2168                             dirScp = NULL;
2169                         }
2170                         code = 0;
2171                         break;
2172                     }
2173                 }
2174                 if (dirScp) {
2175                     cm_ReleaseSCache(dirScp);
2176                     dirScp = NULL;
2177                 }
2178             } /* end of a component */
2179             else
2180                 *cp++ = tc;
2181         } /* we have a component */
2182     } /* big while loop over all components */
2183
2184     /* already held */
2185     if (dirScp)
2186         cm_ReleaseSCache(dirScp);
2187     if (psp)
2188         cm_FreeSpace(psp);
2189     if (code == 0)
2190         *outScpp = tscp;
2191     else if (tscp)
2192         cm_ReleaseSCache(tscp);
2193
2194 #ifdef DEBUG_REFCOUNT
2195     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2196 #endif
2197     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2198     return code;
2199 }
2200
2201 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2202  * We chase the link, and return a held pointer to the target, if it exists,
2203  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2204  * and do not hold or return a target vnode.
2205  *
2206  * This is very similar to calling cm_NameI with the last component of a name,
2207  * which happens to be a symlink, except that we've already passed by the name.
2208  *
2209  * This function is typically called by the directory listing functions, which
2210  * encounter symlinks but need to return the proper file length so programs
2211  * like "more" work properly when they make use of the attributes retrieved from
2212  * the dir listing.
2213  *
2214  * The input vnode should not be locked when this function is called.
2215  */
2216 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2217                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2218 {
2219     long code;
2220     cm_space_t *spacep;
2221     cm_scache_t *newRootScp;
2222
2223     *outScpp = NULL;
2224
2225     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2226
2227     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2228     if (code)
2229         return code;
2230
2231     /* now, if newRootScp is NULL, we're really being told that the symlink
2232      * is relative to the current directory (dscp).
2233      */
2234     if (newRootScp == NULL) {
2235         newRootScp = dscp;
2236         cm_HoldSCache(dscp);
2237     }
2238
2239     code = cm_NameI(newRootScp, spacep->wdata,
2240                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2241                     userp, NULL, reqp, outScpp);
2242
2243     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2244         code = CM_ERROR_NOSUCHPATH;
2245
2246     /* this stuff is allocated no matter what happened on the namei call,
2247      * so free it */
2248     cm_FreeSpace(spacep);
2249     cm_ReleaseSCache(newRootScp);
2250
2251     if (linkScp == *outScpp) {
2252         cm_ReleaseSCache(*outScpp);
2253         *outScpp = NULL;
2254         code = CM_ERROR_NOSUCHPATH;
2255     }
2256
2257     return code;
2258 }
2259
2260 /* for a given entry, make sure that it isn't in the stat cache, and then
2261  * add it to the list of file IDs to be obtained.
2262  *
2263  * Don't bother adding it if we already have a vnode.  Note that the dir
2264  * is locked, so we have to be careful checking the vnode we're thinking of
2265  * processing, to avoid deadlocks.
2266  */
2267 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2268                      osi_hyper_t *offp)
2269 {
2270     osi_hyper_t thyper;
2271     cm_bulkStat_t *bsp;
2272     int i;
2273     cm_scache_t *tscp;
2274     cm_fid_t tfid;
2275
2276     bsp = rockp;
2277
2278     /* Don't overflow bsp. */
2279     if (bsp->counter >= CM_BULKMAX)
2280         return CM_ERROR_STOPNOW;
2281
2282     thyper.LowPart = cm_data.buf_blockSize;
2283     thyper.HighPart = 0;
2284     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2285
2286     /* thyper is now the first byte past the end of the record we're
2287      * interested in, and bsp->bufOffset is the first byte of the record
2288      * we're interested in.
2289      * Skip data in the others.
2290      * Skip '.' and '..'
2291      */
2292     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2293         return 0;
2294     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2295         return CM_ERROR_STOPNOW;
2296     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2297         return 0;
2298
2299     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2300     tscp = cm_FindSCache(&tfid);
2301     if (tscp) {
2302         if (lock_TryWrite(&tscp->rw)) {
2303             /* we have an entry that we can look at */
2304             if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
2305                 /* we have a callback on it.  Don't bother
2306                  * fetching this stat entry, since we're happy
2307                  * with the info we have.
2308                  */
2309                 lock_ReleaseWrite(&tscp->rw);
2310                 cm_ReleaseSCache(tscp);
2311                 return 0;
2312             }
2313             lock_ReleaseWrite(&tscp->rw);
2314         }       /* got lock */
2315         cm_ReleaseSCache(tscp);
2316     }   /* found entry */
2317
2318 #ifdef AFS_FREELANCE_CLIENT
2319     // yj: if this is a mountpoint under root.afs then we don't want it
2320     // to be bulkstat-ed, instead, we call getSCache directly and under
2321     // getSCache, it is handled specially.
2322     if  ( cm_freelanceEnabled &&
2323           tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2324           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2325           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2326     {
2327         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2328         return cm_GetSCache(&tfid, &tscp, NULL, NULL);
2329     }
2330 #endif /* AFS_FREELANCE_CLIENT */
2331
2332     i = bsp->counter++;
2333     bsp->fids[i].Volume = scp->fid.volume;
2334     bsp->fids[i].Vnode = tfid.vnode;
2335     bsp->fids[i].Unique = tfid.unique;
2336     return 0;
2337 }
2338
2339 afs_int32
2340 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2341 {
2342     afs_int32 code = 0;
2343     AFSCBFids fidStruct;
2344     AFSBulkStats statStruct;
2345     cm_conn_t *connp;
2346     AFSCBs callbackStruct;
2347     long filex;
2348     AFSVolSync volSync;
2349     cm_callbackRequest_t cbReq;
2350     long filesThisCall;
2351     long i;
2352     long j;
2353     cm_scache_t *scp;
2354     cm_fid_t tfid;
2355     struct rx_connection * rxconnp;
2356     int inlinebulk;             /* Did we use InlineBulkStatus RPC or not? */
2357
2358     memset(&volSync, 0, sizeof(volSync));
2359
2360     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2361      * make the calls to create the entries.  Handle AFSCBMAX files at a
2362      * time.
2363      */
2364     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2365         filesThisCall = bbp->counter - filex;
2366         if (filesThisCall > AFSCBMAX)
2367             filesThisCall = AFSCBMAX;
2368
2369         fidStruct.AFSCBFids_len = filesThisCall;
2370         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2371         statStruct.AFSBulkStats_len = filesThisCall;
2372         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2373         callbackStruct.AFSCBs_len = filesThisCall;
2374         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2375         cm_StartCallbackGrantingCall(NULL, &cbReq);
2376         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2377
2378         /*
2379          * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2380          * be a FID provided.  However, the error code from RXAFS_BulkStatus
2381          * or RXAFS_InlinkBulkStatus does not apply to any FID.  Therefore,
2382          * we generate an invalid FID to match with the RPC error.
2383          */
2384         cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2385
2386         do {
2387             inlinebulk = 0;
2388
2389             code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2390             if (code)
2391                 continue;
2392
2393             rxconnp = cm_GetRxConn(connp);
2394             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2395                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2396                                               &statStruct, &callbackStruct, &volSync);
2397                 if (code == RXGEN_OPCODE) {
2398                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2399                 } else {
2400                     inlinebulk = 1;
2401                 }
2402             }
2403             if (!inlinebulk) {
2404                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2405                                         &statStruct, &callbackStruct, &volSync);
2406             }
2407             rx_PutConnection(rxconnp);
2408
2409             /*
2410              * If InlineBulk RPC was called and it succeeded,
2411              * then pull out the return code from the status info
2412              * and use it for cm_Analyze so that we can failover to other
2413              * .readonly volume instances.  But only do it for errors that
2414              * are volume global.
2415              */
2416             if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2417                 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2418                           (&bbp->stats[0])->errorCode);
2419                 switch ((&bbp->stats[0])->errorCode) {
2420                 case VBUSY:
2421                 case VRESTARTING:
2422                 case VNOVOL:
2423                 case VMOVED:
2424                 case VOFFLINE:
2425                 case VSALVAGE:
2426                 case VNOSERVICE:
2427                 case VIO:
2428                     code = (&bbp->stats[0])->errorCode;
2429                     break;
2430                 default:
2431                     /* Rx and Rxkad errors are volume global */
2432                     if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2433                          (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2434                         code = (&bbp->stats[0])->errorCode;
2435                 }
2436             }
2437         } while (cm_Analyze(connp, userp, reqp, &tfid, &volSync, NULL, &cbReq, code));
2438         code = cm_MapRPCError(code, reqp);
2439
2440         /*
2441          * might as well quit on an error, since we're not going to do
2442          * much better on the next immediate call, either.
2443          */
2444         if (code) {
2445             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2446                       inlinebulk ? "Inline" : "", code);
2447             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2448             break;
2449         }
2450
2451         /*
2452          * The bulk RPC has succeeded or at least not failed with a
2453          * volume global error result.  For items that have inlineBulk
2454          * errors we must call cm_Analyze in order to perform required
2455          * logging of errors.
2456          *
2457          * If the RPC was not inline bulk or the entry either has no error
2458          * the status must be merged.
2459          */
2460         osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2461
2462         for (i = 0; i<filesThisCall; i++) {
2463             j = filex + i;
2464             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2465
2466             if (inlinebulk && (&bbp->stats[j])->errorCode) {
2467                 cm_req_t treq = *reqp;
2468                 cm_Analyze(NULL, userp, &treq, &tfid, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2469             } else {
2470                 code = cm_GetSCache(&tfid, &scp, userp, reqp);
2471                 if (code != 0)
2472                     continue;
2473
2474                 /*
2475                  * otherwise, if this entry has no callback info,
2476                  * merge in this.  If there is existing callback info
2477                  * we skip the merge because the existing data must be
2478                  * current (we have a callback) and the response from
2479                  * a non-inline bulk rpc might actually be wrong.
2480                  *
2481                  * now, we have to be extra paranoid on merging in this
2482                  * information, since we didn't use cm_SyncOp before
2483                  * starting the fetch to make sure that no bad races
2484                  * were occurring.  Specifically, we need to make sure
2485                  * we don't obliterate any newer information in the
2486                  * vnode than have here.
2487                  *
2488                  * Right now, be pretty conservative: if there's a
2489                  * callback or a pending call, skip it.
2490                  * However, if the prior attempt to obtain status
2491                  * was refused access or the volume is .readonly,
2492                  * take the data in any case since we have nothing
2493                  * better for the in flight directory enumeration that
2494                  * resulted in this function being called.
2495                  */
2496                 lock_ObtainRead(&scp->rw);
2497                 if ((scp->cbServerp == NULL &&
2498                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2499                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
2500                      (scp->flags & CM_SCACHEFLAG_EACCESS))
2501                 {
2502                     lock_ConvertRToW(&scp->rw);
2503                     cm_EndCallbackGrantingCall(scp, &cbReq,
2504                                                &bbp->callbacks[j],
2505                                                &volSync,
2506                                                CM_CALLBACK_MAINTAINCOUNT);
2507                     cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, 0);
2508                     lock_ReleaseWrite(&scp->rw);
2509                 } else {
2510                     lock_ReleaseRead(&scp->rw);
2511                 }
2512                 cm_ReleaseSCache(scp);
2513             }
2514         } /* all files in the response */
2515         /* now tell it to drop the count,
2516          * after doing the vnode processing above */
2517         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2518     }   /* while there are still more files to process */
2519
2520     return code;
2521 }
2522
2523 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2524  * calls on all undeleted files in the page of the directory specified.
2525  */
2526 afs_int32
2527 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2528                cm_req_t *reqp)
2529 {
2530     afs_int32 code;
2531     cm_bulkStat_t *bbp;
2532
2533     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2534
2535     /* should be on a buffer boundary */
2536     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2537
2538     bbp = malloc(sizeof(cm_bulkStat_t));
2539     memset(bbp, 0, sizeof(cm_bulkStat_t));
2540     bbp->bufOffset = *offsetp;
2541
2542     lock_ReleaseWrite(&dscp->rw);
2543     /* first, assemble the file IDs we need to stat */
2544     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2545
2546     /* if we failed, bail out early */
2547     if (code && code != CM_ERROR_STOPNOW) {
2548         free(bbp);
2549         lock_ObtainWrite(&dscp->rw);
2550         return code;
2551     }
2552
2553     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2554     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2555
2556     lock_ObtainWrite(&dscp->rw);
2557     free(bbp);
2558     return 0;
2559 }
2560
2561 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2562 {
2563     long mask;
2564
2565     /* initialize store back mask as inexpensive local variable */
2566     mask = 0;
2567     memset(statusp, 0, sizeof(AFSStoreStatus));
2568
2569     /* copy out queued info from scache first, if scp passed in */
2570     if (scp) {
2571         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2572             statusp->ClientModTime = scp->clientModTime;
2573             mask |= AFS_SETMODTIME;
2574             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2575         }
2576     }
2577
2578     if (attrp) {
2579         /* now add in our locally generated request */
2580         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2581             statusp->ClientModTime = attrp->clientModTime;
2582             mask |= AFS_SETMODTIME;
2583         }
2584         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2585             statusp->UnixModeBits = attrp->unixModeBits;
2586             mask |= AFS_SETMODE;
2587         }
2588         if (attrp->mask & CM_ATTRMASK_OWNER) {
2589             statusp->Owner = attrp->owner;
2590             mask |= AFS_SETOWNER;
2591         }
2592         if (attrp->mask & CM_ATTRMASK_GROUP) {
2593             statusp->Group = attrp->group;
2594             mask |= AFS_SETGROUP;
2595         }
2596     }
2597     statusp->Mask = mask;
2598 }
2599
2600 /* set the file size, and make sure that all relevant buffers have been
2601  * truncated.  Ensure that any partially truncated buffers have been zeroed
2602  * to the end of the buffer.
2603  */
2604 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2605                    cm_req_t *reqp)
2606 {
2607     long code;
2608     int shrinking;
2609
2610     /* start by locking out buffer creation */
2611     lock_ObtainWrite(&scp->bufCreateLock);
2612
2613     /* verify that this is a file, not a dir or a symlink */
2614     lock_ObtainWrite(&scp->rw);
2615     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2616                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2617     if (code)
2618         goto done;
2619     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2620
2621     if (scp->fileType != CM_SCACHETYPE_FILE) {
2622         code = CM_ERROR_ISDIR;
2623         goto done;
2624     }
2625
2626   startover:
2627     if (LargeIntegerLessThan(*sizep, scp->length))
2628         shrinking = 1;
2629     else
2630         shrinking = 0;
2631
2632     lock_ReleaseWrite(&scp->rw);
2633
2634     /* can't hold scp->rw lock here, since we may wait for a storeback to
2635      * finish if the buffer package is cleaning a buffer by storing it to
2636      * the server.
2637      */
2638     if (shrinking)
2639         buf_Truncate(scp, userp, reqp, sizep);
2640
2641     /* now ensure that file length is short enough, and update truncPos */
2642     lock_ObtainWrite(&scp->rw);
2643
2644     /* make sure we have a callback (so we have the right value for the
2645      * length), and wait for it to be safe to do a truncate.
2646      */
2647     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2648                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2649                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2650
2651     /* If we only have 'i' bits, then we should still be able to set
2652        the size of a file we created. */
2653     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2654         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2655                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2656                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2657     }
2658
2659     if (code)
2660         goto done;
2661
2662     if (LargeIntegerLessThan(*sizep, scp->length)) {
2663         /* a real truncation.  If truncPos is not set yet, or is bigger
2664          * than where we're truncating the file, set truncPos to this
2665          * new value.
2666          */
2667         if (!shrinking)
2668             goto startover;
2669         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2670              || LargeIntegerLessThan(*sizep, scp->length)) {
2671             /* set trunc pos */
2672             scp->truncPos = *sizep;
2673             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2674         }
2675         /* in either case, the new file size has been changed */
2676         scp->length = *sizep;
2677         scp->mask |= CM_SCACHEMASK_LENGTH;
2678     }
2679     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2680         /* really extending the file */
2681         scp->length = *sizep;
2682         scp->mask |= CM_SCACHEMASK_LENGTH;
2683     }
2684
2685     /* done successfully */
2686     code = 0;
2687
2688     cm_SyncOpDone(scp, NULL,
2689                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2690                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2691
2692   done:
2693     lock_ReleaseWrite(&scp->rw);
2694     lock_ReleaseWrite(&scp->bufCreateLock);
2695
2696     return code;
2697 }
2698
2699 /* set the file size or other attributes (but not both at once) */
2700 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2701                 cm_req_t *reqp)
2702 {
2703     long code;
2704     AFSFetchStatus afsOutStatus;
2705     AFSVolSync volSync;
2706     cm_conn_t *connp;
2707     AFSFid tfid;
2708     AFSStoreStatus afsInStatus;
2709     struct rx_connection * rxconnp;
2710
2711     memset(&volSync, 0, sizeof(volSync));
2712
2713     /* handle file length setting */
2714     if (attrp->mask & CM_ATTRMASK_LENGTH)
2715         return cm_SetLength(scp, &attrp->length, userp, reqp);
2716
2717     lock_ObtainWrite(&scp->rw);
2718     /* Check for RO volume */
2719     if (scp->flags & CM_SCACHEFLAG_RO) {
2720         code = CM_ERROR_READONLY;
2721         lock_ReleaseWrite(&scp->rw);
2722         return code;
2723     }
2724
2725     /* otherwise, we have to make an RPC to get the status */
2726     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2727     if (code) {
2728         lock_ReleaseWrite(&scp->rw);
2729         return code;
2730     }
2731     lock_ConvertWToR(&scp->rw);
2732
2733     /* make the attr structure */
2734     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2735
2736     tfid.Volume = scp->fid.volume;
2737     tfid.Vnode = scp->fid.vnode;
2738     tfid.Unique = scp->fid.unique;
2739     lock_ReleaseRead(&scp->rw);
2740
2741     /* now make the RPC */
2742     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2743     do {
2744         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2745         if (code)
2746             continue;
2747
2748         rxconnp = cm_GetRxConn(connp);
2749         code = RXAFS_StoreStatus(rxconnp, &tfid,
2750                                   &afsInStatus, &afsOutStatus, &volSync);
2751         rx_PutConnection(rxconnp);
2752
2753     } while (cm_Analyze(connp, userp, reqp,
2754                          &scp->fid, &volSync, NULL, NULL, code));
2755     code = cm_MapRPCError(code, reqp);
2756
2757     if (code)
2758         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2759     else
2760         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2761
2762     lock_ObtainWrite(&scp->rw);
2763     if (code == 0)
2764         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2765                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2766     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2767
2768     /* if we're changing the mode bits, discard the ACL cache,
2769      * since we changed the mode bits.
2770      */
2771     if (afsInStatus.Mask & AFS_SETMODE)
2772         cm_FreeAllACLEnts(scp);
2773     lock_ReleaseWrite(&scp->rw);
2774     return code;
2775 }
2776
2777 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2778                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2779 {
2780     cm_conn_t *connp;
2781     long code;
2782     AFSFid dirAFSFid;
2783     cm_callbackRequest_t cbReq;
2784     AFSFid newAFSFid;
2785     cm_fid_t newFid;
2786     cm_scache_t *scp = NULL;
2787     int didEnd;
2788     AFSStoreStatus inStatus;
2789     AFSFetchStatus updatedDirStatus;
2790     AFSFetchStatus newFileStatus;
2791     AFSCallBack newFileCallback;
2792     AFSVolSync volSync;
2793     struct rx_connection * rxconnp;
2794     cm_dirOp_t dirop;
2795     fschar_t * fnamep = NULL;
2796
2797     memset(&volSync, 0, sizeof(volSync));
2798
2799     /* can't create names with @sys in them; must expand it manually first.
2800      * return "invalid request" if they try.
2801      */
2802     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2803         return CM_ERROR_ATSYS;
2804     }
2805
2806 #ifdef AFS_FREELANCE_CLIENT
2807     /* Freelance root volume does not hold files */
2808     if (cm_freelanceEnabled &&
2809         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2810         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2811     {
2812         return CM_ERROR_NOACCESS;
2813     }
2814 #endif /* AFS_FREELANCE_CLIENT */
2815
2816     /* Check for RO volume */
2817     if (dscp->flags & CM_SCACHEFLAG_RO)
2818         return CM_ERROR_READONLY;
2819
2820     /* before starting the RPC, mark that we're changing the file data, so
2821      * that someone who does a chmod will know to wait until our call
2822      * completes.
2823      */
2824     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2825                   &dirop);
2826     lock_ObtainWrite(&dscp->rw);
2827     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2828     lock_ReleaseWrite(&dscp->rw);
2829     if (code == 0) {
2830         cm_StartCallbackGrantingCall(NULL, &cbReq);
2831     } else {
2832         cm_EndDirOp(&dirop);
2833     }
2834     if (code) {
2835         return code;
2836     }
2837     didEnd = 0;
2838
2839     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2840
2841     cm_StatusFromAttr(&inStatus, NULL, attrp);
2842
2843     /* try the RPC now */
2844     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2845     do {
2846         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2847         if (code)
2848             continue;
2849
2850         dirAFSFid.Volume = dscp->fid.volume;
2851         dirAFSFid.Vnode = dscp->fid.vnode;
2852         dirAFSFid.Unique = dscp->fid.unique;
2853
2854         rxconnp = cm_GetRxConn(connp);
2855         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2856                                  &inStatus, &newAFSFid, &newFileStatus,
2857                                  &updatedDirStatus, &newFileCallback,
2858                                  &volSync);
2859         rx_PutConnection(rxconnp);
2860
2861     } while (cm_Analyze(connp, userp, reqp,
2862                          &dscp->fid, &volSync, NULL, &cbReq, code));
2863     code = cm_MapRPCError(code, reqp);
2864
2865     if (code)
2866         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
2867     else
2868         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
2869
2870     if (dirop.scp) {
2871         lock_ObtainWrite(&dirop.scp->dirlock);
2872         dirop.lockType = CM_DIRLOCK_WRITE;
2873     }
2874     lock_ObtainWrite(&dscp->rw);
2875     if (code == 0)
2876         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
2877     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
2878     lock_ReleaseWrite(&dscp->rw);
2879
2880     /* now try to create the file's entry, too, but be careful to
2881      * make sure that we don't merge in old info.  Since we weren't locking
2882      * out any requests during the file's creation, we may have pretty old
2883      * info.
2884      */
2885     if (code == 0) {
2886         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
2887         code = cm_GetSCache(&newFid, &scp, userp, reqp);
2888         if (code == 0) {
2889             lock_ObtainWrite(&scp->rw);
2890             scp->creator = userp;               /* remember who created it */
2891             if (!cm_HaveCallback(scp)) {
2892                 cm_EndCallbackGrantingCall(scp, &cbReq,
2893                                            &newFileCallback, &volSync, 0);
2894                 cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
2895                                userp, reqp, 0);
2896                 didEnd = 1;
2897             }
2898             lock_ReleaseWrite(&scp->rw);
2899         }
2900     }
2901
2902     /* make sure we end things properly */
2903     if (!didEnd)
2904         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2905
2906     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
2907         cm_DirCreateEntry(&dirop, fnamep, &newFid);
2908 #ifdef USE_BPLUS
2909         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
2910 #endif
2911     }
2912     cm_EndDirOp(&dirop);
2913
2914     if (fnamep)
2915         free(fnamep);
2916
2917     if (scp) {
2918         if (scpp)
2919             *scpp = scp;
2920         else
2921             cm_ReleaseSCache(scp);
2922     }
2923     return code;
2924 }
2925
2926 /*
2927  * locked if TRUE means write-locked
2928  * else the cm_scache_t rw must not be held
2929  */
2930 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
2931 {
2932     long code;
2933
2934     if (locked)
2935         lock_ReleaseWrite(&scp->rw);
2936
2937     osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
2938
2939     code = buf_CleanVnode(scp, userp, reqp);
2940     if (code == 0) {
2941         lock_ObtainWrite(&scp->rw);
2942
2943         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
2944                           | CM_SCACHEMASK_CLIENTMODTIME
2945                           | CM_SCACHEMASK_LENGTH))
2946             code = cm_StoreMini(scp, userp, reqp);
2947
2948         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
2949             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
2950             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
2951         }
2952
2953         if (!locked)
2954             lock_ReleaseWrite(&scp->rw);
2955     } else if (locked) {
2956         lock_ObtainWrite(&scp->rw);
2957     }
2958     return code;
2959 }
2960
2961 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2962                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
2963 {
2964     cm_conn_t *connp;
2965     long code;
2966     AFSFid dirAFSFid;
2967     cm_callbackRequest_t cbReq;
2968     AFSFid newAFSFid;
2969     cm_fid_t newFid;
2970     cm_scache_t *scp = NULL;
2971     int didEnd;
2972     AFSStoreStatus inStatus;
2973     AFSFetchStatus updatedDirStatus;
2974     AFSFetchStatus newDirStatus;
2975     AFSCallBack newDirCallback;
2976     AFSVolSync volSync;
2977     struct rx_connection * rxconnp;
2978     cm_dirOp_t dirop;
2979     fschar_t * fnamep = NULL;
2980
2981     memset(&volSync, 0, sizeof(volSync));
2982
2983     /* can't create names with @sys in them; must expand it manually first.
2984      * return "invalid request" if they try.
2985      */
2986     if (cm_ExpandSysName(cnamep, NULL, 0, 0)) {
2987         return CM_ERROR_ATSYS;
2988     }
2989
2990 #ifdef AFS_FREELANCE_CLIENT
2991     /* Freelance root volume does not hold subdirectories */
2992     if (cm_freelanceEnabled &&
2993         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2994         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2995     {
2996         return CM_ERROR_NOACCESS;
2997     }
2998 #endif /* AFS_FREELANCE_CLIENT */
2999
3000     /* Check for RO volume */
3001     if (dscp->flags & CM_SCACHEFLAG_RO)
3002         return CM_ERROR_READONLY;
3003
3004     /* before starting the RPC, mark that we're changing the directory
3005      * data, so that someone who does a chmod on the dir will wait until
3006      * our call completes.
3007      */
3008     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3009                   &dirop);
3010     lock_ObtainWrite(&dscp->rw);
3011     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3012     lock_ReleaseWrite(&dscp->rw);
3013     if (code == 0) {
3014         cm_StartCallbackGrantingCall(NULL, &cbReq);
3015     } else {
3016         cm_EndDirOp(&dirop);
3017     }
3018     if (code) {
3019         return code;
3020     }
3021     didEnd = 0;
3022
3023     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3024     cm_StatusFromAttr(&inStatus, NULL, attrp);
3025
3026     /* try the RPC now */
3027     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3028     do {
3029         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3030         if (code)
3031             continue;
3032
3033         dirAFSFid.Volume = dscp->fid.volume;
3034         dirAFSFid.Vnode = dscp->fid.vnode;
3035         dirAFSFid.Unique = dscp->fid.unique;
3036
3037         rxconnp = cm_GetRxConn(connp);
3038         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3039                               &inStatus, &newAFSFid, &newDirStatus,
3040                               &updatedDirStatus, &newDirCallback,
3041                               &volSync);
3042         rx_PutConnection(rxconnp);
3043
3044     } while (cm_Analyze(connp, userp, reqp,
3045                         &dscp->fid, &volSync, NULL, &cbReq, code));
3046     code = cm_MapRPCError(code, reqp);
3047
3048     if (code)
3049         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3050     else
3051         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3052
3053     if (dirop.scp) {
3054         lock_ObtainWrite(&dirop.scp->dirlock);
3055         dirop.lockType = CM_DIRLOCK_WRITE;
3056     }
3057     lock_ObtainWrite(&dscp->rw);
3058     if (code == 0)
3059         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3060     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3061     lock_ReleaseWrite(&dscp->rw);
3062
3063     /* now try to create the new dir's entry, too, but be careful to
3064      * make sure that we don't merge in old info.  Since we weren't locking
3065      * out any requests during the file's creation, we may have pretty old
3066      * info.
3067      */
3068     if (code == 0) {
3069         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3070         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3071         if (code == 0) {
3072             lock_ObtainWrite(&scp->rw);
3073             if (!cm_HaveCallback(scp)) {
3074                 cm_EndCallbackGrantingCall(scp, &cbReq,
3075                                             &newDirCallback, &volSync, 0);
3076                 cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3077                                 userp, reqp, 0);
3078                 didEnd = 1;
3079             }
3080             lock_ReleaseWrite(&scp->rw);
3081         }
3082     }
3083
3084     /* make sure we end things properly */
3085     if (!didEnd)
3086         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3087
3088     if (scp && cm_CheckDirOpForSingleChange(&dirop)) {
3089         cm_DirCreateEntry(&dirop, fnamep, &newFid);
3090 #ifdef USE_BPLUS
3091         cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3092 #endif
3093     }
3094     cm_EndDirOp(&dirop);
3095
3096     free(fnamep);
3097
3098     if (scp) {
3099         if (scpp)
3100             *scpp = scp;
3101         else
3102             cm_ReleaseSCache(scp);
3103     }
3104
3105     /* and return error code */
3106     return code;
3107 }
3108
3109 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3110              cm_user_t *userp, cm_req_t *reqp)
3111 {
3112     cm_conn_t *connp;
3113     long code = 0;
3114     AFSFid dirAFSFid;
3115     AFSFid existingAFSFid;
3116     AFSFetchStatus updatedDirStatus;
3117     AFSFetchStatus newLinkStatus;
3118     AFSVolSync volSync;
3119     struct rx_connection * rxconnp;
3120     cm_dirOp_t dirop;
3121     fschar_t * fnamep = NULL;
3122
3123     memset(&volSync, 0, sizeof(volSync));
3124
3125     if (dscp->fid.cell != sscp->fid.cell ||
3126         dscp->fid.volume != sscp->fid.volume) {
3127         return CM_ERROR_CROSSDEVLINK;
3128     }
3129
3130     /* Check for RO volume */
3131     if (dscp->flags & CM_SCACHEFLAG_RO)
3132         return CM_ERROR_READONLY;
3133
3134     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3135                   &dirop);
3136     lock_ObtainWrite(&dscp->rw);
3137     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3138     lock_ReleaseWrite(&dscp->rw);
3139     if (code != 0)
3140         cm_EndDirOp(&dirop);
3141
3142     if (code)
3143         return code;
3144
3145     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3146
3147     /* try the RPC now */
3148     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3149     do {
3150         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3151         if (code) continue;
3152
3153         dirAFSFid.Volume = dscp->fid.volume;
3154         dirAFSFid.Vnode = dscp->fid.vnode;
3155         dirAFSFid.Unique = dscp->fid.unique;
3156
3157         existingAFSFid.Volume = sscp->fid.volume;
3158         existingAFSFid.Vnode = sscp->fid.vnode;
3159         existingAFSFid.Unique = sscp->fid.unique;
3160
3161         rxconnp = cm_GetRxConn(connp);
3162         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3163             &newLinkStatus, &updatedDirStatus, &volSync);
3164         rx_PutConnection(rxconnp);
3165         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3166
3167     } while (cm_Analyze(connp, userp, reqp,
3168         &dscp->fid, &volSync, NULL, NULL, code));
3169
3170     code = cm_MapRPCError(code, reqp);
3171
3172     if (code)
3173         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3174     else
3175         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3176
3177     if (dirop.scp) {
3178         lock_ObtainWrite(&dirop.scp->dirlock);
3179         dirop.lockType = CM_DIRLOCK_WRITE;
3180     }
3181     lock_ObtainWrite(&dscp->rw);
3182     if (code == 0) {
3183         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3184     }
3185     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3186     lock_ReleaseWrite(&dscp->rw);
3187
3188     if (code == 0) {
3189         if (cm_CheckDirOpForSingleChange(&dirop)) {
3190             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3191 #ifdef USE_BPLUS
3192             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3193 #endif
3194         }
3195     }
3196     cm_EndDirOp(&dirop);
3197
3198     /* Update the linked object status */
3199     if (code == 0) {
3200         lock_ObtainWrite(&sscp->rw);
3201         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3202         lock_ReleaseWrite(&sscp->rw);
3203     }
3204
3205     free(fnamep);
3206
3207     return code;
3208 }
3209
3210 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3211                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp)
3212 {
3213     cm_conn_t *connp;
3214     long code;
3215     AFSFid dirAFSFid;
3216     AFSFid newAFSFid;
3217     cm_fid_t newFid;
3218     cm_scache_t *scp;
3219     AFSStoreStatus inStatus;
3220     AFSFetchStatus updatedDirStatus;
3221     AFSFetchStatus newLinkStatus;
3222     AFSVolSync volSync;
3223     struct rx_connection * rxconnp;
3224     cm_dirOp_t dirop;
3225     fschar_t *fnamep = NULL;
3226
3227     /* Check for RO volume */
3228     if (dscp->flags & CM_SCACHEFLAG_RO)
3229         return CM_ERROR_READONLY;
3230
3231     memset(&volSync, 0, sizeof(volSync));
3232
3233     /* before starting the RPC, mark that we're changing the directory data,
3234      * so that someone who does a chmod on the dir will wait until our
3235      * call completes.
3236      */
3237     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3238                   &dirop);
3239     lock_ObtainWrite(&dscp->rw);
3240     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3241     lock_ReleaseWrite(&dscp->rw);
3242     if (code != 0)
3243         cm_EndDirOp(&dirop);
3244     if (code) {
3245         return code;
3246     }
3247
3248     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3249
3250     cm_StatusFromAttr(&inStatus, NULL, attrp);
3251
3252     /* try the RPC now */
3253     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3254     do {
3255         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3256         if (code)
3257             continue;
3258
3259         dirAFSFid.Volume = dscp->fid.volume;
3260         dirAFSFid.Vnode = dscp->fid.vnode;
3261         dirAFSFid.Unique = dscp->fid.unique;
3262
3263         rxconnp = cm_GetRxConn(connp);
3264         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3265                               &inStatus, &newAFSFid, &newLinkStatus,
3266                               &updatedDirStatus, &volSync);
3267         rx_PutConnection(rxconnp);
3268
3269     } while (cm_Analyze(connp, userp, reqp,
3270                          &dscp->fid, &volSync, NULL, NULL, code));
3271     code = cm_MapRPCError(code, reqp);
3272
3273     if (code)
3274         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3275     else
3276         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3277
3278     if (dirop.scp) {
3279         lock_ObtainWrite(&dirop.scp->dirlock);
3280         dirop.lockType = CM_DIRLOCK_WRITE;
3281     }
3282     lock_ObtainWrite(&dscp->rw);
3283     if (code == 0)
3284         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3285     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3286     lock_ReleaseWrite(&dscp->rw);
3287
3288     if (code == 0) {
3289         if (cm_CheckDirOpForSingleChange(&dirop)) {
3290             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3291
3292             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3293 #ifdef USE_BPLUS
3294             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3295 #endif
3296         }
3297     }
3298     cm_EndDirOp(&dirop);
3299
3300     /* now try to create the new dir's entry, too, but be careful to
3301      * make sure that we don't merge in old info.  Since we weren't locking
3302      * out any requests during the file's creation, we may have pretty old
3303      * info.
3304      */
3305     if (code == 0) {
3306         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3307         code = cm_GetSCache(&newFid, &scp, userp, reqp);
3308         if (code == 0) {
3309             lock_ObtainWrite(&scp->rw);
3310             if (!cm_HaveCallback(scp)) {
3311                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3312                                 userp, reqp, 0);
3313             }
3314             lock_ReleaseWrite(&scp->rw);
3315             cm_ReleaseSCache(scp);
3316         }
3317     }
3318
3319     free(fnamep);
3320
3321     /* and return error code */
3322     return code;
3323 }
3324
3325 /*! \brief Remove a directory
3326
3327   Encapsulates a call to RXAFS_RemoveDir().
3328
3329   \param[in] dscp cm_scache_t for the directory containing the
3330       directory to be removed.
3331
3332   \param[in] fnamep This will be the original name of the directory
3333       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3334       This parameter is optional.  If it is not provided the value
3335       will be looked up.
3336
3337   \param[in] cnamep Normalized name used to update the local
3338       directory caches.
3339
3340   \param[in] userp cm_user_t for the request.
3341
3342   \param[in] reqp Request tracker.
3343 */
3344 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3345 {
3346     cm_conn_t *connp;
3347     long code;
3348     AFSFid dirAFSFid;
3349     int didEnd;
3350     AFSFetchStatus updatedDirStatus;
3351     AFSVolSync volSync;
3352     struct rx_connection * rxconnp;
3353     cm_dirOp_t dirop;
3354     cm_scache_t *scp = NULL;
3355     int free_fnamep = FALSE;
3356
3357     memset(&volSync, 0, sizeof(volSync));
3358
3359     if (fnamep == NULL) {
3360         code = -1;
3361 #ifdef USE_BPLUS
3362         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3363                              CM_DIROP_FLAG_NONE, &dirop);
3364         if (code == 0) {
3365             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3366             if (code == 0)
3367                 free_fnamep = TRUE;
3368             cm_EndDirOp(&dirop);
3369         }
3370 #endif
3371         if (code)
3372             goto done;
3373     }
3374
3375     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3376     if (code)
3377         goto done;
3378
3379     /* Check for RO volume */
3380     if (dscp->flags & CM_SCACHEFLAG_RO) {
3381         code = CM_ERROR_READONLY;
3382         goto done;
3383     }
3384
3385     /* before starting the RPC, mark that we're changing the directory data,
3386      * so that someone who does a chmod on the dir will wait until our
3387      * call completes.
3388      */
3389     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3390                   &dirop);
3391     lock_ObtainWrite(&dscp->rw);
3392     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3393     lock_ReleaseWrite(&dscp->rw);
3394     if (code) {
3395         cm_EndDirOp(&dirop);
3396         goto done;
3397     }
3398     didEnd = 0;
3399
3400     /* try the RPC now */
3401     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3402     do {
3403         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3404         if (code)
3405             continue;
3406
3407         dirAFSFid.Volume = dscp->fid.volume;
3408         dirAFSFid.Vnode = dscp->fid.vnode;
3409         dirAFSFid.Unique = dscp->fid.unique;
3410
3411         rxconnp = cm_GetRxConn(connp);
3412         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3413                                &updatedDirStatus, &volSync);
3414         rx_PutConnection(rxconnp);
3415
3416     } while (cm_Analyze(connp, userp, reqp,
3417                         &dscp->fid, &volSync, NULL, NULL, code));
3418     code = cm_MapRPCErrorRmdir(code, reqp);
3419
3420     if (code)
3421         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3422     else
3423         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3424
3425     if (dirop.scp) {
3426         lock_ObtainWrite(&dirop.scp->dirlock);
3427         dirop.lockType = CM_DIRLOCK_WRITE;
3428     }
3429     lock_ObtainWrite(&dscp->rw);
3430     if (code == 0) {
3431         cm_dnlcRemove(dscp, cnamep);
3432         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3433     }
3434     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3435     lock_ReleaseWrite(&dscp->rw);
3436
3437     if (code == 0) {
3438         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3439             cm_DirDeleteEntry(&dirop, fnamep);
3440 #ifdef USE_BPLUS
3441             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3442 #endif
3443         }
3444     }
3445     cm_EndDirOp(&dirop);
3446
3447     if (scp) {
3448         cm_ReleaseSCache(scp);
3449         if (code == 0) {
3450             lock_ObtainWrite(&scp->rw);
3451             scp->flags |= CM_SCACHEFLAG_DELETED;
3452             lock_ReleaseWrite(&scp->rw);
3453         }
3454     }
3455
3456   done:
3457     if (free_fnamep)
3458         free(fnamep);
3459
3460     /* and return error code */
3461     return code;
3462 }
3463
3464 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3465 {
3466     /* grab mutex on contents */
3467     lock_ObtainWrite(&scp->rw);
3468
3469     /* reset the prefetch info */
3470     scp->prefetch.base.LowPart = 0;             /* base */
3471     scp->prefetch.base.HighPart = 0;
3472     scp->prefetch.end.LowPart = 0;              /* and end */
3473     scp->prefetch.end.HighPart = 0;
3474
3475     /* release mutex on contents */
3476     lock_ReleaseWrite(&scp->rw);
3477
3478     /* we're done */
3479     return 0;
3480 }
3481
3482 /*! \brief Rename a file or directory
3483
3484   Encapsulates a RXAFS_Rename() call.
3485
3486   \param[in] oldDscp cm_scache_t for the directory containing the old
3487       name.
3488
3489   \param[in] oldNamep The original old name known to the file server.
3490       This is the name that will be passed into the RXAFS_Rename().
3491       If it is not provided, it will be looked up.
3492
3493   \param[in] normalizedOldNamep Normalized old name.  This is used for
3494   updating local directory caches.
3495
3496   \param[in] newDscp cm_scache_t for the directory containing the new
3497   name.
3498
3499   \param[in] newNamep New name. Normalized.
3500
3501   \param[in] userp cm_user_t for the request.
3502
3503   \param[in,out] reqp Request tracker.
3504
3505 */
3506 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3507                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3508                cm_req_t *reqp)
3509 {
3510     cm_conn_t *connp;
3511     long code;
3512     AFSFid oldDirAFSFid;
3513     AFSFid newDirAFSFid;
3514     int didEnd;
3515     AFSFetchStatus updatedOldDirStatus;
3516     AFSFetchStatus updatedNewDirStatus;
3517     AFSVolSync volSync;
3518     int oneDir;
3519     struct rx_connection * rxconnp;
3520     cm_dirOp_t oldDirOp;
3521     cm_fid_t   fileFid;
3522     int        diropCode = -1;
3523     cm_dirOp_t newDirOp;
3524     fschar_t * newNamep = NULL;
3525     int free_oldNamep = FALSE;
3526     cm_scache_t *oldScp = NULL, *newScp = NULL;
3527
3528     memset(&volSync, 0, sizeof(volSync));
3529
3530     if (cOldNamep == NULL || cNewNamep == NULL ||
3531         cm_ClientStrLen(cOldNamep) == 0 ||
3532         cm_ClientStrLen(cNewNamep) == 0)
3533         return CM_ERROR_INVAL;
3534
3535     /*
3536      * Before we permit the operation, make sure that we do not already have
3537      * an object in the destination directory that has a case-insensitive match
3538      * for this name UNLESS the matching object is the object we are renaming.
3539      */
3540     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3541     if (code) {
3542         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3543                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3544         goto done;
3545     }
3546
3547     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3548     if (code == 0) {
3549         /* found a matching object with the new name */
3550         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3551             /* and they don't match so return an error */
3552             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3553                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3554             code = CM_ERROR_EXISTS;
3555         }
3556         cm_ReleaseSCache(newScp);
3557         newScp = NULL;
3558     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3559         code = CM_ERROR_EXISTS;
3560     } else {
3561         code = 0;
3562     }
3563
3564     /* Check for RO volume */
3565     if (code == 0 &&
3566         (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3567         code = CM_ERROR_READONLY;
3568     }
3569
3570     if (code)
3571         goto done;
3572
3573     if (oldNamep == NULL) {
3574         code = -1;
3575 #ifdef USE_BPLUS
3576         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3577                              CM_DIROP_FLAG_NONE, &oldDirOp);
3578         if (code == 0) {
3579             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3580             if (code == 0)
3581                 free_oldNamep = TRUE;
3582             cm_EndDirOp(&oldDirOp);
3583         }
3584 #endif
3585         if (code) {
3586             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3587                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3588             goto done;
3589         }
3590     }
3591
3592
3593     /* before starting the RPC, mark that we're changing the directory data,
3594      * so that someone who does a chmod on the dir will wait until our call
3595      * completes.  We do this in vnode order so that we don't deadlock,
3596      * which makes the code a little verbose.
3597      */
3598     if (oldDscp == newDscp) {
3599         /* check for identical names */
3600         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3601             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3602                       oldDscp, newDscp);
3603             code = CM_ERROR_RENAME_IDENTICAL;
3604             goto done;
3605         }
3606
3607         oneDir = 1;
3608         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3609                       CM_DIROP_FLAG_NONE, &oldDirOp);
3610         lock_ObtainWrite(&oldDscp->rw);
3611         cm_dnlcRemove(oldDscp, cOldNamep);
3612         cm_dnlcRemove(oldDscp, cNewNamep);
3613         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3614                           CM_SCACHESYNC_STOREDATA);
3615         lock_ReleaseWrite(&oldDscp->rw);
3616         if (code != 0) {
3617             cm_EndDirOp(&oldDirOp);
3618         }
3619     }
3620     else {
3621         /* two distinct dir vnodes */
3622         oneDir = 0;
3623         if (oldDscp->fid.cell != newDscp->fid.cell ||
3624              oldDscp->fid.volume != newDscp->fid.volume) {
3625             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3626                       oldDscp, newDscp);
3627             code = CM_ERROR_CROSSDEVLINK;
3628             goto done;
3629         }
3630
3631         /* shouldn't happen that we have distinct vnodes for two
3632          * different files, but could due to deliberate attack, or
3633          * stale info.  Avoid deadlocks and quit now.
3634          */
3635         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3636             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3637                       oldDscp, newDscp);
3638             code = CM_ERROR_CROSSDEVLINK;
3639             goto done;
3640         }
3641
3642         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3643             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3644                           CM_DIROP_FLAG_NONE, &oldDirOp);
3645             lock_ObtainWrite(&oldDscp->rw);
3646             cm_dnlcRemove(oldDscp, cOldNamep);
3647             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3648                              CM_SCACHESYNC_STOREDATA);
3649             lock_ReleaseWrite(&oldDscp->rw);
3650             if (code != 0)
3651                 cm_EndDirOp(&oldDirOp);
3652             if (code == 0) {
3653                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3654                               CM_DIROP_FLAG_NONE, &newDirOp);
3655                 lock_ObtainWrite(&newDscp->rw);
3656                 cm_dnlcRemove(newDscp, cNewNamep);
3657                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3658                                  CM_SCACHESYNC_STOREDATA);
3659                 lock_ReleaseWrite(&newDscp->rw);
3660                 if (code) {
3661                     cm_EndDirOp(&newDirOp);
3662
3663                     /* cleanup first one */
3664                     lock_ObtainWrite(&oldDscp->rw);
3665                     cm_SyncOpDone(oldDscp, NULL,
3666                                    CM_SCACHESYNC_STOREDATA);
3667                     lock_ReleaseWrite(&oldDscp->rw);
3668                     cm_EndDirOp(&oldDirOp);
3669                 }
3670             }
3671         }
3672         else {
3673             /* lock the new vnode entry first */
3674             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3675                           CM_DIROP_FLAG_NONE, &newDirOp);
3676             lock_ObtainWrite(&newDscp->rw);
3677             cm_dnlcRemove(newDscp, cNewNamep);
3678             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3679                               CM_SCACHESYNC_STOREDATA);
3680             lock_ReleaseWrite(&newDscp->rw);
3681             if (code != 0)
3682                 cm_EndDirOp(&newDirOp);
3683             if (code == 0) {
3684                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3685                               CM_DIROP_FLAG_NONE, &oldDirOp);
3686                 lock_ObtainWrite(&oldDscp->rw);
3687                 cm_dnlcRemove(oldDscp, cOldNamep);
3688                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3689                                   CM_SCACHESYNC_STOREDATA);
3690                 lock_ReleaseWrite(&oldDscp->rw);
3691                 if (code != 0)
3692                     cm_EndDirOp(&oldDirOp);
3693                 if (code) {
3694                     /* cleanup first one */
3695                     lock_ObtainWrite(&newDscp->rw);
3696                     cm_SyncOpDone(newDscp, NULL,
3697                                    CM_SCACHESYNC_STOREDATA);
3698                     lock_ReleaseWrite(&newDscp->rw);
3699                     cm_EndDirOp(&newDirOp);
3700                 }
3701             }
3702         }
3703     }   /* two distinct vnodes */
3704
3705     if (code)
3706         goto done;
3707
3708     didEnd = 0;
3709
3710     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3711
3712     /* try the RPC now */
3713     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3714               oldDscp, newDscp);
3715     do {
3716         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3717         if (code)
3718             continue;
3719
3720         oldDirAFSFid.Volume = oldDscp->fid.volume;
3721         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3722         oldDirAFSFid.Unique = oldDscp->fid.unique;
3723         newDirAFSFid.Volume = newDscp->fid.volume;
3724         newDirAFSFid.Vnode = newDscp->fid.vnode;
3725         newDirAFSFid.Unique = newDscp->fid.unique;
3726
3727         rxconnp = cm_GetRxConn(connp);
3728         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3729                             &newDirAFSFid, newNamep,
3730                             &updatedOldDirStatus, &updatedNewDirStatus,
3731                             &volSync);
3732         rx_PutConnection(rxconnp);
3733
3734     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3735                          &volSync, NULL, NULL, code));
3736     code = cm_MapRPCError(code, reqp);
3737
3738     if (code)
3739         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3740     else
3741         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3742
3743     /* update the individual stat cache entries for the directories */
3744     if (oldDirOp.scp) {
3745         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3746         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3747     }
3748     lock_ObtainWrite(&oldDscp->rw);
3749
3750     if (code == 0)
3751         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3752                        userp, reqp, CM_MERGEFLAG_DIROP);
3753     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3754     lock_ReleaseWrite(&oldDscp->rw);
3755
3756     if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3757 #ifdef USE_BPLUS
3758         diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3759         if (diropCode == CM_ERROR_INEXACT_MATCH)
3760             diropCode = 0;
3761         else if (diropCode == EINVAL)
3762 #endif
3763             diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3764
3765         if (diropCode == 0) {
3766             if (oneDir) {
3767                 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3768 #ifdef USE_BPLUS
3769                 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3770 #endif
3771             }
3772
3773             if (diropCode == 0) {
3774                 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3775 #ifdef USE_BPLUS
3776                 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3777 #endif
3778             }
3779         }
3780     }
3781     cm_EndDirOp(&oldDirOp);
3782
3783     /* and update it for the new one, too, if necessary */
3784     if (!oneDir) {
3785         if (newDirOp.scp) {
3786             lock_ObtainWrite(&newDirOp.scp->dirlock);
3787             newDirOp.lockType = CM_DIRLOCK_WRITE;
3788         }
3789         lock_ObtainWrite(&newDscp->rw);
3790         if (code == 0)
3791             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3792                             userp, reqp, CM_MERGEFLAG_DIROP);
3793         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3794         lock_ReleaseWrite(&newDscp->rw);
3795
3796 #if 0
3797         /*
3798          * The following optimization does not work.
3799          * When the file server processed a RXAFS_Rename() request the
3800          * FID of the object being moved between directories is not
3801          * preserved.  The client does not know the new FID nor the
3802          * version number of the target.  Not only can we not create
3803          * the directory entry in the new directory, but we can't
3804          * preserve the cached data for the file.  It must be re-read
3805          * from the file server.  - jaltman, 2009/02/20
3806          */
3807         if (code == 0) {
3808             /* we only make the local change if we successfully made
3809                the change in the old directory AND there was only one
3810                change in the new directory */
3811             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3812                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3813 #ifdef USE_BPLUS
3814                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3815 #endif
3816             }
3817         }
3818 #endif /* 0 */
3819         cm_EndDirOp(&newDirOp);
3820     }
3821
3822     /*
3823      * After the rename the file server has invalidated the callbacks
3824      * on the file that was moved nor do we have a directory reference
3825      * to it anymore.
3826      */
3827     lock_ObtainWrite(&oldScp->rw);
3828     cm_DiscardSCache(oldScp);
3829     lock_ReleaseWrite(&oldScp->rw);
3830
3831   done:
3832     if (oldScp)
3833         cm_ReleaseSCache(oldScp);
3834
3835     if (free_oldNamep)
3836         free(oldNamep);
3837
3838     free(newNamep);
3839
3840     /* and return error code */
3841     return code;
3842 }
3843
3844 /* Byte range locks:
3845
3846    The OpenAFS Windows client has to fake byte range locks given no
3847    server side support for such locks.  This is implemented as keyed
3848    byte range locks on the cache manager.
3849
3850    Keyed byte range locks:
3851
3852    Each cm_scache_t structure keeps track of a list of keyed locks.
3853    The key for a lock identifies an owner of a set of locks (referred
3854    to as a client).  Each key is represented by a value.  The set of
3855    key values used within a specific cm_scache_t structure form a
3856    namespace that has a scope of just that cm_scache_t structure.  The
3857    same key value can be used with another cm_scache_t structure and
3858    correspond to a completely different client.  However it is
3859    advantageous for the SMB or IFS layer to make sure that there is a
3860    1-1 mapping between client and keys over all cm_scache_t objects.
3861
3862    Assume a client C has key Key(C) (although, since the scope of the
3863    key is a cm_scache_t, the key can be Key(C,S), where S is the
3864    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3865    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3866    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3867    through cm_generateKey() function for both SMB and IFS.
3868
3869    The list of locks for a cm_scache_t object S is maintained in
3870    S->fileLocks.  The cache manager will set a lock on the AFS file
3871    server in order to assert the locks in S->fileLocks.  If only
3872    shared locks are in place for S, then the cache manager will obtain
3873    a LockRead lock, while if there are any exclusive locks, it will
3874    obtain a LockWrite lock.  If the exclusive locks are all released
3875    while the shared locks remain, then the cache manager will
3876    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3877    exclusive lock is obtained when only shared locks exist, then the
3878    cache manager will try to upgrade the lock from LockRead to
3879    LockWrite.
3880
3881    Each lock L owned by client C maintains a key L->key such that
3882    L->key == Key(C), the effective range defined by L->LOffset and
3883    L->LLength such that the range of bytes affected by the lock is
3884    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3885    is either exclusive or shared.
3886
3887    Lock states:
3888
3889    A lock exists iff it is in S->fileLocks for some cm_scache_t
3890    S. Existing locks are in one of the following states: ACTIVE,
3891    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3892
3893    The following sections describe each lock and the associated
3894    transitions.
3895
3896    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3897       the lock with the AFS file server.  This type of lock can be
3898       exercised by a client to read or write to the locked region (as
3899       the lock allows).
3900
3901       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3902         server lock that was required to assert the lock.  Before
3903         marking the lock as lost, the cache manager checks if the file
3904         has changed on the server.  If the file has not changed, then
3905         the cache manager will attempt to obtain a new server lock
3906         that is sufficient to assert the client side locks for the
3907         file.  If any of these fail, the lock is marked as LOST.
3908         Otherwise, it is left as ACTIVE.
3909
3910       1.2 ACTIVE->DELETED: Lock is released.
3911
3912    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3913       grants the lock but the lock is yet to be asserted with the AFS
3914       file server.  Once the file server grants the lock, the state
3915       will transition to an ACTIVE lock.
3916
3917       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3918
3919       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3920         waiting.
3921
3922       2.3 WAITLOCK->LOST: One or more locks from this client were
3923         marked as LOST.  No further locks will be granted to this
3924         client until all lost locks are removed.
3925
3926    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3927       receives a request for a lock that conflicts with an existing
3928       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3929       and will be granted at such time the conflicting locks are
3930       removed, at which point the state will transition to either
3931       WAITLOCK or ACTIVE.
3932
3933       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3934         current serverLock is sufficient to assert this lock, or a
3935         sufficient serverLock is obtained.
3936
3937       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3938         however the required serverLock is yet to be asserted with the
3939         server.
3940
3941       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3942         released.
3943
3944       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3945         marked as LOST.  No further locks will be granted to this
3946         client until all lost locks are removed.
3947
3948    4. LOST: A lock L is LOST if the server lock that was required to
3949       assert the lock could not be obtained or if it could not be
3950       extended, or if other locks by the same client were LOST.
3951       Essentially, once a lock is LOST, the contract between the cache
3952       manager and that specific client is no longer valid.
3953
3954       The cache manager rechecks the server lock once every minute and
3955       extends it as appropriate.  If this is not done for 5 minutes,
3956       the AFS file server will release the lock (the 5 minute timeout
3957       is based on current file server code and is fairly arbitrary).
3958       Once released, the lock cannot be re-obtained without verifying
3959       that the contents of the file hasn't been modified since the
3960       time the lock was released.  Re-obtaining the lock without
3961       verifying this may lead to data corruption.  If the lock can not
3962       be obtained safely, then all active locks for the cm_scache_t
3963       are marked as LOST.
3964
3965       4.1 LOST->DELETED: The lock is released.
3966
3967    5. DELETED: The lock is no longer relevant.  Eventually, it will
3968       get removed from the cm_scache_t. In the meantime, it will be
3969       treated as if it does not exist.
3970
3971       5.1 DELETED->not exist: The lock is removed from the
3972         cm_scache_t.
3973
3974    The following are classifications of locks based on their state.
3975
3976    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
3977       have been accepted by the cache manager, but may or may not have
3978       been granted back to the client.
3979
3980    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
3981
3982    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
3983
3984    Lock operation:
3985
3986    A client C can READ range (Offset,+Length) of a file represented by
3987    cm_scache_t S iff (1):
3988
3989    1. for all _a_ in (Offset,+Length), all of the following is true:
3990
3991        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
3992          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
3993          shared.
3994
3995        1.2 For each LOST lock L in S->fileLocks such that _a_ in
3996          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
3997          Key(C)
3998
3999        (When locks are lost on an cm_scache_t, all locks are lost.  By
4000        4.2 (below), if there is an exclusive LOST lock, then there
4001        can't be any overlapping ACTIVE locks.)
4002
4003    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4004
4005    2. for all _a_ in (Offset,+Length), one of the following is true:
4006
4007        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4008          does not exist a LOST lock L such that _a_ in
4009          (L->LOffset,+L->LLength).
4010
4011        2.2 Byte _a_ of S is owned by C under lock L (as specified in
4012          1.2) AND L->LockType is exclusive.
4013
4014    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4015
4016    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4017       true:
4018
4019        3.1 If L->LockType is exclusive then there does NOT exist a
4020          ACCEPTED lock M in S->fileLocks such that _a_ in
4021          (M->LOffset,+M->LLength).
4022
4023          (If we count all QUEUED locks then we hit cases such as
4024          cascading waiting locks where the locks later on in the queue
4025          can be granted without compromising file integrity.  On the
4026          other hand if only ACCEPTED locks are considered, then locks
4027          that were received earlier may end up waiting for locks that
4028          were received later to be unlocked. The choice of ACCEPTED
4029          locks was made to mimic the Windows byte range lock
4030          semantics.)
4031
4032        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4033          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4034          M->LockType is shared.
4035
4036    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4037
4038        4.1 M->key != Key(C)
4039
4040        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4041          and (M->LOffset,+M->LLength) do not intersect.
4042
4043          (Note: If a client loses a lock, it loses all locks.
4044          Subsequently, it will not be allowed to obtain any more locks
4045          until all existing LOST locks that belong to the client are
4046          released.  Once all locks are released by a single client,
4047          there exists no further contract between the client and AFS
4048          about the contents of the file, hence the client can then
4049          proceed to obtain new locks and establish a new contract.
4050
4051          This doesn't quite work as you think it should, because most
4052          applications aren't built to deal with losing locks they
4053          thought they once had.  For now, we don't have a good
4054          solution to lost locks.
4055
4056          Also, for consistency reasons, we have to hold off on
4057          granting locks that overlap exclusive LOST locks.)
4058
4059    A client C can only unlock locks L in S->fileLocks which have
4060    L->key == Key(C).
4061
4062    The representation and invariants are as follows:
4063
4064    - Each cm_scache_t structure keeps:
4065
4066        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4067          are of type cm_file_lock_t.
4068
4069        - A record of the highest server-side lock that has been
4070          obtained for this object (cm_scache_t::serverLock), which is
4071          one of (-1), LockRead, LockWrite.
4072
4073        - A count of ACCEPTED exclusive and shared locks that are in the
4074          queue (cm_scache_t::sharedLocks and
4075          cm_scache_t::exclusiveLocks)
4076
4077    - Each cm_file_lock_t structure keeps:
4078
4079        - The type of lock (cm_file_lock_t::LockType)
4080
4081        - The key associated with the lock (cm_file_lock_t::key)
4082
4083        - The offset and length of the lock (cm_file_lock_t::LOffset
4084          and cm_file_lock_t::LLength)
4085
4086        - The state of the lock.
4087
4088        - Time of issuance or last successful extension
4089
4090    Semantic invariants:
4091
4092        I1. The number of ACCEPTED locks in S->fileLocks are
4093            (S->sharedLocks + S->exclusiveLocks)
4094
4095    External invariants:
4096
4097        I3. S->serverLock is the lock that we have asserted with the
4098            AFS file server for this cm_scache_t.
4099
4100        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4101            shared lock, but no ACTIVE exclusive locks.
4102
4103        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4104            exclusive lock.
4105
4106        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4107            M->key == L->key IMPLIES M is LOST or DELETED.
4108
4109    --asanka
4110  */
4111
4112 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4113
4114 #define IS_LOCK_WAITLOCK(lockp)   (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITLOCK)
4115
4116 #define IS_LOCK_WAITUNLOCK(lockp) (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_WAITUNLOCK)
4117
4118 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4119
4120 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4121
4122 /* unsafe */
4123 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4124
4125 /* unsafe */
4126 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4127
4128 /* unsafe */
4129 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4130
4131 /* unsafe */
4132 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4133
4134 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4135 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4136 #else
4137 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4138 #endif
4139
4140 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4141
4142 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4143 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4144 #else
4145 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4146
4147 /* This should really be defined in any build that this code is being
4148    compiled. */
4149 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4150 #endif
4151
4152 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4153 {
4154     afs_int64 int_begin;
4155     afs_int64 int_end;
4156
4157     int_begin = MAX(pos->offset, neg->offset);
4158     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4159
4160     if (int_begin < int_end) {
4161         if (int_begin == pos->offset) {
4162             pos->length = pos->offset + pos->length - int_end;
4163             pos->offset = int_end;
4164         } else if (int_end == pos->offset + pos->length) {
4165             pos->length = int_begin - pos->offset;
4166         }
4167
4168         /* We only subtract ranges if the resulting range is
4169            contiguous.  If we try to support non-contigous ranges, we
4170            aren't actually improving performance. */
4171     }
4172 }
4173
4174 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4175    specified range by the client identified by key.
4176  */
4177 long cm_LockCheckRead(cm_scache_t *scp,
4178                       LARGE_INTEGER LOffset,
4179                       LARGE_INTEGER LLength,
4180                       cm_key_t key)
4181 {
4182 #ifndef ADVISORY_LOCKS
4183
4184     cm_file_lock_t *fileLock;
4185     osi_queue_t *q;
4186     long code = 0;
4187     cm_range_t range;
4188     int substract_ranges = FALSE;
4189
4190     range.offset = LOffset.QuadPart;
4191     range.length = LLength.QuadPart;
4192
4193     /*
4194
4195      1. for all _a_ in (Offset,+Length), all of the following is true:
4196
4197        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4198          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4199          shared.
4200
4201        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4202          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4203          Key(C)
4204
4205     */
4206
4207     lock_ObtainRead(&cm_scacheLock);
4208
4209     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4210         fileLock =
4211             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4212
4213         if (INTERSECT_RANGE(range, fileLock->range)) {
4214             if (IS_LOCK_ACTIVE(fileLock)) {
4215                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4216
4217                     /* If there is an active lock for this client, it
4218                        is safe to substract ranges.*/
4219                     cm_LockRangeSubtract(&range, &fileLock->range);
4220                     substract_ranges = TRUE;
4221                 } else {
4222                     if (fileLock->lockType != LockRead) {
4223                         code = CM_ERROR_LOCK_CONFLICT;
4224                         break;
4225                     }
4226
4227                     /* even if the entire range is locked for reading,
4228                        we still can't grant the lock at this point
4229                        because the client may have lost locks. That
4230                        is, unless we have already seen an active lock
4231                        belonging to the client, in which case there
4232                        can't be any lost locks for this client. */
4233                     if (substract_ranges)
4234                         cm_LockRangeSubtract(&range, &fileLock->range);
4235                 }
4236             } else if (IS_LOCK_LOST(fileLock) &&
4237                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4238                 code = CM_ERROR_BADFD;
4239                 break;
4240             }
4241         }
4242     }
4243
4244     lock_ReleaseRead(&cm_scacheLock);
4245
4246     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4247               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4248
4249     return code;
4250
4251 #else
4252
4253     return 0;
4254
4255 #endif
4256 }
4257
4258 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4259    specified range by the client identified by key.
4260  */
4261 long cm_LockCheckWrite(cm_scache_t *scp,
4262                        LARGE_INTEGER LOffset,
4263                        LARGE_INTEGER LLength,
4264                        cm_key_t key)
4265 {
4266 #ifndef ADVISORY_LOCKS
4267
4268     cm_file_lock_t *fileLock;
4269     osi_queue_t *q;
4270     long code = 0;
4271     cm_range_t range;
4272
4273     range.offset = LOffset.QuadPart;
4274     range.length = LLength.QuadPart;
4275
4276     /*
4277    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4278
4279    2. for all _a_ in (Offset,+Length), one of the following is true:
4280
4281        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4282          lock L such that _a_ in (L->LOffset,+L->LLength).
4283
4284        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4285          exclusive.
4286     */
4287
4288     lock_ObtainRead(&cm_scacheLock);
4289
4290     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4291         fileLock =
4292             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4293
4294         if (INTERSECT_RANGE(range, fileLock->range)) {
4295             if (IS_LOCK_ACTIVE(fileLock)) {
4296                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4297                     if (fileLock->lockType == LockWrite) {
4298
4299                         /* if there is an active lock for this client, it
4300                            is safe to substract ranges */
4301                         cm_LockRangeSubtract(&range, &fileLock->range);
4302                     } else {
4303                         code = CM_ERROR_LOCK_CONFLICT;
4304                         break;
4305                     }
4306                 } else {
4307                     code = CM_ERROR_LOCK_CONFLICT;
4308                     break;
4309                 }
4310             } else if (IS_LOCK_LOST(fileLock)) {
4311                 code = CM_ERROR_BADFD;
4312                 break;
4313             }
4314         }
4315     }
4316
4317     lock_ReleaseRead(&cm_scacheLock);
4318
4319     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4320               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4321
4322     return code;
4323
4324 #else
4325
4326     return 0;
4327
4328 #endif
4329 }
4330
4331 /* Called with cm_scacheLock write locked */
4332 static cm_file_lock_t * cm_GetFileLock(void) {
4333     cm_file_lock_t * l;
4334
4335     l = (cm_file_lock_t *) cm_freeFileLocks;
4336     if (l) {
4337         osi_QRemove(&cm_freeFileLocks, &l->q);
4338     } else {
4339         l = malloc(sizeof(cm_file_lock_t));
4340         osi_assertx(l, "null cm_file_lock_t");
4341     }
4342
4343     memset(l, 0, sizeof(cm_file_lock_t));
4344
4345     return l;
4346 }
4347
4348 /* Called with cm_scacheLock write locked */
4349 static void cm_PutFileLock(cm_file_lock_t *l) {
4350     osi_QAdd(&cm_freeFileLocks, &l->q);
4351 }
4352
4353 /* called with scp->rw held.  May release it during processing, but
4354    leaves it held on exit. */
4355 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4356                    cm_req_t * reqp) {
4357     long code = 0;
4358     AFSFid tfid;
4359     cm_fid_t cfid;
4360     cm_conn_t * connp;
4361     struct rx_connection * rxconnp;
4362     AFSVolSync volSync;
4363     afs_uint32 reqflags = reqp->flags;
4364
4365     memset(&volSync, 0, sizeof(volSync));
4366
4367     tfid.Volume = scp->fid.volume;
4368     tfid.Vnode = scp->fid.vnode;
4369     tfid.Unique = scp->fid.unique;
4370     cfid = scp->fid;
4371
4372     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4373
4374     reqp->flags |= CM_REQ_NORETRY;
4375     lock_ReleaseWrite(&scp->rw);
4376
4377     do {
4378         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4379         if (code)
4380             break;
4381
4382         rxconnp = cm_GetRxConn(connp);
4383         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4384                              &volSync);
4385         rx_PutConnection(rxconnp);
4386
4387     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4388                         NULL, NULL, code));
4389
4390     code = cm_MapRPCError(code, reqp);
4391     if (code) {
4392         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4393     } else {
4394         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4395     }
4396
4397     lock_ObtainWrite(&scp->rw);
4398     reqp->flags = reqflags;
4399     return code;
4400 }
4401
4402 /* called with scp->rw held.  Releases it during processing */
4403 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4404                        cm_req_t * reqp) {
4405     long code = 0;
4406     AFSFid tfid;
4407     cm_fid_t cfid;
4408     cm_conn_t * connp;
4409     struct rx_connection * rxconnp;
4410     AFSVolSync volSync;
4411
4412     if (scp->flags & CM_SCACHEFLAG_DELETED) {
4413         osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4414         return 0;
4415     }
4416
4417     memset(&volSync, 0, sizeof(volSync));
4418
4419     tfid.Volume = scp->fid.volume;
4420     tfid.Vnode = scp->fid.vnode;
4421     tfid.Unique = scp->fid.unique;
4422     cfid = scp->fid;
4423
4424     lock_ReleaseWrite(&scp->rw);
4425
4426     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4427
4428     do {
4429         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4430         if (code)
4431             break;
4432
4433         rxconnp = cm_GetRxConn(connp);
4434         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4435         rx_PutConnection(rxconnp);
4436
4437     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4438                         NULL, NULL, code));
4439     code = cm_MapRPCError(code, reqp);
4440     if (code)
4441         osi_Log1(afsd_logp,
4442                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4443     else
4444         osi_Log0(afsd_logp,
4445                  "CALL ReleaseLock SUCCESS");
4446
4447     lock_ObtainWrite(&scp->rw);
4448
4449     return (code != CM_ERROR_BADFD ? code : 0);
4450 }
4451
4452 /* called with scp->rw held.  May release it during processing, but
4453    will exit with lock held.
4454
4455    This will return:
4456
4457    - 0 if the user has permission to get the specified lock for the scp
4458
4459    - CM_ERROR_NOACCESS if not
4460
4461    Any other error from cm_SyncOp will be sent down untranslated.
4462
4463    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4464    phas_insert (if non-NULL) will receive a boolean value indicating
4465    whether the user has INSERT permission or not.
4466 */
4467 long cm_LockCheckPerms(cm_scache_t * scp,
4468                        int lock_type,
4469                        cm_user_t * userp,
4470                        cm_req_t * reqp,
4471                        int * phas_insert)
4472 {
4473     long rights = 0;
4474     long code = 0, code2 = 0;
4475
4476     /* lock permissions are slightly tricky because of the 'i' bit.
4477        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4478        user has PRSFS_WRITE, she can write-lock the file.  However, if
4479        the user has PRSFS_INSERT, then she can write-lock new files,
4480        but not old ones.  Since we don't have information about
4481        whether a file is new or not, we assume that if the user owns
4482        the scp, then she has the permissions that are granted by
4483        PRSFS_INSERT. */
4484
4485     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4486              scp, lock_type, userp);
4487
4488     if (lock_type == LockRead)
4489         rights |= PRSFS_LOCK;
4490     else if (lock_type == LockWrite)
4491         rights |= PRSFS_WRITE | PRSFS_LOCK;
4492     else {
4493         /* hmmkay */
4494         osi_assertx(FALSE, "invalid lock type");
4495         return 0;
4496     }
4497
4498     if (phas_insert)
4499         *phas_insert = FALSE;
4500
4501     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4502                      CM_SCACHESYNC_GETSTATUS |
4503                      CM_SCACHESYNC_NEEDCALLBACK);
4504
4505     if (phas_insert && scp->creator == userp) {
4506
4507         /* If this file was created by the user, then we check for
4508            PRSFS_INSERT.  If the file server is recent enough, then
4509            this should be sufficient for her to get a write-lock (but
4510            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4511            indicates whether a file server supports getting write
4512            locks when the user only has PRSFS_INSERT.
4513
4514            If the file was not created by the user we skip the check
4515            because the INSERT bit will not apply to this user even
4516            if it is set.
4517          */
4518
4519         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4520                          CM_SCACHESYNC_GETSTATUS |
4521                          CM_SCACHESYNC_NEEDCALLBACK);
4522
4523         if (code2 == CM_ERROR_NOACCESS) {
4524             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4525         } else {
4526             *phas_insert = TRUE;
4527             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4528         }
4529     }
4530
4531     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4532
4533     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4534
4535     return code;
4536 }
4537
4538 /* called with scp->rw held */
4539 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4540              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4541              cm_key_t key,
4542              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4543              cm_file_lock_t **lockpp)
4544 {
4545     long code = 0;
4546     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4547     cm_file_lock_t *fileLock;
4548     osi_queue_t *q;
4549     cm_range_t range;
4550     int wait_unlock = FALSE;
4551     int force_client_lock = FALSE;
4552
4553     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4554              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4555     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4556              key.process_id, key.session_id, key.file_id);
4557
4558     /*
4559    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4560
4561    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4562       true:
4563
4564        3.1 If L->LockType is exclusive then there does NOT exist a
4565          ACCEPTED lock M in S->fileLocks such that _a_ in
4566          (M->LOffset,+M->LLength).
4567
4568        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4569          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4570          M->LockType is shared.
4571
4572    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4573
4574        4.1 M->key != Key(C)
4575
4576        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4577          and (M->LOffset,+M->LLength) do not intersect.
4578     */
4579
4580     range.offset = LOffset.QuadPart;
4581     range.length = LLength.QuadPart;
4582
4583     lock_ObtainRead(&cm_scacheLock);
4584
4585     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4586         fileLock =
4587             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4588
4589         if (IS_LOCK_LOST(fileLock)) {
4590             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4591                 code = CM_ERROR_BADFD;
4592                 break;
4593             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4594                 code = CM_ERROR_WOULDBLOCK;
4595                 wait_unlock = TRUE;
4596                 break;
4597             }
4598         }
4599
4600         /* we don't need to check for deleted locks here since deleted
4601            locks are dequeued from scp->fileLocks */
4602         if (IS_LOCK_ACCEPTED(fileLock) &&
4603            INTERSECT_RANGE(range, fileLock->range)) {
4604
4605             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4606                 fileLock->lockType != LockRead) {
4607                 wait_unlock = TRUE;
4608                 code = CM_ERROR_WOULDBLOCK;
4609                 break;
4610             }
4611         }
4612     }
4613
4614     lock_ReleaseRead(&cm_scacheLock);
4615
4616     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4617         if (Which == scp->serverLock ||
4618            (Which == LockRead && scp->serverLock == LockWrite)) {
4619
4620             int has_insert = 0;
4621
4622             /* we already have the lock we need */
4623             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4624                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4625
4626             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4627
4628             /* special case: if we don't have permission to read-lock
4629                the file, then we force a clientside lock.  This is to
4630                compensate for applications that obtain a read-lock for
4631                reading files off of directories that don't grant
4632                read-locks to the user. */
4633             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4634
4635                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4636                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4637                     code = 0;
4638                 } else {
4639                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4640                     force_client_lock = TRUE;
4641                 }
4642             }
4643
4644         } else if ((scp->exclusiveLocks > 0) ||
4645                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4646             int has_insert = 0;
4647
4648             /* We are already waiting for some other lock.  We should
4649                wait for the daemon to catch up instead of generating a
4650                flood of SetLock calls. */
4651             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4652                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4653
4654             /* see if we have permission to create the lock in the
4655                first place. */
4656             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4657             if (code == 0)
4658                 code = CM_ERROR_WOULDBLOCK;
4659             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4660
4661                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4662                     osi_Log0(afsd_logp,
4663                              "   User has no read-lock perms, but has INSERT perms.");
4664                     code = CM_ERROR_WOULDBLOCK;
4665                 } else {
4666                     osi_Log0(afsd_logp,
4667                              "   User has no read-lock perms. Forcing client-side lock");
4668                     force_client_lock = TRUE;
4669                 }
4670             }
4671
4672             /* leave any other codes as-is */
4673
4674         } else {
4675             int newLock;
4676             int check_data_version = FALSE;
4677             int has_insert = 0;
4678
4679             /* first check if we have permission to elevate or obtain
4680                the lock. */
4681             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4682             if (code) {
4683                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4684                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4685                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4686                     force_client_lock = TRUE;
4687                 }
4688                 goto check_code;
4689             }
4690
4691             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4692
4693             if (scp->serverLock == LockRead && Which == LockWrite) {
4694
4695                 /* We want to escalate the lock to a LockWrite.
4696                  * Unfortunately that's not really possible without
4697                  * letting go of the current lock.  But for now we do
4698                  * it anyway. */
4699
4700                 osi_Log0(afsd_logp,
4701                          "   attempting to UPGRADE from LockRead to LockWrite.");
4702                 osi_Log1(afsd_logp,
4703                          "   dataVersion on scp: %I64d", scp->dataVersion);
4704
4705                 /* we assume at this point (because scp->serverLock
4706                    was valid) that we had a valid server lock. */
4707                 scp->lockDataVersion = scp->dataVersion;
4708                 check_data_version = TRUE;
4709
4710                 code = cm_IntReleaseLock(scp, userp, reqp);
4711
4712                 if (code) {
4713                     /* We couldn't release the lock */
4714                     goto check_code;
4715                 } else {
4716                     scp->serverLock = -1;
4717                 }
4718             }
4719
4720             /* We need to obtain a server lock of type Which in order
4721              * to assert this file lock */
4722 #ifndef AGGRESSIVE_LOCKS
4723             newLock = Which;
4724 #else
4725             newLock = LockWrite;
4726 #endif
4727
4728             code = cm_IntSetLock(scp, userp, newLock, reqp);
4729
4730 #ifdef AGGRESSIVE_LOCKS
4731             if ((code == CM_ERROR_WOULDBLOCK ||
4732                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4733                 /* we wanted LockRead.  We tried LockWrite. Now try
4734                  * LockRead again */
4735                 newLock = Which;
4736
4737                 /* am I sane? */
4738                 osi_assertx(newLock == LockRead, "lock type not read");
4739
4740                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4741             }
4742 #endif
4743
4744             if (code == CM_ERROR_NOACCESS) {
4745                 if (Which == LockRead) {
4746                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4747                         long tcode;
4748                         /* We requested a read-lock, but we have permission to
4749                          * get a write-lock. Try that */
4750
4751                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4752
4753                         if (tcode == 0) {
4754                             newLock = LockWrite;
4755
4756                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4757
4758                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4759                         }
4760                     } else {
4761                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4762                         force_client_lock = TRUE;
4763                     }
4764                 } else if (Which == LockWrite &&
4765                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4766                     long tcode;
4767
4768                     /* Special case: if the lock request was for a
4769                      * LockWrite and the user owns the file and we weren't
4770                      * allowed to obtain the serverlock, we either lost a
4771                      * race (the permissions changed from under us), or we
4772                      * have 'i' bits, but we aren't allowed to lock the
4773                      * file. */
4774
4775                     /* check if we lost a race... */
4776                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4777
4778                     if (tcode == 0) {
4779                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4780                         force_client_lock = TRUE;
4781                     }
4782                 }
4783             }
4784
4785             if (code == 0 && check_data_version &&
4786                scp->dataVersion != scp->lockDataVersion) {
4787                 /* We lost a race.  Although we successfully obtained
4788                  * a lock, someone modified the file in between.  The
4789                  * locks have all been technically lost. */
4790
4791                 osi_Log0(afsd_logp,
4792                          "  Data version mismatch while upgrading lock.");
4793                 osi_Log2(afsd_logp,
4794                          "  Data versions before=%I64d, after=%I64d",
4795                          scp->lockDataVersion,
4796                          scp->dataVersion);
4797                 osi_Log1(afsd_logp,
4798                          "  Releasing stale lock for scp 0x%x", scp);
4799
4800                 code = cm_IntReleaseLock(scp, userp, reqp);
4801
4802                 scp->serverLock = -1;
4803
4804                 code = CM_ERROR_INVAL;
4805             } else if (code == 0) {
4806                 scp->serverLock = newLock;
4807                 scp->lockDataVersion = scp->dataVersion;
4808             }
4809
4810             if (code != 0 &&
4811                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4812                 scp->serverLock == -1) {
4813                 /* Oops. We lost the lock. */
4814                 cm_LockMarkSCacheLost(scp);
4815             }
4816         }
4817     } else if (code == 0) {     /* server locks not enabled */
4818         osi_Log0(afsd_logp,
4819                  "  Skipping server lock for scp");
4820     }
4821
4822  check_code:
4823
4824     if (code != 0 && !force_client_lock) {
4825         /* Special case error translations
4826
4827            Applications don't expect certain errors from a
4828            LockFile/UnlockFile call.  We need to translate some error
4829            code to codes that apps expect and handle. */
4830
4831         /* We shouldn't actually need to handle this case since we
4832            simulate locks for RO scps anyway. */
4833         if (code == CM_ERROR_READONLY) {
4834             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4835             code = CM_ERROR_NOACCESS;
4836         }
4837     }
4838
4839     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4840         force_client_lock) {
4841
4842         /* clear the error if we are forcing a client lock, so we
4843            don't get confused later. */
4844         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4845             code = 0;
4846
4847         lock_ObtainWrite(&cm_scacheLock);
4848         fileLock = cm_GetFileLock();
4849         lock_ReleaseWrite(&cm_scacheLock);
4850 #ifdef DEBUG
4851         fileLock->fid = scp->fid;
4852 #endif
4853         fileLock->key = key;
4854         fileLock->lockType = Which;
4855         cm_HoldUser(userp);
4856         fileLock->userp = userp;
4857         fileLock->range = range;
4858         fileLock->flags = (code == 0 ? 0 :
4859                            ((wait_unlock)?
4860                             CM_FILELOCK_FLAG_WAITUNLOCK :
4861                             CM_FILELOCK_FLAG_WAITLOCK));
4862
4863         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4864             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4865
4866         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4867
4868         lock_ObtainWrite(&cm_scacheLock);
4869         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4870         cm_HoldSCacheNoLock(scp);
4871         fileLock->scp = scp;
4872         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4873         lock_ReleaseWrite(&cm_scacheLock);
4874
4875         if (code != 0) {
4876             *lockpp = fileLock;
4877         }
4878
4879         if (IS_LOCK_CLIENTONLY(fileLock)) {
4880             scp->clientLocks++;
4881         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4882             if (Which == LockRead)
4883                 scp->sharedLocks++;
4884             else
4885                 scp->exclusiveLocks++;
4886         }
4887
4888         osi_Log3(afsd_logp,
4889                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4890                  fileLock, fileLock->flags, scp);
4891         osi_Log4(afsd_logp,
4892                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4893                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4894                  (int)(signed char) scp->serverLock);
4895     } else {
4896         osi_Log1(afsd_logp,
4897                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4898     }
4899
4900     /* Convert from would block to lock not granted */
4901     if (code == CM_ERROR_WOULDBLOCK)
4902         code = CM_ERROR_LOCK_NOT_GRANTED;
4903
4904     return code;
4905 }
4906
4907 static long
4908 cm_IntUnlock(cm_scache_t * scp,
4909              cm_user_t * userp,
4910              cm_req_t *  reqp)
4911 {
4912     long code = 0;
4913
4914     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4915     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4916     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4917
4918     if (!SERVERLOCKS_ENABLED(scp)) {
4919         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4920         goto done;
4921     }
4922
4923     /* Ideally we would go through the rest of the locks to determine
4924      * if one or more locks that were formerly in WAITUNLOCK can now
4925      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4926      * scp->sharedLocks accordingly.  However, the retrying of locks
4927      * in that manner is done cm_RetryLock() manually.
4928      */
4929
4930     if (scp->serverLock == LockWrite &&
4931         scp->exclusiveLocks == 0 &&
4932         scp->sharedLocks > 0) {
4933         /* The serverLock should be downgraded to LockRead */
4934         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4935
4936         /* Make sure there are no dirty buffers left. */
4937         code = cm_FSync(scp, userp, reqp, TRUE);
4938
4939         /* since scp->serverLock looked sane, we are going to assume
4940            that we have a valid server lock. */
4941         scp->lockDataVersion = scp->dataVersion;
4942         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4943
4944         /* before we downgrade, make sure that we have enough
4945            permissions to get the read lock. */
4946         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4947         if (code != 0) {
4948
4949             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4950
4951             code = 0;
4952             goto done;
4953         }
4954
4955         code = cm_IntReleaseLock(scp, userp, reqp);
4956
4957         if (code) {
4958             /* so we couldn't release it.  Just let the lock be for now */
4959             code = 0;
4960             goto done;
4961         } else {
4962             scp->serverLock = -1;
4963         }
4964
4965         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4966
4967         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4968             scp->serverLock = LockRead;
4969         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4970             /* We lost a race condition.  Although we have a valid
4971                lock on the file, the data has changed and essentially
4972                we have lost the lock we had during the transition. */
4973
4974             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4975             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
4976                      scp->lockDataVersion,
4977                      scp->dataVersion);
4978
4979             code = cm_IntReleaseLock(scp, userp, reqp);
4980
4981             code = CM_ERROR_INVAL;
4982             scp->serverLock = -1;
4983         }
4984
4985         if (code != 0 &&
4986             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4987                 (scp->serverLock == -1)) {
4988                 /* Oopsie */
4989                 cm_LockMarkSCacheLost(scp);
4990             }
4991
4992         /* failure here has no bearing on the return value of cm_Unlock() */
4993         code = 0;
4994
4995     } else if (scp->serverLock != (-1) &&
4996               scp->exclusiveLocks == 0 &&
4997               scp->sharedLocks == 0) {
4998         /* The serverLock should be released entirely */
4999
5000         if (scp->serverLock == LockWrite) {
5001             osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
5002
5003             /* Make sure there are no dirty buffers left. */
5004             code = cm_FSync(scp, userp, reqp, TRUE);
5005         } else {
5006             osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
5007         }
5008
5009         code = cm_IntReleaseLock(scp, userp, reqp);
5010
5011         if (code == 0)
5012             scp->serverLock = (-1);
5013     }
5014
5015   done:
5016     return code;
5017 }
5018 /* Called with scp->rw held */
5019 long cm_UnlockByKey(cm_scache_t * scp,
5020                     cm_key_t key,
5021                     afs_uint32 flags,
5022                     cm_user_t * userp,
5023                     cm_req_t * reqp)
5024 {
5025     long code = 0;
5026     cm_file_lock_t *fileLock;
5027     osi_queue_t *q, *qn;
5028     int n_unlocks = 0;
5029
5030     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5031              scp, key.process_id, key.session_id, key.file_id);
5032     osi_Log1(afsd_logp, "    flags=0x%x", flags);
5033
5034     lock_ObtainWrite(&cm_scacheLock);
5035
5036     for (q = scp->fileLocksH; q; q = qn) {
5037         qn = osi_QNext(q);
5038
5039         fileLock = (cm_file_lock_t *)
5040             ((char *) q - offsetof(cm_file_lock_t, fileq));
5041
5042 #ifdef DEBUG
5043         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
5044                  fileLock,
5045                  (unsigned long) fileLock->range.offset,
5046                  (unsigned long) fileLock->range.length,
5047                 fileLock->lockType);
5048         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5049                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5050                  fileLock->flags);
5051
5052         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5053             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5054             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5055                      fileLock->fid.cell,
5056                      fileLock->fid.volume,
5057                      fileLock->fid.vnode,
5058                      fileLock->fid.unique);
5059             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5060                      fileLock->scp->fid.cell,
5061                      fileLock->scp->fid.volume,
5062                      fileLock->scp->fid.vnode,
5063                      fileLock->scp->fid.unique);
5064             osi_assertx(FALSE, "invalid fid value");
5065         }
5066 #endif
5067
5068         if (!IS_LOCK_DELETED(fileLock) &&
5069             cm_KeyEquals(&fileLock->key, &key, flags)) {
5070             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5071                     fileLock->range.offset,
5072                     fileLock->range.length,
5073                     fileLock->lockType);
5074
5075             if (scp->fileLocksT == q)
5076                 scp->fileLocksT = osi_QPrev(q);
5077             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5078
5079             if (IS_LOCK_CLIENTONLY(fileLock)) {
5080                 scp->clientLocks--;
5081             } else if (IS_LOCK_ACCEPTED(fileLock)) {
5082                 if (fileLock->lockType == LockRead)
5083                     scp->sharedLocks--;
5084                 else
5085                     scp->exclusiveLocks--;
5086             }
5087
5088             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5089
5090             n_unlocks++;
5091         }
5092     }
5093
5094     lock_ReleaseWrite(&cm_scacheLock);
5095
5096     if (n_unlocks == 0) {
5097         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5098         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5099                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5100
5101         return 0;
5102     }
5103
5104     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
5105
5106     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5107     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5108              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5109              (int)(signed char) scp->serverLock);
5110
5111     return code;
5112 }
5113
5114 long cm_Unlock(cm_scache_t *scp,
5115                unsigned char sLockType,
5116                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5117                cm_key_t key,
5118                afs_uint32 flags,
5119                cm_user_t *userp,
5120                cm_req_t *reqp)
5121 {
5122     long code = 0;
5123     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5124     cm_file_lock_t *fileLock;
5125     osi_queue_t *q;
5126     int release_userp = FALSE;
5127     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5128     int lock_found  = 0;
5129     LARGE_INTEGER RangeEnd;
5130
5131     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5132              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5133     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5134              key.process_id, key.session_id, key.file_id, flags);
5135
5136     if (!exact_match)
5137         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5138
5139   try_again:
5140     lock_ObtainRead(&cm_scacheLock);
5141
5142     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5143         fileLock = (cm_file_lock_t *)
5144             ((char *) q - offsetof(cm_file_lock_t, fileq));
5145
5146 #ifdef DEBUG
5147         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5148             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5149             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5150                      fileLock->fid.cell,
5151                      fileLock->fid.volume,
5152                      fileLock->fid.vnode,
5153                      fileLock->fid.unique);
5154             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5155                      fileLock->scp->fid.cell,
5156                      fileLock->scp->fid.volume,
5157                      fileLock->scp->fid.vnode,
5158                      fileLock->scp->fid.unique);
5159             osi_assertx(FALSE, "invalid fid value");
5160         }
5161 #endif
5162         if (exact_match) {
5163             if (!IS_LOCK_DELETED(fileLock) &&
5164                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5165                  fileLock->range.offset == LOffset.QuadPart &&
5166                  fileLock->range.length == LLength.QuadPart) {
5167                 lock_found = 1;
5168                 break;
5169             }
5170         } else {
5171
5172             if (!IS_LOCK_DELETED(fileLock) &&
5173                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5174                  fileLock->range.offset >= LOffset.QuadPart &&
5175                  fileLock->range.offset < RangeEnd.QuadPart &&
5176                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5177                 lock_found = 1;
5178                 break;
5179             }
5180         }
5181     }
5182
5183     if (!q) {
5184         lock_ReleaseRead(&cm_scacheLock);
5185
5186         if (lock_found && !exact_match) {
5187             code = 0;
5188             goto done;
5189         } else {
5190             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5191
5192             /* The lock didn't exist anyway. *shrug* */
5193             return CM_ERROR_RANGE_NOT_LOCKED;
5194         }
5195     }
5196
5197     /* discard lock record */
5198     lock_ConvertRToW(&cm_scacheLock);
5199     if (scp->fileLocksT == q)
5200         scp->fileLocksT = osi_QPrev(q);
5201     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5202
5203     /*
5204      * Don't delete it here; let the daemon delete it, to simplify
5205      * the daemon's traversal of the list.
5206      */
5207
5208     if (IS_LOCK_CLIENTONLY(fileLock)) {
5209         scp->clientLocks--;
5210     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5211         if (fileLock->lockType == LockRead)
5212             scp->sharedLocks--;
5213         else
5214             scp->exclusiveLocks--;
5215     }
5216
5217     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5218     lock_ReleaseWrite(&cm_scacheLock);
5219
5220     if (release_userp) {
5221         cm_ReleaseUser(userp);
5222         release_userp = FALSE;
5223     }
5224
5225     if (!exact_match) {
5226         osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5227         goto try_again;         /* might be more than one lock in the range */
5228     }
5229
5230  done:
5231
5232     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5233     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5234              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5235              (int)(signed char) scp->serverLock);
5236
5237     return code;
5238 }
5239
5240 /* called with scp->rw held */
5241 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5242 {
5243     cm_file_lock_t *fileLock;
5244     osi_queue_t *q;
5245
5246     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5247
5248     /* cm_scacheLock needed because we are modifying fileLock->flags */
5249     lock_ObtainWrite(&cm_scacheLock);
5250
5251     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5252         fileLock =
5253             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5254
5255         if (IS_LOCK_ACTIVE(fileLock) &&
5256             !IS_LOCK_CLIENTONLY(fileLock)) {
5257             if (fileLock->lockType == LockRead)
5258                 scp->sharedLocks--;
5259             else
5260                 scp->exclusiveLocks--;
5261
5262             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5263         }
5264     }
5265
5266     scp->serverLock = -1;
5267     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5268     lock_ReleaseWrite(&cm_scacheLock);
5269 }
5270
5271 /* Called with no relevant locks held */
5272 void cm_CheckLocks()
5273 {
5274     osi_queue_t *q, *nq;
5275     cm_file_lock_t *fileLock;
5276     cm_req_t req;
5277     AFSFid tfid;
5278     AFSVolSync volSync;
5279     cm_conn_t *connp;
5280     long code;
5281     struct rx_connection * rxconnp;
5282     cm_scache_t * scp;
5283
5284     memset(&volSync, 0, sizeof(volSync));
5285
5286     cm_InitReq(&req);
5287
5288     lock_ObtainWrite(&cm_scacheLock);
5289
5290     cm_lockRefreshCycle++;
5291
5292     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5293
5294     for (q = cm_allFileLocks; q; q = nq) {
5295         fileLock = (cm_file_lock_t *) q;
5296         nq = osi_QNext(q);
5297         code = -1;
5298
5299         if (IS_LOCK_DELETED(fileLock)) {
5300             cm_user_t *userp = fileLock->userp;
5301             cm_scache_t *scp = fileLock->scp;
5302             fileLock->userp = NULL;
5303             fileLock->scp = NULL;
5304
5305             lock_ReleaseWrite(&cm_scacheLock);
5306             lock_ObtainWrite(&scp->rw);
5307             code = cm_IntUnlock(scp, userp, &req);
5308             lock_ReleaseWrite(&scp->rw);
5309
5310             cm_ReleaseUser(fileLock->userp);
5311             lock_ObtainWrite(&cm_scacheLock);
5312             cm_ReleaseSCacheNoLock(scp);
5313
5314             osi_QRemove(&cm_allFileLocks, q);
5315             cm_PutFileLock(fileLock);
5316
5317         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5318
5319             /* Server locks must have been enabled for us to have
5320                received an active non-client-only lock. */
5321             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5322
5323             scp = fileLock->scp;
5324             osi_assertx(scp != NULL, "null cm_scache_t");
5325
5326             cm_HoldSCacheNoLock(scp);
5327
5328 #ifdef DEBUG
5329             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5330                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5331                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5332                          fileLock->fid.cell,
5333                          fileLock->fid.volume,
5334                          fileLock->fid.vnode,
5335                          fileLock->fid.unique);
5336                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5337                          fileLock->scp->fid.cell,
5338                          fileLock->scp->fid.volume,
5339                          fileLock->scp->fid.vnode,
5340                          fileLock->scp->fid.unique);
5341                 osi_assertx(FALSE, "invalid fid");
5342             }
5343 #endif
5344             /* Server locks are extended once per scp per refresh
5345                cycle. */
5346             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5347
5348                 int scp_done = FALSE;
5349
5350                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5351
5352                 lock_ReleaseWrite(&cm_scacheLock);
5353                 lock_ObtainWrite(&scp->rw);
5354
5355                 /* did the lock change while we weren't holding the lock? */
5356                 if (!IS_LOCK_ACTIVE(fileLock))
5357                     goto post_syncopdone;
5358
5359                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5360                                  CM_SCACHESYNC_NEEDCALLBACK
5361                                  | CM_SCACHESYNC_GETSTATUS
5362                                  | CM_SCACHESYNC_LOCK);
5363
5364                 if (code) {
5365                     osi_Log1(afsd_logp,
5366                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5367                     goto post_syncopdone;
5368                 }
5369
5370                 /* cm_SyncOp releases scp->rw during which the lock
5371                    may get released. */
5372                 if (!IS_LOCK_ACTIVE(fileLock))
5373                     goto pre_syncopdone;
5374
5375                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5376                     cm_fid_t cfid;
5377                     cm_user_t * userp;
5378
5379                     tfid.Volume = scp->fid.volume;
5380                     tfid.Vnode = scp->fid.vnode;
5381                     tfid.Unique = scp->fid.unique;
5382                     cfid = scp->fid;
5383                     userp = fileLock->userp;
5384
5385                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5386                              fileLock,
5387                              scp,
5388                              (int) scp->serverLock);
5389
5390                     lock_ReleaseWrite(&scp->rw);
5391
5392                     do {
5393                         code = cm_ConnFromFID(&cfid, userp,
5394                                        &req, &connp);
5395                         if (code)
5396                             break;
5397
5398                         rxconnp = cm_GetRxConn(connp);
5399                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5400                                                 &volSync);
5401                         rx_PutConnection(rxconnp);
5402
5403                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5404
5405                     } while (cm_Analyze(connp, userp, &req,
5406                                         &cfid, &volSync, NULL, NULL,
5407                                         code));
5408
5409                     code = cm_MapRPCError(code, &req);
5410
5411                     lock_ObtainWrite(&scp->rw);
5412
5413                     if (code) {
5414                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5415                     } else {
5416                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5417                         scp->lockDataVersion = scp->dataVersion;
5418                     }
5419
5420                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5421                         scp->lockDataVersion == scp->dataVersion) {
5422                         int lockType;
5423
5424                         lockType =
5425                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5426
5427                         /* we might still have a chance to obtain a
5428                            new lock */
5429
5430                         code = cm_IntSetLock(scp, userp, lockType, &req);
5431
5432                         if (code) {
5433                             code = CM_ERROR_INVAL;
5434                         } else if (scp->lockDataVersion != scp->dataVersion) {
5435
5436                             /* now check if we still have the file at
5437                                the right data version. */
5438                             osi_Log1(afsd_logp,
5439                                      "Data version mismatch on scp 0x%p",
5440                                      scp);
5441                             osi_Log2(afsd_logp,
5442                                      "   Data versions: before=%I64d, after=%I64d",
5443                                      scp->lockDataVersion,
5444                                      scp->dataVersion);
5445
5446                             code = cm_IntReleaseLock(scp, userp, &req);
5447
5448                             code = CM_ERROR_INVAL;
5449                         }
5450                     }
5451
5452                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5453                         code == CM_ERROR_BADFD) {
5454                         cm_LockMarkSCacheLost(scp);
5455                     }
5456
5457                 } else {
5458                     /* interestingly, we have found an active lock
5459                        belonging to an scache that has no
5460                        serverLock */
5461                     cm_LockMarkSCacheLost(scp);
5462                 }
5463
5464                 scp_done = TRUE;
5465
5466             pre_syncopdone:
5467
5468                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5469
5470             post_syncopdone:
5471                 lock_ReleaseWrite(&scp->rw);
5472
5473                 lock_ObtainWrite(&cm_scacheLock);
5474
5475                 if (code == 0) {
5476                     fileLock->lastUpdate = time(NULL);
5477                 }
5478
5479                 if (scp_done)
5480                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5481
5482             } else {
5483                 /* we have already refreshed the locks on this scp */
5484                 fileLock->lastUpdate = time(NULL);
5485             }
5486
5487             cm_ReleaseSCacheNoLock(scp);
5488
5489         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5490             /* TODO: Check callbacks */
5491         }
5492     }
5493
5494     lock_ReleaseWrite(&cm_scacheLock);
5495     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5496 }
5497
5498 /* NOT called with scp->rw held. */
5499 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5500 {
5501     long code = 0;
5502     cm_scache_t *scp = NULL;
5503     cm_file_lock_t *fileLock;
5504     osi_queue_t *q;
5505     cm_req_t req;
5506     int newLock = -1;
5507     int force_client_lock = FALSE;
5508     int has_insert = FALSE;
5509     int check_data_version = FALSE;
5510
5511     cm_InitReq(&req);
5512
5513     if (client_is_dead) {
5514         code = CM_ERROR_TIMEDOUT;
5515         goto updateLock;
5516     }
5517
5518     lock_ObtainRead(&cm_scacheLock);
5519
5520     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5521     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5522              (unsigned)(oldFileLock->range.offset >> 32),
5523              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5524              (unsigned)(oldFileLock->range.length >> 32),
5525              (unsigned)(oldFileLock->range.length & 0xffffffff));
5526     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5527              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5528              (unsigned)(oldFileLock->flags));
5529
5530     /* if the lock has already been granted, then we have nothing to do */
5531     if (IS_LOCK_ACTIVE(oldFileLock)) {
5532         lock_ReleaseRead(&cm_scacheLock);
5533         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5534         return 0;
5535     }
5536
5537     /* we can't do anything with lost or deleted locks at the moment. */
5538     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5539         code = CM_ERROR_BADFD;
5540         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5541         lock_ReleaseRead(&cm_scacheLock);
5542         goto updateLock;
5543     }
5544
5545     scp = oldFileLock->scp;
5546
5547     osi_assertx(scp != NULL, "null cm_scache_t");
5548
5549     lock_ReleaseRead(&cm_scacheLock);
5550     lock_ObtainWrite(&scp->rw);
5551
5552     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5553                              oldFileLock->userp,
5554                              &req, &has_insert);
5555
5556     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5557         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5558         force_client_lock = TRUE;
5559         }
5560         code = 0;
5561     } else if (code) {
5562         lock_ReleaseWrite(&scp->rw);
5563         return code;
5564     }
5565
5566     lock_ObtainWrite(&cm_scacheLock);
5567
5568     /* Check if we already have a sufficient server lock to allow this
5569        lock to go through. */
5570     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5571         (!SERVERLOCKS_ENABLED(scp) ||
5572          scp->serverLock == oldFileLock->lockType ||
5573          scp->serverLock == LockWrite)) {
5574
5575         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5576
5577         if (SERVERLOCKS_ENABLED(scp)) {
5578             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5579                      (int) scp->serverLock);
5580         } else {
5581             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5582         }
5583
5584         lock_ReleaseWrite(&cm_scacheLock);
5585         lock_ReleaseWrite(&scp->rw);
5586
5587         return 0;
5588     }
5589
5590     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5591
5592         /* check if the conflicting locks have dissappeared already */
5593         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5594
5595             fileLock = (cm_file_lock_t *)
5596                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5597
5598             if (IS_LOCK_LOST(fileLock)) {
5599                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5600                     code = CM_ERROR_BADFD;
5601                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5602                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5603                              fileLock);
5604                     break;
5605                 } else if (fileLock->lockType == LockWrite &&
5606                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5607                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5608                     code = CM_ERROR_WOULDBLOCK;
5609                     break;
5610                 }
5611             }
5612
5613             if (IS_LOCK_ACCEPTED(fileLock) &&
5614                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5615
5616                 if (oldFileLock->lockType != LockRead ||
5617                    fileLock->lockType != LockRead) {
5618
5619                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5620                     code = CM_ERROR_WOULDBLOCK;
5621                     break;
5622                 }
5623             }
5624         }
5625     }
5626
5627     if (code != 0) {
5628         lock_ReleaseWrite(&cm_scacheLock);
5629         lock_ReleaseWrite(&scp->rw);
5630
5631         goto handleCode;
5632     }
5633
5634     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5635        If it is WAITUNLOCK, then we didn't find any conflicting lock
5636        but we haven't verfied whether the serverLock is sufficient to
5637        assert it.  If it is WAITLOCK, then the serverLock is
5638        insufficient to assert it. Eitherway, we are ready to accept
5639        the lock as either ACTIVE or WAITLOCK depending on the
5640        serverLock. */
5641
5642     /* First, promote the WAITUNLOCK to a WAITLOCK */
5643     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5644         if (oldFileLock->lockType == LockRead)
5645             scp->sharedLocks++;
5646         else
5647             scp->exclusiveLocks++;
5648
5649         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5650         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5651     }
5652
5653     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5654
5655     if (force_client_lock ||
5656         !SERVERLOCKS_ENABLED(scp) ||
5657         scp->serverLock == oldFileLock->lockType ||
5658         (oldFileLock->lockType == LockRead &&
5659          scp->serverLock == LockWrite)) {
5660
5661         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5662
5663         if ((force_client_lock ||
5664              !SERVERLOCKS_ENABLED(scp)) &&
5665             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5666
5667             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5668
5669             if (oldFileLock->lockType == LockRead)
5670                 scp->sharedLocks--;
5671             else
5672                 scp->exclusiveLocks--;
5673
5674             scp->clientLocks++;
5675         }
5676
5677         lock_ReleaseWrite(&cm_scacheLock);
5678         lock_ReleaseWrite(&scp->rw);
5679
5680         return 0;
5681
5682     } else {
5683         cm_user_t * userp;
5684
5685         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5686                          CM_SCACHESYNC_NEEDCALLBACK
5687                          | CM_SCACHESYNC_GETSTATUS
5688                          | CM_SCACHESYNC_LOCK);
5689         if (code) {
5690             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5691             lock_ReleaseWrite(&cm_scacheLock);
5692             goto post_syncopdone;
5693         }
5694
5695         if (!IS_LOCK_WAITLOCK(oldFileLock))
5696             goto pre_syncopdone;
5697
5698         userp = oldFileLock->userp;
5699
5700 #ifndef AGGRESSIVE_LOCKS
5701         newLock = oldFileLock->lockType;
5702 #else
5703         newLock = LockWrite;
5704 #endif
5705
5706         if (has_insert) {
5707             /* if has_insert is non-zero, then:
5708                - the lock a LockRead
5709                - we don't have permission to get a LockRead
5710                - we do have permission to get a LockWrite
5711                - the server supports VICED_CAPABILITY_WRITELOCKACL
5712             */
5713
5714             newLock = LockWrite;
5715         }
5716
5717         lock_ReleaseWrite(&cm_scacheLock);
5718
5719         /* when we get here, either we have a read-lock and want a
5720            write-lock or we don't have any locks and we want some
5721            lock. */
5722
5723         if (scp->serverLock == LockRead) {
5724
5725             osi_assertx(newLock == LockWrite, "!LockWrite");
5726
5727             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5728
5729             scp->lockDataVersion = scp->dataVersion;
5730             check_data_version = TRUE;
5731
5732             code = cm_IntReleaseLock(scp, userp, &req);
5733
5734             if (code)
5735                 goto pre_syncopdone;
5736             else
5737                 scp->serverLock = -1;
5738         }
5739
5740         code = cm_IntSetLock(scp, userp, newLock, &req);
5741
5742         if (code == 0) {
5743             if (scp->dataVersion != scp->lockDataVersion) {
5744                 /* we lost a race.  too bad */
5745
5746                 osi_Log0(afsd_logp,
5747                          "  Data version mismatch while upgrading lock.");
5748                 osi_Log2(afsd_logp,
5749                          "  Data versions before=%I64d, after=%I64d",
5750                          scp->lockDataVersion,
5751                          scp->dataVersion);
5752                 osi_Log1(afsd_logp,
5753                          "  Releasing stale lock for scp 0x%x", scp);
5754
5755                 code = cm_IntReleaseLock(scp, userp, &req);
5756
5757                 scp->serverLock = -1;
5758
5759                 code = CM_ERROR_INVAL;
5760
5761                 cm_LockMarkSCacheLost(scp);
5762             } else {
5763                 scp->serverLock = newLock;
5764             }
5765         }
5766
5767     pre_syncopdone:
5768         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5769     post_syncopdone:
5770         ;
5771     }
5772
5773   handleCode:
5774     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5775         lock_ObtainWrite(&cm_scacheLock);
5776         if (scp->fileLocksT == &oldFileLock->fileq)
5777             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5778         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5779         lock_ReleaseWrite(&cm_scacheLock);
5780     }
5781     lock_ReleaseWrite(&scp->rw);
5782
5783   updateLock:
5784     lock_ObtainWrite(&cm_scacheLock);
5785     if (code == 0) {
5786         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5787     } else if (code != CM_ERROR_WOULDBLOCK) {
5788         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5789         cm_ReleaseUser(oldFileLock->userp);
5790         oldFileLock->userp = NULL;
5791         if (oldFileLock->scp) {
5792             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5793             oldFileLock->scp = NULL;
5794         }
5795     }
5796     lock_ReleaseWrite(&cm_scacheLock);
5797
5798     return code;
5799 }
5800
5801 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5802 {
5803     cm_key_t key;
5804
5805     key.process_id = process_id;
5806     key.session_id = session_id;
5807     key.file_id = file_id;
5808
5809     return key;
5810 }
5811
5812 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5813 {
5814     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5815         ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5816 }
5817
5818 void cm_ReleaseAllLocks(void)
5819 {
5820     cm_scache_t *scp;
5821     cm_req_t req;
5822     cm_user_t *userp;
5823     cm_key_t   key;
5824     cm_file_lock_t *fileLock;
5825     unsigned int i;
5826
5827     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5828     {
5829         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5830             while (scp->fileLocksH != NULL) {
5831                 lock_ObtainWrite(&scp->rw);
5832                 lock_ObtainWrite(&cm_scacheLock);
5833                 if (!scp->fileLocksH) {
5834                     lock_ReleaseWrite(&cm_scacheLock);
5835                     lock_ReleaseWrite(&scp->rw);
5836                     break;
5837                 }
5838                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5839                 userp = fileLock->userp;
5840                 cm_HoldUser(userp);
5841                 key = fileLock->key;
5842                 cm_HoldSCacheNoLock(scp);
5843                 lock_ReleaseWrite(&cm_scacheLock);
5844                 cm_UnlockByKey(scp, key, 0, userp, &req);
5845                 cm_ReleaseSCache(scp);
5846                 cm_ReleaseUser(userp);
5847                 lock_ReleaseWrite(&scp->rw);
5848             }
5849         }
5850     }
5851 }