7f234de769a580c75b801edd052bd73126226c1d
[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     memset(&volSync, 0, sizeof(volSync));
4413
4414     tfid.Volume = scp->fid.volume;
4415     tfid.Vnode = scp->fid.vnode;
4416     tfid.Unique = scp->fid.unique;
4417     cfid = scp->fid;
4418
4419     lock_ReleaseWrite(&scp->rw);
4420
4421     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4422
4423     do {
4424         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4425         if (code)
4426             break;
4427
4428         rxconnp = cm_GetRxConn(connp);
4429         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4430         rx_PutConnection(rxconnp);
4431
4432     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4433                         NULL, NULL, code));
4434     code = cm_MapRPCError(code, reqp);
4435     if (code)
4436         osi_Log1(afsd_logp,
4437                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4438     else
4439         osi_Log0(afsd_logp,
4440                  "CALL ReleaseLock SUCCESS");
4441
4442     lock_ObtainWrite(&scp->rw);
4443
4444     return code;
4445 }
4446
4447 /* called with scp->rw held.  May release it during processing, but
4448    will exit with lock held.
4449
4450    This will return:
4451
4452    - 0 if the user has permission to get the specified lock for the scp
4453
4454    - CM_ERROR_NOACCESS if not
4455
4456    Any other error from cm_SyncOp will be sent down untranslated.
4457
4458    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4459    phas_insert (if non-NULL) will receive a boolean value indicating
4460    whether the user has INSERT permission or not.
4461 */
4462 long cm_LockCheckPerms(cm_scache_t * scp,
4463                        int lock_type,
4464                        cm_user_t * userp,
4465                        cm_req_t * reqp,
4466                        int * phas_insert)
4467 {
4468     long rights = 0;
4469     long code = 0, code2 = 0;
4470
4471     /* lock permissions are slightly tricky because of the 'i' bit.
4472        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4473        user has PRSFS_WRITE, she can write-lock the file.  However, if
4474        the user has PRSFS_INSERT, then she can write-lock new files,
4475        but not old ones.  Since we don't have information about
4476        whether a file is new or not, we assume that if the user owns
4477        the scp, then she has the permissions that are granted by
4478        PRSFS_INSERT. */
4479
4480     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4481              scp, lock_type, userp);
4482
4483     if (lock_type == LockRead)
4484         rights |= PRSFS_LOCK;
4485     else if (lock_type == LockWrite)
4486         rights |= PRSFS_WRITE | PRSFS_LOCK;
4487     else {
4488         /* hmmkay */
4489         osi_assertx(FALSE, "invalid lock type");
4490         return 0;
4491     }
4492
4493     if (phas_insert)
4494         *phas_insert = FALSE;
4495
4496     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4497                      CM_SCACHESYNC_GETSTATUS |
4498                      CM_SCACHESYNC_NEEDCALLBACK);
4499
4500     if (phas_insert && scp->creator == userp) {
4501
4502         /* If this file was created by the user, then we check for
4503            PRSFS_INSERT.  If the file server is recent enough, then
4504            this should be sufficient for her to get a write-lock (but
4505            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4506            indicates whether a file server supports getting write
4507            locks when the user only has PRSFS_INSERT.
4508
4509            If the file was not created by the user we skip the check
4510            because the INSERT bit will not apply to this user even
4511            if it is set.
4512          */
4513
4514         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4515                          CM_SCACHESYNC_GETSTATUS |
4516                          CM_SCACHESYNC_NEEDCALLBACK);
4517
4518         if (code2 == CM_ERROR_NOACCESS) {
4519             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4520         } else {
4521             *phas_insert = TRUE;
4522             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4523         }
4524     }
4525
4526     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4527
4528     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4529
4530     return code;
4531 }
4532
4533 /* called with scp->rw held */
4534 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4535              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4536              cm_key_t key,
4537              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4538              cm_file_lock_t **lockpp)
4539 {
4540     long code = 0;
4541     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4542     cm_file_lock_t *fileLock;
4543     osi_queue_t *q;
4544     cm_range_t range;
4545     int wait_unlock = FALSE;
4546     int force_client_lock = FALSE;
4547
4548     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4549              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4550     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4551              key.process_id, key.session_id, key.file_id);
4552
4553     /*
4554    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4555
4556    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4557       true:
4558
4559        3.1 If L->LockType is exclusive then there does NOT exist a
4560          ACCEPTED lock M in S->fileLocks such that _a_ in
4561          (M->LOffset,+M->LLength).
4562
4563        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4564          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4565          M->LockType is shared.
4566
4567    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4568
4569        4.1 M->key != Key(C)
4570
4571        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4572          and (M->LOffset,+M->LLength) do not intersect.
4573     */
4574
4575     range.offset = LOffset.QuadPart;
4576     range.length = LLength.QuadPart;
4577
4578     lock_ObtainRead(&cm_scacheLock);
4579
4580     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4581         fileLock =
4582             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4583
4584         if (IS_LOCK_LOST(fileLock)) {
4585             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4586                 code = CM_ERROR_BADFD;
4587                 break;
4588             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4589                 code = CM_ERROR_WOULDBLOCK;
4590                 wait_unlock = TRUE;
4591                 break;
4592             }
4593         }
4594
4595         /* we don't need to check for deleted locks here since deleted
4596            locks are dequeued from scp->fileLocks */
4597         if (IS_LOCK_ACCEPTED(fileLock) &&
4598            INTERSECT_RANGE(range, fileLock->range)) {
4599
4600             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4601                 fileLock->lockType != LockRead) {
4602                 wait_unlock = TRUE;
4603                 code = CM_ERROR_WOULDBLOCK;
4604                 break;
4605             }
4606         }
4607     }
4608
4609     lock_ReleaseRead(&cm_scacheLock);
4610
4611     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4612         if (Which == scp->serverLock ||
4613            (Which == LockRead && scp->serverLock == LockWrite)) {
4614
4615             int has_insert = 0;
4616
4617             /* we already have the lock we need */
4618             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4619                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4620
4621             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4622
4623             /* special case: if we don't have permission to read-lock
4624                the file, then we force a clientside lock.  This is to
4625                compensate for applications that obtain a read-lock for
4626                reading files off of directories that don't grant
4627                read-locks to the user. */
4628             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4629
4630                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4631                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4632                     code = 0;
4633                 } else {
4634                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4635                     force_client_lock = TRUE;
4636                 }
4637             }
4638
4639         } else if ((scp->exclusiveLocks > 0) ||
4640                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4641             int has_insert = 0;
4642
4643             /* We are already waiting for some other lock.  We should
4644                wait for the daemon to catch up instead of generating a
4645                flood of SetLock calls. */
4646             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4647                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4648
4649             /* see if we have permission to create the lock in the
4650                first place. */
4651             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4652             if (code == 0)
4653                 code = CM_ERROR_WOULDBLOCK;
4654             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4655
4656                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4657                     osi_Log0(afsd_logp,
4658                              "   User has no read-lock perms, but has INSERT perms.");
4659                     code = CM_ERROR_WOULDBLOCK;
4660                 } else {
4661                     osi_Log0(afsd_logp,
4662                              "   User has no read-lock perms. Forcing client-side lock");
4663                     force_client_lock = TRUE;
4664                 }
4665             }
4666
4667             /* leave any other codes as-is */
4668
4669         } else {
4670             int newLock;
4671             int check_data_version = FALSE;
4672             int has_insert = 0;
4673
4674             /* first check if we have permission to elevate or obtain
4675                the lock. */
4676             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4677             if (code) {
4678                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4679                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4680                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4681                     force_client_lock = TRUE;
4682                 }
4683                 goto check_code;
4684             }
4685
4686             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4687
4688             if (scp->serverLock == LockRead && Which == LockWrite) {
4689
4690                 /* We want to escalate the lock to a LockWrite.
4691                  * Unfortunately that's not really possible without
4692                  * letting go of the current lock.  But for now we do
4693                  * it anyway. */
4694
4695                 osi_Log0(afsd_logp,
4696                          "   attempting to UPGRADE from LockRead to LockWrite.");
4697                 osi_Log1(afsd_logp,
4698                          "   dataVersion on scp: %I64d", scp->dataVersion);
4699
4700                 /* we assume at this point (because scp->serverLock
4701                    was valid) that we had a valid server lock. */
4702                 scp->lockDataVersion = scp->dataVersion;
4703                 check_data_version = TRUE;
4704
4705                 code = cm_IntReleaseLock(scp, userp, reqp);
4706
4707                 if (code) {
4708                     /* We couldn't release the lock */
4709                     goto check_code;
4710                 } else {
4711                     scp->serverLock = -1;
4712                 }
4713             }
4714
4715             /* We need to obtain a server lock of type Which in order
4716              * to assert this file lock */
4717 #ifndef AGGRESSIVE_LOCKS
4718             newLock = Which;
4719 #else
4720             newLock = LockWrite;
4721 #endif
4722
4723             code = cm_IntSetLock(scp, userp, newLock, reqp);
4724
4725 #ifdef AGGRESSIVE_LOCKS
4726             if ((code == CM_ERROR_WOULDBLOCK ||
4727                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4728                 /* we wanted LockRead.  We tried LockWrite. Now try
4729                  * LockRead again */
4730                 newLock = Which;
4731
4732                 /* am I sane? */
4733                 osi_assertx(newLock == LockRead, "lock type not read");
4734
4735                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4736             }
4737 #endif
4738
4739             if (code == CM_ERROR_NOACCESS) {
4740                 if (Which == LockRead) {
4741                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4742                         long tcode;
4743                         /* We requested a read-lock, but we have permission to
4744                          * get a write-lock. Try that */
4745
4746                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4747
4748                         if (tcode == 0) {
4749                             newLock = LockWrite;
4750
4751                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4752
4753                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4754                         }
4755                     } else {
4756                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4757                         force_client_lock = TRUE;
4758                     }
4759                 } else if (Which == LockWrite &&
4760                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4761                     long tcode;
4762
4763                     /* Special case: if the lock request was for a
4764                      * LockWrite and the user owns the file and we weren't
4765                      * allowed to obtain the serverlock, we either lost a
4766                      * race (the permissions changed from under us), or we
4767                      * have 'i' bits, but we aren't allowed to lock the
4768                      * file. */
4769
4770                     /* check if we lost a race... */
4771                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4772
4773                     if (tcode == 0) {
4774                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4775                         force_client_lock = TRUE;
4776                     }
4777                 }
4778             }
4779
4780             if (code == 0 && check_data_version &&
4781                scp->dataVersion != scp->lockDataVersion) {
4782                 /* We lost a race.  Although we successfully obtained
4783                  * a lock, someone modified the file in between.  The
4784                  * locks have all been technically lost. */
4785
4786                 osi_Log0(afsd_logp,
4787                          "  Data version mismatch while upgrading lock.");
4788                 osi_Log2(afsd_logp,
4789                          "  Data versions before=%I64d, after=%I64d",
4790                          scp->lockDataVersion,
4791                          scp->dataVersion);
4792                 osi_Log1(afsd_logp,
4793                          "  Releasing stale lock for scp 0x%x", scp);
4794
4795                 code = cm_IntReleaseLock(scp, userp, reqp);
4796
4797                 scp->serverLock = -1;
4798
4799                 code = CM_ERROR_INVAL;
4800             } else if (code == 0) {
4801                 scp->serverLock = newLock;
4802                 scp->lockDataVersion = scp->dataVersion;
4803             }
4804
4805             if (code != 0 &&
4806                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4807                 scp->serverLock == -1) {
4808                 /* Oops. We lost the lock. */
4809                 cm_LockMarkSCacheLost(scp);
4810             }
4811         }
4812     } else if (code == 0) {     /* server locks not enabled */
4813         osi_Log0(afsd_logp,
4814                  "  Skipping server lock for scp");
4815     }
4816
4817  check_code:
4818
4819     if (code != 0 && !force_client_lock) {
4820         /* Special case error translations
4821
4822            Applications don't expect certain errors from a
4823            LockFile/UnlockFile call.  We need to translate some error
4824            code to codes that apps expect and handle. */
4825
4826         /* We shouldn't actually need to handle this case since we
4827            simulate locks for RO scps anyway. */
4828         if (code == CM_ERROR_READONLY) {
4829             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4830             code = CM_ERROR_NOACCESS;
4831         }
4832     }
4833
4834     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4835         force_client_lock) {
4836
4837         /* clear the error if we are forcing a client lock, so we
4838            don't get confused later. */
4839         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4840             code = 0;
4841
4842         lock_ObtainWrite(&cm_scacheLock);
4843         fileLock = cm_GetFileLock();
4844         lock_ReleaseWrite(&cm_scacheLock);
4845 #ifdef DEBUG
4846         fileLock->fid = scp->fid;
4847 #endif
4848         fileLock->key = key;
4849         fileLock->lockType = Which;
4850         cm_HoldUser(userp);
4851         fileLock->userp = userp;
4852         fileLock->range = range;
4853         fileLock->flags = (code == 0 ? 0 :
4854                            ((wait_unlock)?
4855                             CM_FILELOCK_FLAG_WAITUNLOCK :
4856                             CM_FILELOCK_FLAG_WAITLOCK));
4857
4858         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4859             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4860
4861         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4862
4863         lock_ObtainWrite(&cm_scacheLock);
4864         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4865         cm_HoldSCacheNoLock(scp);
4866         fileLock->scp = scp;
4867         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4868         lock_ReleaseWrite(&cm_scacheLock);
4869
4870         if (code != 0) {
4871             *lockpp = fileLock;
4872         }
4873
4874         if (IS_LOCK_CLIENTONLY(fileLock)) {
4875             scp->clientLocks++;
4876         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4877             if (Which == LockRead)
4878                 scp->sharedLocks++;
4879             else
4880                 scp->exclusiveLocks++;
4881         }
4882
4883         osi_Log3(afsd_logp,
4884                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4885                  fileLock, fileLock->flags, scp);
4886         osi_Log4(afsd_logp,
4887                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4888                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4889                  (int)(signed char) scp->serverLock);
4890     } else {
4891         osi_Log1(afsd_logp,
4892                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4893     }
4894
4895     /* Convert from would block to lock not granted */
4896     if (code == CM_ERROR_WOULDBLOCK)
4897         code = CM_ERROR_LOCK_NOT_GRANTED;
4898
4899     return code;
4900 }
4901
4902 static long
4903 cm_IntUnlock(cm_scache_t * scp,
4904              cm_user_t * userp,
4905              cm_req_t *  reqp)
4906 {
4907     long code = 0;
4908
4909     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4910     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4911     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4912
4913     if (!SERVERLOCKS_ENABLED(scp)) {
4914         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4915         goto done;
4916     }
4917
4918     /* Ideally we would go through the rest of the locks to determine
4919      * if one or more locks that were formerly in WAITUNLOCK can now
4920      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4921      * scp->sharedLocks accordingly.  However, the retrying of locks
4922      * in that manner is done cm_RetryLock() manually.
4923      */
4924
4925     if (scp->serverLock == LockWrite &&
4926         scp->exclusiveLocks == 0 &&
4927         scp->sharedLocks > 0) {
4928         /* The serverLock should be downgraded to LockRead */
4929         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
4930
4931         /* Make sure there are no dirty buffers left. */
4932         code = cm_FSync(scp, userp, reqp, TRUE);
4933
4934         /* since scp->serverLock looked sane, we are going to assume
4935            that we have a valid server lock. */
4936         scp->lockDataVersion = scp->dataVersion;
4937         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
4938
4939         /* before we downgrade, make sure that we have enough
4940            permissions to get the read lock. */
4941         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
4942         if (code != 0) {
4943
4944             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
4945
4946             code = 0;
4947             goto done;
4948         }
4949
4950         code = cm_IntReleaseLock(scp, userp, reqp);
4951
4952         if (code) {
4953             /* so we couldn't release it.  Just let the lock be for now */
4954             code = 0;
4955             goto done;
4956         } else {
4957             scp->serverLock = -1;
4958         }
4959
4960         code = cm_IntSetLock(scp, userp, LockRead, reqp);
4961
4962         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
4963             scp->serverLock = LockRead;
4964         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
4965             /* We lost a race condition.  Although we have a valid
4966                lock on the file, the data has changed and essentially
4967                we have lost the lock we had during the transition. */
4968
4969             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
4970             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
4971                      scp->lockDataVersion,
4972                      scp->dataVersion);
4973
4974             code = cm_IntReleaseLock(scp, userp, reqp);
4975
4976             code = CM_ERROR_INVAL;
4977             scp->serverLock = -1;
4978         }
4979
4980         if (code != 0 &&
4981             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4982                 (scp->serverLock == -1)) {
4983                 /* Oopsie */
4984                 cm_LockMarkSCacheLost(scp);
4985             }
4986
4987         /* failure here has no bearing on the return value of cm_Unlock() */
4988         code = 0;
4989
4990     } else if (scp->serverLock != (-1) &&
4991               scp->exclusiveLocks == 0 &&
4992               scp->sharedLocks == 0) {
4993         /* The serverLock should be released entirely */
4994
4995         if (scp->serverLock == LockWrite) {
4996             osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
4997
4998             /* Make sure there are no dirty buffers left. */
4999             code = cm_FSync(scp, userp, reqp, TRUE);
5000         } else {
5001             osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
5002         }
5003
5004         code = cm_IntReleaseLock(scp, userp, reqp);
5005
5006         if (code == 0)
5007             scp->serverLock = (-1);
5008     }
5009
5010   done:
5011     return code;
5012 }
5013 /* Called with scp->rw held */
5014 long cm_UnlockByKey(cm_scache_t * scp,
5015                     cm_key_t key,
5016                     afs_uint32 flags,
5017                     cm_user_t * userp,
5018                     cm_req_t * reqp)
5019 {
5020     long code = 0;
5021     cm_file_lock_t *fileLock;
5022     osi_queue_t *q, *qn;
5023     int n_unlocks = 0;
5024
5025     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5026              scp, key.process_id, key.session_id, key.file_id);
5027     osi_Log1(afsd_logp, "    flags=0x%x", flags);
5028
5029     lock_ObtainWrite(&cm_scacheLock);
5030
5031     for (q = scp->fileLocksH; q; q = qn) {
5032         qn = osi_QNext(q);
5033
5034         fileLock = (cm_file_lock_t *)
5035             ((char *) q - offsetof(cm_file_lock_t, fileq));
5036
5037 #ifdef DEBUG
5038         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
5039                  fileLock,
5040                  (unsigned long) fileLock->range.offset,
5041                  (unsigned long) fileLock->range.length,
5042                 fileLock->lockType);
5043         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5044                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5045                  fileLock->flags);
5046
5047         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5048             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5049             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5050                      fileLock->fid.cell,
5051                      fileLock->fid.volume,
5052                      fileLock->fid.vnode,
5053                      fileLock->fid.unique);
5054             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5055                      fileLock->scp->fid.cell,
5056                      fileLock->scp->fid.volume,
5057                      fileLock->scp->fid.vnode,
5058                      fileLock->scp->fid.unique);
5059             osi_assertx(FALSE, "invalid fid value");
5060         }
5061 #endif
5062
5063         if (!IS_LOCK_DELETED(fileLock) &&
5064             cm_KeyEquals(&fileLock->key, &key, flags)) {
5065             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5066                     fileLock->range.offset,
5067                     fileLock->range.length,
5068                     fileLock->lockType);
5069
5070             if (scp->fileLocksT == q)
5071                 scp->fileLocksT = osi_QPrev(q);
5072             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5073
5074             if (IS_LOCK_CLIENTONLY(fileLock)) {
5075                 scp->clientLocks--;
5076             } else if (IS_LOCK_ACCEPTED(fileLock)) {
5077                 if (fileLock->lockType == LockRead)
5078                     scp->sharedLocks--;
5079                 else
5080                     scp->exclusiveLocks--;
5081             }
5082
5083             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5084
5085             cm_ReleaseUser(fileLock->userp);
5086             cm_ReleaseSCacheNoLock(scp);
5087
5088             fileLock->userp = NULL;
5089             fileLock->scp = NULL;
5090
5091             n_unlocks++;
5092         }
5093     }
5094
5095     lock_ReleaseWrite(&cm_scacheLock);
5096
5097     if (n_unlocks == 0) {
5098         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5099         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5100                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5101
5102         return 0;
5103     }
5104
5105     osi_Log1(afsd_logp, "cm_UnlockByKey done with %d locks", n_unlocks);
5106
5107     code = cm_IntUnlock(scp, userp, reqp);
5108
5109     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5110     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5111              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5112              (int)(signed char) scp->serverLock);
5113
5114     return code;
5115 }
5116
5117 long cm_Unlock(cm_scache_t *scp,
5118                unsigned char sLockType,
5119                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5120                cm_key_t key,
5121                afs_uint32 flags,
5122                cm_user_t *userp,
5123                cm_req_t *reqp)
5124 {
5125     long code = 0;
5126     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5127     cm_file_lock_t *fileLock;
5128     osi_queue_t *q;
5129     int release_userp = FALSE;
5130     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5131     int lock_found  = 0;
5132     LARGE_INTEGER RangeEnd;
5133
5134     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5135              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5136     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5137              key.process_id, key.session_id, key.file_id, flags);
5138
5139     if (!exact_match)
5140         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5141
5142   try_again:
5143     lock_ObtainRead(&cm_scacheLock);
5144
5145     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5146         fileLock = (cm_file_lock_t *)
5147             ((char *) q - offsetof(cm_file_lock_t, fileq));
5148
5149 #ifdef DEBUG
5150         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5151             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5152             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5153                      fileLock->fid.cell,
5154                      fileLock->fid.volume,
5155                      fileLock->fid.vnode,
5156                      fileLock->fid.unique);
5157             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5158                      fileLock->scp->fid.cell,
5159                      fileLock->scp->fid.volume,
5160                      fileLock->scp->fid.vnode,
5161                      fileLock->scp->fid.unique);
5162             osi_assertx(FALSE, "invalid fid value");
5163         }
5164 #endif
5165         if (exact_match) {
5166             if (!IS_LOCK_DELETED(fileLock) &&
5167                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5168                  fileLock->range.offset == LOffset.QuadPart &&
5169                  fileLock->range.length == LLength.QuadPart) {
5170                 lock_found = 1;
5171                 break;
5172             }
5173         } else {
5174
5175             if (!IS_LOCK_DELETED(fileLock) &&
5176                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5177                  fileLock->range.offset >= LOffset.QuadPart &&
5178                  fileLock->range.offset < RangeEnd.QuadPart &&
5179                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5180                 lock_found = 1;
5181                 break;
5182             }
5183         }
5184     }
5185
5186     if (!q) {
5187         lock_ReleaseRead(&cm_scacheLock);
5188
5189         if (lock_found && !exact_match) {
5190             code = 0;
5191             goto done;
5192         } else {
5193             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5194
5195             /* The lock didn't exist anyway. *shrug* */
5196             return CM_ERROR_RANGE_NOT_LOCKED;
5197         }
5198     }
5199
5200     /* discard lock record */
5201     lock_ConvertRToW(&cm_scacheLock);
5202     if (scp->fileLocksT == q)
5203         scp->fileLocksT = osi_QPrev(q);
5204     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5205
5206     /*
5207      * Don't delete it here; let the daemon delete it, to simplify
5208      * the daemon's traversal of the list.
5209      */
5210
5211     if (IS_LOCK_CLIENTONLY(fileLock)) {
5212         scp->clientLocks--;
5213     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5214         if (fileLock->lockType == LockRead)
5215             scp->sharedLocks--;
5216         else
5217             scp->exclusiveLocks--;
5218     }
5219
5220     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5221     if (userp != NULL) {
5222         cm_ReleaseUser(fileLock->userp);
5223     } else {
5224         userp = fileLock->userp;
5225         release_userp = TRUE;
5226     }
5227     fileLock->userp = NULL;
5228     cm_ReleaseSCacheNoLock(scp);
5229     fileLock->scp = NULL;
5230     lock_ReleaseWrite(&cm_scacheLock);
5231
5232     code = cm_IntUnlock(scp, userp, reqp);
5233
5234     if (release_userp) {
5235         cm_ReleaseUser(userp);
5236         release_userp = FALSE;
5237     }
5238
5239     if (!exact_match) {
5240         osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5241         goto try_again;         /* might be more than one lock in the range */
5242     }
5243
5244  done:
5245
5246     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5247     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5248              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5249              (int)(signed char) scp->serverLock);
5250
5251     return code;
5252 }
5253
5254 /* called with scp->rw held */
5255 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5256 {
5257     cm_file_lock_t *fileLock;
5258     osi_queue_t *q;
5259
5260     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5261
5262     /* cm_scacheLock needed because we are modifying fileLock->flags */
5263     lock_ObtainWrite(&cm_scacheLock);
5264
5265     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5266         fileLock =
5267             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5268
5269         if (IS_LOCK_ACTIVE(fileLock) &&
5270             !IS_LOCK_CLIENTONLY(fileLock)) {
5271             if (fileLock->lockType == LockRead)
5272                 scp->sharedLocks--;
5273             else
5274                 scp->exclusiveLocks--;
5275
5276             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5277         }
5278     }
5279
5280     scp->serverLock = -1;
5281     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5282     lock_ReleaseWrite(&cm_scacheLock);
5283 }
5284
5285 /* Called with no relevant locks held */
5286 void cm_CheckLocks()
5287 {
5288     osi_queue_t *q, *nq;
5289     cm_file_lock_t *fileLock;
5290     cm_req_t req;
5291     AFSFid tfid;
5292     AFSVolSync volSync;
5293     cm_conn_t *connp;
5294     long code;
5295     struct rx_connection * rxconnp;
5296     cm_scache_t * scp;
5297
5298     memset(&volSync, 0, sizeof(volSync));
5299
5300     cm_InitReq(&req);
5301
5302     lock_ObtainWrite(&cm_scacheLock);
5303
5304     cm_lockRefreshCycle++;
5305
5306     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5307
5308     for (q = cm_allFileLocks; q; q = nq) {
5309         fileLock = (cm_file_lock_t *) q;
5310         nq = osi_QNext(q);
5311         code = -1;
5312
5313         if (IS_LOCK_DELETED(fileLock)) {
5314
5315             osi_QRemove(&cm_allFileLocks, q);
5316             cm_PutFileLock(fileLock);
5317
5318         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5319
5320             /* Server locks must have been enabled for us to have
5321                received an active non-client-only lock. */
5322             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5323
5324             scp = fileLock->scp;
5325             osi_assertx(scp != NULL, "null cm_scache_t");
5326
5327             cm_HoldSCacheNoLock(scp);
5328
5329 #ifdef DEBUG
5330             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5331                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5332                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5333                          fileLock->fid.cell,
5334                          fileLock->fid.volume,
5335                          fileLock->fid.vnode,
5336                          fileLock->fid.unique);
5337                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5338                          fileLock->scp->fid.cell,
5339                          fileLock->scp->fid.volume,
5340                          fileLock->scp->fid.vnode,
5341                          fileLock->scp->fid.unique);
5342                 osi_assertx(FALSE, "invalid fid");
5343             }
5344 #endif
5345             /* Server locks are extended once per scp per refresh
5346                cycle. */
5347             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5348
5349                 int scp_done = FALSE;
5350
5351                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5352
5353                 lock_ReleaseWrite(&cm_scacheLock);
5354                 lock_ObtainWrite(&scp->rw);
5355
5356                 /* did the lock change while we weren't holding the lock? */
5357                 if (!IS_LOCK_ACTIVE(fileLock))
5358                     goto post_syncopdone;
5359
5360                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5361                                  CM_SCACHESYNC_NEEDCALLBACK
5362                                  | CM_SCACHESYNC_GETSTATUS
5363                                  | CM_SCACHESYNC_LOCK);
5364
5365                 if (code) {
5366                     osi_Log1(afsd_logp,
5367                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5368                     goto post_syncopdone;
5369                 }
5370
5371                 /* cm_SyncOp releases scp->rw during which the lock
5372                    may get released. */
5373                 if (!IS_LOCK_ACTIVE(fileLock))
5374                     goto pre_syncopdone;
5375
5376                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5377                     cm_fid_t cfid;
5378                     cm_user_t * userp;
5379
5380                     tfid.Volume = scp->fid.volume;
5381                     tfid.Vnode = scp->fid.vnode;
5382                     tfid.Unique = scp->fid.unique;
5383                     cfid = scp->fid;
5384                     userp = fileLock->userp;
5385
5386                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5387                              fileLock,
5388                              scp,
5389                              (int) scp->serverLock);
5390
5391                     lock_ReleaseWrite(&scp->rw);
5392
5393                     do {
5394                         code = cm_ConnFromFID(&cfid, userp,
5395                                        &req, &connp);
5396                         if (code)
5397                             break;
5398
5399                         rxconnp = cm_GetRxConn(connp);
5400                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5401                                                 &volSync);
5402                         rx_PutConnection(rxconnp);
5403
5404                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5405
5406                     } while (cm_Analyze(connp, userp, &req,
5407                                         &cfid, &volSync, NULL, NULL,
5408                                         code));
5409
5410                     code = cm_MapRPCError(code, &req);
5411
5412                     lock_ObtainWrite(&scp->rw);
5413
5414                     if (code) {
5415                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5416                     } else {
5417                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5418                         scp->lockDataVersion = scp->dataVersion;
5419                     }
5420
5421                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5422                         scp->lockDataVersion == scp->dataVersion) {
5423                         int lockType;
5424
5425                         lockType =
5426                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5427
5428                         /* we might still have a chance to obtain a
5429                            new lock */
5430
5431                         code = cm_IntSetLock(scp, userp, lockType, &req);
5432
5433                         if (code) {
5434                             code = CM_ERROR_INVAL;
5435                         } else if (scp->lockDataVersion != scp->dataVersion) {
5436
5437                             /* now check if we still have the file at
5438                                the right data version. */
5439                             osi_Log1(afsd_logp,
5440                                      "Data version mismatch on scp 0x%p",
5441                                      scp);
5442                             osi_Log2(afsd_logp,
5443                                      "   Data versions: before=%I64d, after=%I64d",
5444                                      scp->lockDataVersion,
5445                                      scp->dataVersion);
5446
5447                             code = cm_IntReleaseLock(scp, userp, &req);
5448
5449                             code = CM_ERROR_INVAL;
5450                         }
5451                     }
5452
5453                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5454                         code == CM_ERROR_BADFD) {
5455                         cm_LockMarkSCacheLost(scp);
5456                     }
5457
5458                 } else {
5459                     /* interestingly, we have found an active lock
5460                        belonging to an scache that has no
5461                        serverLock */
5462                     cm_LockMarkSCacheLost(scp);
5463                 }
5464
5465                 scp_done = TRUE;
5466
5467             pre_syncopdone:
5468
5469                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5470
5471             post_syncopdone:
5472                 lock_ReleaseWrite(&scp->rw);
5473
5474                 lock_ObtainWrite(&cm_scacheLock);
5475
5476                 if (code == 0) {
5477                     fileLock->lastUpdate = time(NULL);
5478                 }
5479
5480                 if (scp_done)
5481                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5482
5483             } else {
5484                 /* we have already refreshed the locks on this scp */
5485                 fileLock->lastUpdate = time(NULL);
5486             }
5487
5488             cm_ReleaseSCacheNoLock(scp);
5489
5490         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5491             /* TODO: Check callbacks */
5492         }
5493     }
5494
5495     lock_ReleaseWrite(&cm_scacheLock);
5496     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5497 }
5498
5499 /* NOT called with scp->rw held. */
5500 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5501 {
5502     long code = 0;
5503     cm_scache_t *scp = NULL;
5504     cm_file_lock_t *fileLock;
5505     osi_queue_t *q;
5506     cm_req_t req;
5507     int newLock = -1;
5508     int force_client_lock = FALSE;
5509     int has_insert = FALSE;
5510     int check_data_version = FALSE;
5511
5512     cm_InitReq(&req);
5513
5514     if (client_is_dead) {
5515         code = CM_ERROR_TIMEDOUT;
5516         goto updateLock;
5517     }
5518
5519     lock_ObtainRead(&cm_scacheLock);
5520
5521     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5522     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5523              (unsigned)(oldFileLock->range.offset >> 32),
5524              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5525              (unsigned)(oldFileLock->range.length >> 32),
5526              (unsigned)(oldFileLock->range.length & 0xffffffff));
5527     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5528              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5529              (unsigned)(oldFileLock->flags));
5530
5531     /* if the lock has already been granted, then we have nothing to do */
5532     if (IS_LOCK_ACTIVE(oldFileLock)) {
5533         lock_ReleaseRead(&cm_scacheLock);
5534         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5535         return 0;
5536     }
5537
5538     /* we can't do anything with lost or deleted locks at the moment. */
5539     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5540         code = CM_ERROR_BADFD;
5541         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5542         lock_ReleaseRead(&cm_scacheLock);
5543         goto updateLock;
5544     }
5545
5546     scp = oldFileLock->scp;
5547
5548     osi_assertx(scp != NULL, "null cm_scache_t");
5549
5550     lock_ReleaseRead(&cm_scacheLock);
5551     lock_ObtainWrite(&scp->rw);
5552
5553     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5554                              oldFileLock->userp,
5555                              &req, &has_insert);
5556
5557     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5558         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5559         force_client_lock = TRUE;
5560         }
5561         code = 0;
5562     } else if (code) {
5563         lock_ReleaseWrite(&scp->rw);
5564         return code;
5565     }
5566
5567     lock_ObtainWrite(&cm_scacheLock);
5568
5569     /* Check if we already have a sufficient server lock to allow this
5570        lock to go through. */
5571     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5572         (!SERVERLOCKS_ENABLED(scp) ||
5573          scp->serverLock == oldFileLock->lockType ||
5574          scp->serverLock == LockWrite)) {
5575
5576         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5577
5578         if (SERVERLOCKS_ENABLED(scp)) {
5579             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5580                      (int) scp->serverLock);
5581         } else {
5582             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5583         }
5584
5585         lock_ReleaseWrite(&cm_scacheLock);
5586         lock_ReleaseWrite(&scp->rw);
5587
5588         return 0;
5589     }
5590
5591     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5592
5593         /* check if the conflicting locks have dissappeared already */
5594         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5595
5596             fileLock = (cm_file_lock_t *)
5597                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5598
5599             if (IS_LOCK_LOST(fileLock)) {
5600                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5601                     code = CM_ERROR_BADFD;
5602                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5603                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5604                              fileLock);
5605                     break;
5606                 } else if (fileLock->lockType == LockWrite &&
5607                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5608                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5609                     code = CM_ERROR_WOULDBLOCK;
5610                     break;
5611                 }
5612             }
5613
5614             if (IS_LOCK_ACCEPTED(fileLock) &&
5615                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5616
5617                 if (oldFileLock->lockType != LockRead ||
5618                    fileLock->lockType != LockRead) {
5619
5620                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5621                     code = CM_ERROR_WOULDBLOCK;
5622                     break;
5623                 }
5624             }
5625         }
5626     }
5627
5628     if (code != 0) {
5629         lock_ReleaseWrite(&cm_scacheLock);
5630         lock_ReleaseWrite(&scp->rw);
5631
5632         goto handleCode;
5633     }
5634
5635     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5636        If it is WAITUNLOCK, then we didn't find any conflicting lock
5637        but we haven't verfied whether the serverLock is sufficient to
5638        assert it.  If it is WAITLOCK, then the serverLock is
5639        insufficient to assert it. Eitherway, we are ready to accept
5640        the lock as either ACTIVE or WAITLOCK depending on the
5641        serverLock. */
5642
5643     /* First, promote the WAITUNLOCK to a WAITLOCK */
5644     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5645         if (oldFileLock->lockType == LockRead)
5646             scp->sharedLocks++;
5647         else
5648             scp->exclusiveLocks++;
5649
5650         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5651         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5652     }
5653
5654     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5655
5656     if (force_client_lock ||
5657         !SERVERLOCKS_ENABLED(scp) ||
5658         scp->serverLock == oldFileLock->lockType ||
5659         (oldFileLock->lockType == LockRead &&
5660          scp->serverLock == LockWrite)) {
5661
5662         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5663
5664         if ((force_client_lock ||
5665              !SERVERLOCKS_ENABLED(scp)) &&
5666             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5667
5668             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5669
5670             if (oldFileLock->lockType == LockRead)
5671                 scp->sharedLocks--;
5672             else
5673                 scp->exclusiveLocks--;
5674
5675             scp->clientLocks++;
5676         }
5677
5678         lock_ReleaseWrite(&cm_scacheLock);
5679         lock_ReleaseWrite(&scp->rw);
5680
5681         return 0;
5682
5683     } else {
5684         cm_user_t * userp;
5685
5686         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5687                          CM_SCACHESYNC_NEEDCALLBACK
5688                          | CM_SCACHESYNC_GETSTATUS
5689                          | CM_SCACHESYNC_LOCK);
5690         if (code) {
5691             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5692             lock_ReleaseWrite(&cm_scacheLock);
5693             goto post_syncopdone;
5694         }
5695
5696         if (!IS_LOCK_WAITLOCK(oldFileLock))
5697             goto pre_syncopdone;
5698
5699         userp = oldFileLock->userp;
5700
5701 #ifndef AGGRESSIVE_LOCKS
5702         newLock = oldFileLock->lockType;
5703 #else
5704         newLock = LockWrite;
5705 #endif
5706
5707         if (has_insert) {
5708             /* if has_insert is non-zero, then:
5709                - the lock a LockRead
5710                - we don't have permission to get a LockRead
5711                - we do have permission to get a LockWrite
5712                - the server supports VICED_CAPABILITY_WRITELOCKACL
5713             */
5714
5715             newLock = LockWrite;
5716         }
5717
5718         lock_ReleaseWrite(&cm_scacheLock);
5719
5720         /* when we get here, either we have a read-lock and want a
5721            write-lock or we don't have any locks and we want some
5722            lock. */
5723
5724         if (scp->serverLock == LockRead) {
5725
5726             osi_assertx(newLock == LockWrite, "!LockWrite");
5727
5728             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5729
5730             scp->lockDataVersion = scp->dataVersion;
5731             check_data_version = TRUE;
5732
5733             code = cm_IntReleaseLock(scp, userp, &req);
5734
5735             if (code)
5736                 goto pre_syncopdone;
5737             else
5738                 scp->serverLock = -1;
5739         }
5740
5741         code = cm_IntSetLock(scp, userp, newLock, &req);
5742
5743         if (code == 0) {
5744             if (scp->dataVersion != scp->lockDataVersion) {
5745                 /* we lost a race.  too bad */
5746
5747                 osi_Log0(afsd_logp,
5748                          "  Data version mismatch while upgrading lock.");
5749                 osi_Log2(afsd_logp,
5750                          "  Data versions before=%I64d, after=%I64d",
5751                          scp->lockDataVersion,
5752                          scp->dataVersion);
5753                 osi_Log1(afsd_logp,
5754                          "  Releasing stale lock for scp 0x%x", scp);
5755
5756                 code = cm_IntReleaseLock(scp, userp, &req);
5757
5758                 scp->serverLock = -1;
5759
5760                 code = CM_ERROR_INVAL;
5761
5762                 cm_LockMarkSCacheLost(scp);
5763             } else {
5764                 scp->serverLock = newLock;
5765             }
5766         }
5767
5768     pre_syncopdone:
5769         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5770     post_syncopdone:
5771         ;
5772     }
5773
5774   handleCode:
5775     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5776         lock_ObtainWrite(&cm_scacheLock);
5777         if (scp->fileLocksT == &oldFileLock->fileq)
5778             scp->fileLocksT = osi_QPrev(&oldFileLock->fileq);
5779         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5780         lock_ReleaseWrite(&cm_scacheLock);
5781     }
5782     lock_ReleaseWrite(&scp->rw);
5783
5784   updateLock:
5785     lock_ObtainWrite(&cm_scacheLock);
5786     if (code == 0) {
5787         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5788     } else if (code != CM_ERROR_WOULDBLOCK) {
5789         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5790         cm_ReleaseUser(oldFileLock->userp);
5791         oldFileLock->userp = NULL;
5792         if (oldFileLock->scp) {
5793             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5794             oldFileLock->scp = NULL;
5795         }
5796     }
5797     lock_ReleaseWrite(&cm_scacheLock);
5798
5799     return code;
5800 }
5801
5802 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5803 {
5804     cm_key_t key;
5805
5806     key.process_id = process_id;
5807     key.session_id = session_id;
5808     key.file_id = file_id;
5809
5810     return key;
5811 }
5812
5813 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5814 {
5815     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5816         ((flags & CM_UNLOCK_BY_FID) || (k1->process_id == k2->process_id));
5817 }
5818
5819 void cm_ReleaseAllLocks(void)
5820 {
5821     cm_scache_t *scp;
5822     cm_req_t req;
5823     cm_user_t *userp;
5824     cm_key_t   key;
5825     cm_file_lock_t *fileLock;
5826     unsigned int i;
5827
5828     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5829     {
5830         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5831             while (scp->fileLocksH != NULL) {
5832                 lock_ObtainWrite(&scp->rw);
5833                 lock_ObtainWrite(&cm_scacheLock);
5834                 if (!scp->fileLocksH) {
5835                     lock_ReleaseWrite(&cm_scacheLock);
5836                     lock_ReleaseWrite(&scp->rw);
5837                     break;
5838                 }
5839                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5840                 userp = fileLock->userp;
5841                 cm_HoldUser(userp);
5842                 key = fileLock->key;
5843                 cm_HoldSCacheNoLock(scp);
5844                 lock_ReleaseWrite(&cm_scacheLock);
5845                 cm_UnlockByKey(scp, key, 0, userp, &req);
5846                 cm_ReleaseSCache(scp);
5847                 cm_ReleaseUser(userp);
5848                 lock_ReleaseWrite(&scp->rw);
5849             }
5850         }
5851     }
5852 }