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