Windows: Redirector interface for afsd_service.exe
[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     code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &newScp);
3590     if (code == 0) {
3591         /* found a matching object with the new name */
3592         if (cm_FidCmp(&oldScp->fid, &newScp->fid)) {
3593             /* and they don't match so return an error */
3594             osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3595                       newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3596             code = CM_ERROR_EXISTS;
3597         }
3598         cm_ReleaseSCache(newScp);
3599         newScp = NULL;
3600     } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3601         code = CM_ERROR_EXISTS;
3602     } else {
3603         code = 0;
3604     }
3605
3606     /* Check for RO volume */
3607     if (code == 0 &&
3608         (oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3609         code = CM_ERROR_READONLY;
3610     }
3611
3612     if (code)
3613         goto done;
3614
3615     if (oldNamep == NULL) {
3616         code = -1;
3617 #ifdef USE_BPLUS
3618         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3619                              CM_DIROP_FLAG_NONE, &oldDirOp);
3620         if (code == 0) {
3621             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3622             if (code == 0)
3623                 free_oldNamep = TRUE;
3624             cm_EndDirOp(&oldDirOp);
3625         }
3626 #endif
3627         if (code) {
3628             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3629                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3630             goto done;
3631         }
3632     }
3633
3634
3635     /* before starting the RPC, mark that we're changing the directory data,
3636      * so that someone who does a chmod on the dir will wait until our call
3637      * completes.  We do this in vnode order so that we don't deadlock,
3638      * which makes the code a little verbose.
3639      */
3640     if (oldDscp == newDscp) {
3641         /* check for identical names */
3642         if (cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3643             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3644                       oldDscp, newDscp);
3645             code = CM_ERROR_RENAME_IDENTICAL;
3646             goto done;
3647         }
3648
3649         oneDir = 1;
3650         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3651                       CM_DIROP_FLAG_NONE, &oldDirOp);
3652         lock_ObtainWrite(&oldDscp->rw);
3653         cm_dnlcRemove(oldDscp, cOldNamep);
3654         cm_dnlcRemove(oldDscp, cNewNamep);
3655         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3656                           CM_SCACHESYNC_STOREDATA);
3657         lock_ReleaseWrite(&oldDscp->rw);
3658         if (code != 0) {
3659             cm_EndDirOp(&oldDirOp);
3660         }
3661     }
3662     else {
3663         /* two distinct dir vnodes */
3664         oneDir = 0;
3665         if (oldDscp->fid.cell != newDscp->fid.cell ||
3666              oldDscp->fid.volume != newDscp->fid.volume) {
3667             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3668                       oldDscp, newDscp);
3669             code = CM_ERROR_CROSSDEVLINK;
3670             goto done;
3671         }
3672
3673         /* shouldn't happen that we have distinct vnodes for two
3674          * different files, but could due to deliberate attack, or
3675          * stale info.  Avoid deadlocks and quit now.
3676          */
3677         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3678             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3679                       oldDscp, newDscp);
3680             code = CM_ERROR_CROSSDEVLINK;
3681             goto done;
3682         }
3683
3684         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3685             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3686                           CM_DIROP_FLAG_NONE, &oldDirOp);
3687             lock_ObtainWrite(&oldDscp->rw);
3688             cm_dnlcRemove(oldDscp, cOldNamep);
3689             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3690                              CM_SCACHESYNC_STOREDATA);
3691             lock_ReleaseWrite(&oldDscp->rw);
3692             if (code != 0)
3693                 cm_EndDirOp(&oldDirOp);
3694             if (code == 0) {
3695                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3696                               CM_DIROP_FLAG_NONE, &newDirOp);
3697                 lock_ObtainWrite(&newDscp->rw);
3698                 cm_dnlcRemove(newDscp, cNewNamep);
3699                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3700                                  CM_SCACHESYNC_STOREDATA);
3701                 lock_ReleaseWrite(&newDscp->rw);
3702                 if (code) {
3703                     cm_EndDirOp(&newDirOp);
3704
3705                     /* cleanup first one */
3706                     lock_ObtainWrite(&oldDscp->rw);
3707                     cm_SyncOpDone(oldDscp, NULL,
3708                                    CM_SCACHESYNC_STOREDATA);
3709                     lock_ReleaseWrite(&oldDscp->rw);
3710                     cm_EndDirOp(&oldDirOp);
3711                 }
3712             }
3713         }
3714         else {
3715             /* lock the new vnode entry first */
3716             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3717                           CM_DIROP_FLAG_NONE, &newDirOp);
3718             lock_ObtainWrite(&newDscp->rw);
3719             cm_dnlcRemove(newDscp, cNewNamep);
3720             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3721                               CM_SCACHESYNC_STOREDATA);
3722             lock_ReleaseWrite(&newDscp->rw);
3723             if (code != 0)
3724                 cm_EndDirOp(&newDirOp);
3725             if (code == 0) {
3726                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3727                               CM_DIROP_FLAG_NONE, &oldDirOp);
3728                 lock_ObtainWrite(&oldDscp->rw);
3729                 cm_dnlcRemove(oldDscp, cOldNamep);
3730                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3731                                   CM_SCACHESYNC_STOREDATA);
3732                 lock_ReleaseWrite(&oldDscp->rw);
3733                 if (code != 0)
3734                     cm_EndDirOp(&oldDirOp);
3735                 if (code) {
3736                     /* cleanup first one */
3737                     lock_ObtainWrite(&newDscp->rw);
3738                     cm_SyncOpDone(newDscp, NULL,
3739                                    CM_SCACHESYNC_STOREDATA);
3740                     lock_ReleaseWrite(&newDscp->rw);
3741                     cm_EndDirOp(&newDirOp);
3742                 }
3743             }
3744         }
3745     }   /* two distinct vnodes */
3746
3747     if (code)
3748         goto done;
3749
3750     didEnd = 0;
3751
3752     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3753
3754     /* try the RPC now */
3755     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3756               oldDscp, newDscp);
3757     do {
3758         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3759         if (code)
3760             continue;
3761
3762         oldDirAFSFid.Volume = oldDscp->fid.volume;
3763         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3764         oldDirAFSFid.Unique = oldDscp->fid.unique;
3765         newDirAFSFid.Volume = newDscp->fid.volume;
3766         newDirAFSFid.Vnode = newDscp->fid.vnode;
3767         newDirAFSFid.Unique = newDscp->fid.unique;
3768
3769         rxconnp = cm_GetRxConn(connp);
3770         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3771                             &newDirAFSFid, newNamep,
3772                             &updatedOldDirStatus, &updatedNewDirStatus,
3773                             &volSync);
3774         rx_PutConnection(rxconnp);
3775
3776     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid,
3777                          &volSync, NULL, NULL, code));
3778     code = cm_MapRPCError(code, reqp);
3779
3780     if (code)
3781         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3782     else
3783         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3784
3785     /* update the individual stat cache entries for the directories */
3786     if (oldDirOp.scp) {
3787         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3788         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3789     }
3790     lock_ObtainWrite(&oldDscp->rw);
3791
3792     if (code == 0)
3793         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3794                        userp, reqp, CM_MERGEFLAG_DIROP);
3795     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
3796     lock_ReleaseWrite(&oldDscp->rw);
3797
3798     if (code == 0 && cm_CheckDirOpForSingleChange(&oldDirOp)) {
3799 #ifdef USE_BPLUS
3800         diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3801         if (diropCode == CM_ERROR_INEXACT_MATCH)
3802             diropCode = 0;
3803         else if (diropCode == EINVAL)
3804 #endif
3805             diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3806
3807         if (diropCode == 0) {
3808             if (oneDir) {
3809                 diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3810 #ifdef USE_BPLUS
3811                 cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3812 #endif
3813             }
3814
3815             if (diropCode == 0) {
3816                 diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3817 #ifdef USE_BPLUS
3818                 cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3819 #endif
3820             }
3821         }
3822     }
3823     cm_EndDirOp(&oldDirOp);
3824
3825     /* and update it for the new one, too, if necessary */
3826     if (!oneDir) {
3827         if (newDirOp.scp) {
3828             lock_ObtainWrite(&newDirOp.scp->dirlock);
3829             newDirOp.lockType = CM_DIRLOCK_WRITE;
3830         }
3831         lock_ObtainWrite(&newDscp->rw);
3832         if (code == 0)
3833             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
3834                             userp, reqp, CM_MERGEFLAG_DIROP);
3835         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
3836         lock_ReleaseWrite(&newDscp->rw);
3837
3838 #if 0
3839         /*
3840          * The following optimization does not work.
3841          * When the file server processed a RXAFS_Rename() request the
3842          * FID of the object being moved between directories is not
3843          * preserved.  The client does not know the new FID nor the
3844          * version number of the target.  Not only can we not create
3845          * the directory entry in the new directory, but we can't
3846          * preserve the cached data for the file.  It must be re-read
3847          * from the file server.  - jaltman, 2009/02/20
3848          */
3849         if (code == 0) {
3850             /* we only make the local change if we successfully made
3851                the change in the old directory AND there was only one
3852                change in the new directory */
3853             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
3854                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
3855 #ifdef USE_BPLUS
3856                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
3857 #endif
3858             }
3859         }
3860 #endif /* 0 */
3861         cm_EndDirOp(&newDirOp);
3862     }
3863
3864     /*
3865      * After the rename the file server has invalidated the callbacks
3866      * on the file that was moved nor do we have a directory reference
3867      * to it anymore.
3868      */
3869     lock_ObtainWrite(&oldScp->rw);
3870     cm_DiscardSCache(oldScp);
3871     lock_ReleaseWrite(&oldScp->rw);
3872
3873     if (RDR_Initialized)
3874         RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
3875                               oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
3876   done:
3877     if (oldScp)
3878         cm_ReleaseSCache(oldScp);
3879
3880     if (free_oldNamep)
3881         free(oldNamep);
3882
3883     free(newNamep);
3884
3885     /* and return error code */
3886     return code;
3887 }
3888
3889 /* Byte range locks:
3890
3891    The OpenAFS Windows client has to fake byte range locks given no
3892    server side support for such locks.  This is implemented as keyed
3893    byte range locks on the cache manager.
3894
3895    Keyed byte range locks:
3896
3897    Each cm_scache_t structure keeps track of a list of keyed locks.
3898    The key for a lock identifies an owner of a set of locks (referred
3899    to as a client).  Each key is represented by a value.  The set of
3900    key values used within a specific cm_scache_t structure form a
3901    namespace that has a scope of just that cm_scache_t structure.  The
3902    same key value can be used with another cm_scache_t structure and
3903    correspond to a completely different client.  However it is
3904    advantageous for the SMB or IFS layer to make sure that there is a
3905    1-1 mapping between client and keys over all cm_scache_t objects.
3906
3907    Assume a client C has key Key(C) (although, since the scope of the
3908    key is a cm_scache_t, the key can be Key(C,S), where S is the
3909    cm_scache_t.  But assume a 1-1 relation between keys and clients).
3910    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
3911    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
3912    through cm_generateKey() function for both SMB and IFS.
3913
3914    The list of locks for a cm_scache_t object S is maintained in
3915    S->fileLocks.  The cache manager will set a lock on the AFS file
3916    server in order to assert the locks in S->fileLocks.  If only
3917    shared locks are in place for S, then the cache manager will obtain
3918    a LockRead lock, while if there are any exclusive locks, it will
3919    obtain a LockWrite lock.  If the exclusive locks are all released
3920    while the shared locks remain, then the cache manager will
3921    downgrade the lock from LockWrite to LockRead.  Similarly, if an
3922    exclusive lock is obtained when only shared locks exist, then the
3923    cache manager will try to upgrade the lock from LockRead to
3924    LockWrite.
3925
3926    Each lock L owned by client C maintains a key L->key such that
3927    L->key == Key(C), the effective range defined by L->LOffset and
3928    L->LLength such that the range of bytes affected by the lock is
3929    (L->LOffset, +L->LLength), a type maintained in L->LockType which
3930    is either exclusive or shared.
3931
3932    Lock states:
3933
3934    A lock exists iff it is in S->fileLocks for some cm_scache_t
3935    S. Existing locks are in one of the following states: ACTIVE,
3936    WAITLOCK, WAITUNLOCK, LOST, DELETED.
3937
3938    The following sections describe each lock and the associated
3939    transitions.
3940
3941    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
3942       the lock with the AFS file server.  This type of lock can be
3943       exercised by a client to read or write to the locked region (as
3944       the lock allows).
3945
3946       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
3947         server lock that was required to assert the lock.  Before
3948         marking the lock as lost, the cache manager checks if the file
3949         has changed on the server.  If the file has not changed, then
3950         the cache manager will attempt to obtain a new server lock
3951         that is sufficient to assert the client side locks for the
3952         file.  If any of these fail, the lock is marked as LOST.
3953         Otherwise, it is left as ACTIVE.
3954
3955       1.2 ACTIVE->DELETED: Lock is released.
3956
3957    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
3958       grants the lock but the lock is yet to be asserted with the AFS
3959       file server.  Once the file server grants the lock, the state
3960       will transition to an ACTIVE lock.
3961
3962       2.1 WAITLOCK->ACTIVE: The server granted the lock.
3963
3964       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
3965         waiting.
3966
3967       2.3 WAITLOCK->LOST: One or more locks from this client were
3968         marked as LOST.  No further locks will be granted to this
3969         client until all lost locks are removed.
3970
3971    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
3972       receives a request for a lock that conflicts with an existing
3973       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
3974       and will be granted at such time the conflicting locks are
3975       removed, at which point the state will transition to either
3976       WAITLOCK or ACTIVE.
3977
3978       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
3979         current serverLock is sufficient to assert this lock, or a
3980         sufficient serverLock is obtained.
3981
3982       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
3983         however the required serverLock is yet to be asserted with the
3984         server.
3985
3986       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
3987         released.
3988
3989       3.5 WAITUNLOCK->LOST: One or more locks from this client were
3990         marked as LOST.  No further locks will be granted to this
3991         client until all lost locks are removed.
3992
3993    4. LOST: A lock L is LOST if the server lock that was required to
3994       assert the lock could not be obtained or if it could not be
3995       extended, or if other locks by the same client were LOST.
3996       Essentially, once a lock is LOST, the contract between the cache
3997       manager and that specific client is no longer valid.
3998
3999       The cache manager rechecks the server lock once every minute and
4000       extends it as appropriate.  If this is not done for 5 minutes,
4001       the AFS file server will release the lock (the 5 minute timeout
4002       is based on current file server code and is fairly arbitrary).
4003       Once released, the lock cannot be re-obtained without verifying
4004       that the contents of the file hasn't been modified since the
4005       time the lock was released.  Re-obtaining the lock without
4006       verifying this may lead to data corruption.  If the lock can not
4007       be obtained safely, then all active locks for the cm_scache_t
4008       are marked as LOST.
4009
4010       4.1 LOST->DELETED: The lock is released.
4011
4012    5. DELETED: The lock is no longer relevant.  Eventually, it will
4013       get removed from the cm_scache_t. In the meantime, it will be
4014       treated as if it does not exist.
4015
4016       5.1 DELETED->not exist: The lock is removed from the
4017         cm_scache_t.
4018
4019    The following are classifications of locks based on their state.
4020
4021    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
4022       have been accepted by the cache manager, but may or may not have
4023       been granted back to the client.
4024
4025    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4026
4027    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4028
4029    Lock operation:
4030
4031    A client C can READ range (Offset,+Length) of a file represented by
4032    cm_scache_t S iff (1):
4033
4034    1. for all _a_ in (Offset,+Length), all of the following is true:
4035
4036        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4037          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4038          shared.
4039
4040        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4041          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4042          Key(C)
4043
4044        (When locks are lost on an cm_scache_t, all locks are lost.  By
4045        4.2 (below), if there is an exclusive LOST lock, then there
4046        can't be any overlapping ACTIVE locks.)
4047
4048    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4049
4050    2. for all _a_ in (Offset,+Length), one of the following is true:
4051
4052        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4053          does not exist a LOST lock L such that _a_ in
4054          (L->LOffset,+L->LLength).
4055
4056        2.2 Byte _a_ of S is owned by C under lock L (as specified in
4057          1.2) AND L->LockType is exclusive.
4058
4059    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4060
4061    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4062       true:
4063
4064        3.1 If L->LockType is exclusive then there does NOT exist a
4065          ACCEPTED lock M in S->fileLocks such that _a_ in
4066          (M->LOffset,+M->LLength).
4067
4068          (If we count all QUEUED locks then we hit cases such as
4069          cascading waiting locks where the locks later on in the queue
4070          can be granted without compromising file integrity.  On the
4071          other hand if only ACCEPTED locks are considered, then locks
4072          that were received earlier may end up waiting for locks that
4073          were received later to be unlocked. The choice of ACCEPTED
4074          locks was made to mimic the Windows byte range lock
4075          semantics.)
4076
4077        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4078          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4079          M->LockType is shared.
4080
4081    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4082
4083        4.1 M->key != Key(C)
4084
4085        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4086          and (M->LOffset,+M->LLength) do not intersect.
4087
4088          (Note: If a client loses a lock, it loses all locks.
4089          Subsequently, it will not be allowed to obtain any more locks
4090          until all existing LOST locks that belong to the client are
4091          released.  Once all locks are released by a single client,
4092          there exists no further contract between the client and AFS
4093          about the contents of the file, hence the client can then
4094          proceed to obtain new locks and establish a new contract.
4095
4096          This doesn't quite work as you think it should, because most
4097          applications aren't built to deal with losing locks they
4098          thought they once had.  For now, we don't have a good
4099          solution to lost locks.
4100
4101          Also, for consistency reasons, we have to hold off on
4102          granting locks that overlap exclusive LOST locks.)
4103
4104    A client C can only unlock locks L in S->fileLocks which have
4105    L->key == Key(C).
4106
4107    The representation and invariants are as follows:
4108
4109    - Each cm_scache_t structure keeps:
4110
4111        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4112          are of type cm_file_lock_t.
4113
4114        - A record of the highest server-side lock that has been
4115          obtained for this object (cm_scache_t::serverLock), which is
4116          one of (-1), LockRead, LockWrite.
4117
4118        - A count of ACCEPTED exclusive and shared locks that are in the
4119          queue (cm_scache_t::sharedLocks and
4120          cm_scache_t::exclusiveLocks)
4121
4122    - Each cm_file_lock_t structure keeps:
4123
4124        - The type of lock (cm_file_lock_t::LockType)
4125
4126        - The key associated with the lock (cm_file_lock_t::key)
4127
4128        - The offset and length of the lock (cm_file_lock_t::LOffset
4129          and cm_file_lock_t::LLength)
4130
4131        - The state of the lock.
4132
4133        - Time of issuance or last successful extension
4134
4135    Semantic invariants:
4136
4137        I1. The number of ACCEPTED locks in S->fileLocks are
4138            (S->sharedLocks + S->exclusiveLocks)
4139
4140    External invariants:
4141
4142        I3. S->serverLock is the lock that we have asserted with the
4143            AFS file server for this cm_scache_t.
4144
4145        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4146            shared lock, but no ACTIVE exclusive locks.
4147
4148        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4149            exclusive lock.
4150
4151        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4152            M->key == L->key IMPLIES M is LOST or DELETED.
4153
4154    --asanka
4155  */
4156
4157 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4158
4159 #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)
4160
4161 #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)
4162
4163 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4164
4165 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4166
4167 /* unsafe */
4168 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4169
4170 /* unsafe */
4171 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4172
4173 /* unsafe */
4174 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4175
4176 /* unsafe */
4177 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4178
4179 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4180 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4181 #else
4182 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4183 #endif
4184
4185 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4186
4187 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4188 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4189 #else
4190 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4191
4192 /* This should really be defined in any build that this code is being
4193    compiled. */
4194 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4195 #endif
4196
4197 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4198 {
4199     afs_int64 int_begin;
4200     afs_int64 int_end;
4201
4202     int_begin = MAX(pos->offset, neg->offset);
4203     int_end = MIN(pos->offset+pos->length, neg->offset+neg->length);
4204
4205     if (int_begin < int_end) {
4206         if (int_begin == pos->offset) {
4207             pos->length = pos->offset + pos->length - int_end;
4208             pos->offset = int_end;
4209         } else if (int_end == pos->offset + pos->length) {
4210             pos->length = int_begin - pos->offset;
4211         }
4212
4213         /* We only subtract ranges if the resulting range is
4214            contiguous.  If we try to support non-contigous ranges, we
4215            aren't actually improving performance. */
4216     }
4217 }
4218
4219 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4220    specified range by the client identified by key.
4221  */
4222 long cm_LockCheckRead(cm_scache_t *scp,
4223                       LARGE_INTEGER LOffset,
4224                       LARGE_INTEGER LLength,
4225                       cm_key_t key)
4226 {
4227 #ifndef ADVISORY_LOCKS
4228
4229     cm_file_lock_t *fileLock;
4230     osi_queue_t *q;
4231     long code = 0;
4232     cm_range_t range;
4233     int substract_ranges = FALSE;
4234
4235     range.offset = LOffset.QuadPart;
4236     range.length = LLength.QuadPart;
4237
4238     /*
4239
4240      1. for all _a_ in (Offset,+Length), all of the following is true:
4241
4242        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4243          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4244          shared.
4245
4246        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4247          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4248          Key(C)
4249
4250     */
4251
4252     lock_ObtainRead(&cm_scacheLock);
4253
4254     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4255         fileLock =
4256             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4257
4258         if (INTERSECT_RANGE(range, fileLock->range)) {
4259             if (IS_LOCK_ACTIVE(fileLock)) {
4260                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4261
4262                     /* If there is an active lock for this client, it
4263                        is safe to substract ranges.*/
4264                     cm_LockRangeSubtract(&range, &fileLock->range);
4265                     substract_ranges = TRUE;
4266                 } else {
4267                     if (fileLock->lockType != LockRead) {
4268                         code = CM_ERROR_LOCK_CONFLICT;
4269                         break;
4270                     }
4271
4272                     /* even if the entire range is locked for reading,
4273                        we still can't grant the lock at this point
4274                        because the client may have lost locks. That
4275                        is, unless we have already seen an active lock
4276                        belonging to the client, in which case there
4277                        can't be any lost locks for this client. */
4278                     if (substract_ranges)
4279                         cm_LockRangeSubtract(&range, &fileLock->range);
4280                 }
4281             } else if (IS_LOCK_LOST(fileLock) &&
4282                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4283                 code = CM_ERROR_BADFD;
4284                 break;
4285             }
4286         }
4287     }
4288
4289     lock_ReleaseRead(&cm_scacheLock);
4290
4291     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4292               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4293
4294     return code;
4295
4296 #else
4297
4298     return 0;
4299
4300 #endif
4301 }
4302
4303 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4304    specified range by the client identified by key.
4305  */
4306 long cm_LockCheckWrite(cm_scache_t *scp,
4307                        LARGE_INTEGER LOffset,
4308                        LARGE_INTEGER LLength,
4309                        cm_key_t key)
4310 {
4311 #ifndef ADVISORY_LOCKS
4312
4313     cm_file_lock_t *fileLock;
4314     osi_queue_t *q;
4315     long code = 0;
4316     cm_range_t range;
4317
4318     range.offset = LOffset.QuadPart;
4319     range.length = LLength.QuadPart;
4320
4321     /*
4322    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4323
4324    2. for all _a_ in (Offset,+Length), one of the following is true:
4325
4326        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4327          lock L such that _a_ in (L->LOffset,+L->LLength).
4328
4329        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4330          exclusive.
4331     */
4332
4333     lock_ObtainRead(&cm_scacheLock);
4334
4335     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4336         fileLock =
4337             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4338
4339         if (INTERSECT_RANGE(range, fileLock->range)) {
4340             if (IS_LOCK_ACTIVE(fileLock)) {
4341                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4342                     if (fileLock->lockType == LockWrite) {
4343
4344                         /* if there is an active lock for this client, it
4345                            is safe to substract ranges */
4346                         cm_LockRangeSubtract(&range, &fileLock->range);
4347                     } else {
4348                         code = CM_ERROR_LOCK_CONFLICT;
4349                         break;
4350                     }
4351                 } else {
4352                     code = CM_ERROR_LOCK_CONFLICT;
4353                     break;
4354                 }
4355             } else if (IS_LOCK_LOST(fileLock)) {
4356                 code = CM_ERROR_BADFD;
4357                 break;
4358             }
4359         }
4360     }
4361
4362     lock_ReleaseRead(&cm_scacheLock);
4363
4364     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4365               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4366
4367     return code;
4368
4369 #else
4370
4371     return 0;
4372
4373 #endif
4374 }
4375
4376 /* Called with cm_scacheLock write locked */
4377 static cm_file_lock_t * cm_GetFileLock(void) {
4378     cm_file_lock_t * l;
4379
4380     l = (cm_file_lock_t *) cm_freeFileLocks;
4381     if (l) {
4382         osi_QRemove(&cm_freeFileLocks, &l->q);
4383     } else {
4384         l = malloc(sizeof(cm_file_lock_t));
4385         osi_assertx(l, "null cm_file_lock_t");
4386     }
4387
4388     memset(l, 0, sizeof(cm_file_lock_t));
4389
4390     return l;
4391 }
4392
4393 /* Called with cm_scacheLock write locked */
4394 static void cm_PutFileLock(cm_file_lock_t *l) {
4395     osi_QAdd(&cm_freeFileLocks, &l->q);
4396 }
4397
4398 /* called with scp->rw held.  May release it during processing, but
4399    leaves it held on exit. */
4400 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4401                    cm_req_t * reqp) {
4402     long code = 0;
4403     AFSFid tfid;
4404     cm_fid_t cfid;
4405     cm_conn_t * connp;
4406     struct rx_connection * rxconnp;
4407     AFSVolSync volSync;
4408     afs_uint32 reqflags = reqp->flags;
4409
4410     memset(&volSync, 0, sizeof(volSync));
4411
4412     tfid.Volume = scp->fid.volume;
4413     tfid.Vnode = scp->fid.vnode;
4414     tfid.Unique = scp->fid.unique;
4415     cfid = scp->fid;
4416
4417     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4418
4419     reqp->flags |= CM_REQ_NORETRY;
4420     lock_ReleaseWrite(&scp->rw);
4421
4422     do {
4423         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4424         if (code)
4425             break;
4426
4427         rxconnp = cm_GetRxConn(connp);
4428         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4429                              &volSync);
4430         rx_PutConnection(rxconnp);
4431
4432     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4433                         NULL, NULL, code));
4434
4435     code = cm_MapRPCError(code, reqp);
4436     if (code) {
4437         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4438     } else {
4439         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4440     }
4441
4442     reqp->flags = reqflags;
4443
4444     lock_ObtainWrite(&scp->rw);
4445     if (code == 0) {
4446         /*
4447          * The file server does not return a status structure so we must
4448          * locally track the file server lock count to the best of our
4449          * ability.
4450          */
4451         if (lockType == LockWrite)
4452             scp->fsLockCount = -1;
4453         else
4454             scp->fsLockCount++;
4455     }
4456     return code;
4457 }
4458
4459 /* called with scp->rw held.  Releases it during processing */
4460 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4461                        cm_req_t * reqp) {
4462     long code = 0;
4463     AFSFid tfid;
4464     cm_fid_t cfid;
4465     cm_conn_t * connp;
4466     struct rx_connection * rxconnp;
4467     AFSVolSync volSync;
4468
4469     if (scp->flags & CM_SCACHEFLAG_DELETED) {
4470         osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4471         return 0;
4472     }
4473
4474     memset(&volSync, 0, sizeof(volSync));
4475
4476     tfid.Volume = scp->fid.volume;
4477     tfid.Vnode = scp->fid.vnode;
4478     tfid.Unique = scp->fid.unique;
4479     cfid = scp->fid;
4480
4481     lock_ReleaseWrite(&scp->rw);
4482
4483     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4484
4485     do {
4486         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4487         if (code)
4488             break;
4489
4490         rxconnp = cm_GetRxConn(connp);
4491         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4492         rx_PutConnection(rxconnp);
4493
4494     } while (cm_Analyze(connp, userp, reqp, &cfid, &volSync,
4495                         NULL, NULL, code));
4496     code = cm_MapRPCError(code, reqp);
4497     if (code)
4498         osi_Log1(afsd_logp,
4499                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4500     else
4501         osi_Log0(afsd_logp,
4502                  "CALL ReleaseLock SUCCESS");
4503
4504     lock_ObtainWrite(&scp->rw);
4505     if (code == 0) {
4506         /*
4507          * The file server does not return a status structure so we must
4508          * locally track the file server lock count to the best of our
4509          * ability.
4510          */
4511         scp->fsLockCount--;
4512         if (scp->fsLockCount < 0)
4513             scp->fsLockCount = 0;
4514     }
4515
4516     return (code != CM_ERROR_BADFD ? code : 0);
4517 }
4518
4519 /* called with scp->rw held.  May release it during processing, but
4520    will exit with lock held.
4521
4522    This will return:
4523
4524    - 0 if the user has permission to get the specified lock for the scp
4525
4526    - CM_ERROR_NOACCESS if not
4527
4528    Any other error from cm_SyncOp will be sent down untranslated.
4529
4530    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4531    phas_insert (if non-NULL) will receive a boolean value indicating
4532    whether the user has INSERT permission or not.
4533 */
4534 long cm_LockCheckPerms(cm_scache_t * scp,
4535                        int lock_type,
4536                        cm_user_t * userp,
4537                        cm_req_t * reqp,
4538                        int * phas_insert)
4539 {
4540     long rights = 0;
4541     long code = 0, code2 = 0;
4542
4543     /* lock permissions are slightly tricky because of the 'i' bit.
4544        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4545        user has PRSFS_WRITE, she can write-lock the file.  However, if
4546        the user has PRSFS_INSERT, then she can write-lock new files,
4547        but not old ones.  Since we don't have information about
4548        whether a file is new or not, we assume that if the user owns
4549        the scp, then she has the permissions that are granted by
4550        PRSFS_INSERT. */
4551
4552     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4553              scp, lock_type, userp);
4554
4555     if (lock_type == LockRead)
4556         rights |= PRSFS_LOCK;
4557     else if (lock_type == LockWrite)
4558         rights |= PRSFS_WRITE | PRSFS_LOCK;
4559     else {
4560         /* hmmkay */
4561         osi_assertx(FALSE, "invalid lock type");
4562         return 0;
4563     }
4564
4565     if (phas_insert)
4566         *phas_insert = FALSE;
4567
4568     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4569                      CM_SCACHESYNC_GETSTATUS |
4570                      CM_SCACHESYNC_NEEDCALLBACK);
4571
4572     if (phas_insert && scp->creator == userp) {
4573
4574         /* If this file was created by the user, then we check for
4575            PRSFS_INSERT.  If the file server is recent enough, then
4576            this should be sufficient for her to get a write-lock (but
4577            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4578            indicates whether a file server supports getting write
4579            locks when the user only has PRSFS_INSERT.
4580
4581            If the file was not created by the user we skip the check
4582            because the INSERT bit will not apply to this user even
4583            if it is set.
4584          */
4585
4586         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4587                          CM_SCACHESYNC_GETSTATUS |
4588                          CM_SCACHESYNC_NEEDCALLBACK);
4589
4590         if (code2 == CM_ERROR_NOACCESS) {
4591             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4592         } else {
4593             *phas_insert = TRUE;
4594             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4595         }
4596     }
4597
4598     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4599
4600     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4601
4602     return code;
4603 }
4604
4605 /* called with scp->rw held */
4606 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4607              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4608              cm_key_t key,
4609              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4610              cm_file_lock_t **lockpp)
4611 {
4612     long code = 0;
4613     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4614     cm_file_lock_t *fileLock;
4615     osi_queue_t *q;
4616     cm_range_t range;
4617     int wait_unlock = FALSE;
4618     int force_client_lock = FALSE;
4619
4620     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4621              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4622     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4623              key.process_id, key.session_id, key.file_id);
4624
4625     /*
4626    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4627
4628    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4629       true:
4630
4631        3.1 If L->LockType is exclusive then there does NOT exist a
4632          ACCEPTED lock M in S->fileLocks such that _a_ in
4633          (M->LOffset,+M->LLength).
4634
4635        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4636          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4637          M->LockType is shared.
4638
4639    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4640
4641        4.1 M->key != Key(C)
4642
4643        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4644          and (M->LOffset,+M->LLength) do not intersect.
4645     */
4646
4647     range.offset = LOffset.QuadPart;
4648     range.length = LLength.QuadPart;
4649
4650     lock_ObtainRead(&cm_scacheLock);
4651
4652     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4653         fileLock =
4654             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4655
4656         if (IS_LOCK_LOST(fileLock)) {
4657             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4658                 code = CM_ERROR_BADFD;
4659                 break;
4660             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4661                 code = CM_ERROR_WOULDBLOCK;
4662                 wait_unlock = TRUE;
4663                 break;
4664             }
4665         }
4666
4667         /* we don't need to check for deleted locks here since deleted
4668            locks are dequeued from scp->fileLocks */
4669         if (IS_LOCK_ACCEPTED(fileLock) &&
4670            INTERSECT_RANGE(range, fileLock->range)) {
4671
4672             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4673                 fileLock->lockType != LockRead) {
4674                 wait_unlock = TRUE;
4675                 code = CM_ERROR_WOULDBLOCK;
4676                 break;
4677             }
4678         }
4679     }
4680
4681     lock_ReleaseRead(&cm_scacheLock);
4682
4683     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4684         if (Which == scp->serverLock ||
4685            (Which == LockRead && scp->serverLock == LockWrite)) {
4686
4687             int has_insert = 0;
4688
4689             /* we already have the lock we need */
4690             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4691                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4692
4693             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4694
4695             /* special case: if we don't have permission to read-lock
4696                the file, then we force a clientside lock.  This is to
4697                compensate for applications that obtain a read-lock for
4698                reading files off of directories that don't grant
4699                read-locks to the user. */
4700             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4701
4702                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4703                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4704                     code = 0;
4705                 } else {
4706                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4707                     force_client_lock = TRUE;
4708                 }
4709             }
4710
4711         } else if ((scp->exclusiveLocks > 0) ||
4712                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4713             int has_insert = 0;
4714
4715             /* We are already waiting for some other lock.  We should
4716                wait for the daemon to catch up instead of generating a
4717                flood of SetLock calls. */
4718             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4719                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4720
4721             /* see if we have permission to create the lock in the
4722                first place. */
4723             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4724             if (code == 0)
4725                 code = CM_ERROR_WOULDBLOCK;
4726             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4727
4728                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4729                     osi_Log0(afsd_logp,
4730                              "   User has no read-lock perms, but has INSERT perms.");
4731                     code = CM_ERROR_WOULDBLOCK;
4732                 } else {
4733                     osi_Log0(afsd_logp,
4734                              "   User has no read-lock perms. Forcing client-side lock");
4735                     force_client_lock = TRUE;
4736                 }
4737             }
4738
4739             /* leave any other codes as-is */
4740
4741         } else {
4742             int newLock;
4743             int check_data_version = FALSE;
4744             int has_insert = 0;
4745
4746             /* first check if we have permission to elevate or obtain
4747                the lock. */
4748             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4749             if (code) {
4750                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4751                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4752                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4753                     force_client_lock = TRUE;
4754                 }
4755                 goto check_code;
4756             }
4757
4758             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4759
4760             if (scp->serverLock == LockRead && Which == LockWrite) {
4761
4762                 /* We want to escalate the lock to a LockWrite.
4763                  * Unfortunately that's not really possible without
4764                  * letting go of the current lock.  But for now we do
4765                  * it anyway. */
4766
4767                 osi_Log0(afsd_logp,
4768                          "   attempting to UPGRADE from LockRead to LockWrite.");
4769                 osi_Log1(afsd_logp,
4770                          "   dataVersion on scp: %I64d", scp->dataVersion);
4771
4772                 /* we assume at this point (because scp->serverLock
4773                    was valid) that we had a valid server lock. */
4774                 scp->lockDataVersion = scp->dataVersion;
4775                 check_data_version = TRUE;
4776
4777                 code = cm_IntReleaseLock(scp, userp, reqp);
4778
4779                 if (code) {
4780                     /* We couldn't release the lock */
4781                     goto check_code;
4782                 } else {
4783                     scp->serverLock = -1;
4784                 }
4785             }
4786
4787             /* We need to obtain a server lock of type Which in order
4788              * to assert this file lock */
4789 #ifndef AGGRESSIVE_LOCKS
4790             newLock = Which;
4791 #else
4792             newLock = LockWrite;
4793 #endif
4794
4795             code = cm_IntSetLock(scp, userp, newLock, reqp);
4796
4797 #ifdef AGGRESSIVE_LOCKS
4798             if ((code == CM_ERROR_WOULDBLOCK ||
4799                  code == CM_ERROR_NOACCESS) && newLock != Which) {
4800                 /* we wanted LockRead.  We tried LockWrite. Now try
4801                  * LockRead again */
4802                 newLock = Which;
4803
4804                 /* am I sane? */
4805                 osi_assertx(newLock == LockRead, "lock type not read");
4806
4807                 code = cm_IntSetLock(scp, userp, newLock, reqp);
4808             }
4809 #endif
4810
4811             if (code == CM_ERROR_NOACCESS) {
4812                 if (Which == LockRead) {
4813                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4814                         long tcode;
4815                         /* We requested a read-lock, but we have permission to
4816                          * get a write-lock. Try that */
4817
4818                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
4819
4820                         if (tcode == 0) {
4821                             newLock = LockWrite;
4822
4823                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
4824
4825                             code = cm_IntSetLock(scp, userp, newLock, reqp);
4826                         }
4827                     } else {
4828                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4829                         force_client_lock = TRUE;
4830                     }
4831                 } else if (Which == LockWrite &&
4832                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
4833                     long tcode;
4834
4835                     /* Special case: if the lock request was for a
4836                      * LockWrite and the user owns the file and we weren't
4837                      * allowed to obtain the serverlock, we either lost a
4838                      * race (the permissions changed from under us), or we
4839                      * have 'i' bits, but we aren't allowed to lock the
4840                      * file. */
4841
4842                     /* check if we lost a race... */
4843                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
4844
4845                     if (tcode == 0) {
4846                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
4847                         force_client_lock = TRUE;
4848                     }
4849                 }
4850             }
4851
4852             if (code == 0 && check_data_version &&
4853                scp->dataVersion != scp->lockDataVersion) {
4854                 /* We lost a race.  Although we successfully obtained
4855                  * a lock, someone modified the file in between.  The
4856                  * locks have all been technically lost. */
4857
4858                 osi_Log0(afsd_logp,
4859                          "  Data version mismatch while upgrading lock.");
4860                 osi_Log2(afsd_logp,
4861                          "  Data versions before=%I64d, after=%I64d",
4862                          scp->lockDataVersion,
4863                          scp->dataVersion);
4864                 osi_Log1(afsd_logp,
4865                          "  Releasing stale lock for scp 0x%x", scp);
4866
4867                 code = cm_IntReleaseLock(scp, userp, reqp);
4868
4869                 scp->serverLock = -1;
4870
4871                 code = CM_ERROR_INVAL;
4872             } else if (code == 0) {
4873                 scp->serverLock = newLock;
4874                 scp->lockDataVersion = scp->dataVersion;
4875             }
4876
4877             if (code != 0 &&
4878                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
4879                 scp->serverLock == -1) {
4880                 /* Oops. We lost the lock. */
4881                 cm_LockMarkSCacheLost(scp);
4882             }
4883         }
4884     } else if (code == 0) {     /* server locks not enabled */
4885         osi_Log0(afsd_logp,
4886                  "  Skipping server lock for scp");
4887     }
4888
4889  check_code:
4890
4891     if (code != 0 && !force_client_lock) {
4892         /* Special case error translations
4893
4894            Applications don't expect certain errors from a
4895            LockFile/UnlockFile call.  We need to translate some error
4896            code to codes that apps expect and handle. */
4897
4898         /* We shouldn't actually need to handle this case since we
4899            simulate locks for RO scps anyway. */
4900         if (code == CM_ERROR_READONLY) {
4901             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
4902             code = CM_ERROR_NOACCESS;
4903         }
4904     }
4905
4906     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
4907         force_client_lock) {
4908
4909         /* clear the error if we are forcing a client lock, so we
4910            don't get confused later. */
4911         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
4912             code = 0;
4913
4914         cm_HoldUser(userp);
4915
4916         lock_ObtainWrite(&cm_scacheLock);
4917         fileLock = cm_GetFileLock();
4918 #ifdef DEBUG
4919         fileLock->fid = scp->fid;
4920 #endif
4921         fileLock->key = key;
4922         fileLock->lockType = Which;
4923         fileLock->userp = userp;
4924         fileLock->range = range;
4925         fileLock->flags = (code == 0 ? 0 :
4926                            ((wait_unlock)?
4927                             CM_FILELOCK_FLAG_WAITUNLOCK :
4928                             CM_FILELOCK_FLAG_WAITLOCK));
4929
4930         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
4931             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
4932
4933         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
4934
4935         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
4936         cm_HoldSCacheNoLock(scp);
4937         fileLock->scp = scp;
4938         osi_QAdd(&cm_allFileLocks, &fileLock->q);
4939         lock_ReleaseWrite(&cm_scacheLock);
4940
4941         if (code != 0) {
4942             *lockpp = fileLock;
4943         }
4944
4945         if (IS_LOCK_CLIENTONLY(fileLock)) {
4946             scp->clientLocks++;
4947         } else if (IS_LOCK_ACCEPTED(fileLock)) {
4948             if (Which == LockRead)
4949                 scp->sharedLocks++;
4950             else
4951                 scp->exclusiveLocks++;
4952         }
4953
4954         osi_Log3(afsd_logp,
4955                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
4956                  fileLock, fileLock->flags, scp);
4957         osi_Log4(afsd_logp,
4958                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
4959                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
4960                  (int)(signed char) scp->serverLock);
4961     } else {
4962         osi_Log1(afsd_logp,
4963                  "cm_Lock Rejecting lock (code = 0x%x)", code);
4964     }
4965
4966     /* Convert from would block to lock not granted */
4967     if (code == CM_ERROR_WOULDBLOCK)
4968         code = CM_ERROR_LOCK_NOT_GRANTED;
4969
4970     return code;
4971 }
4972
4973 static long
4974 cm_IntUnlock(cm_scache_t * scp,
4975              cm_user_t * userp,
4976              cm_req_t *  reqp)
4977 {
4978     long code = 0;
4979
4980     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
4981     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
4982     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
4983
4984     if (!SERVERLOCKS_ENABLED(scp)) {
4985         osi_Log0(afsd_logp, "  Skipping server lock for scp");
4986         goto done;
4987     }
4988
4989     /* Ideally we would go through the rest of the locks to determine
4990      * if one or more locks that were formerly in WAITUNLOCK can now
4991      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
4992      * scp->sharedLocks accordingly.  However, the retrying of locks
4993      * in that manner is done cm_RetryLock() manually.
4994      */
4995
4996     if (scp->serverLock == LockWrite &&
4997         scp->exclusiveLocks == 0 &&
4998         scp->sharedLocks > 0) {
4999         /* The serverLock should be downgraded to LockRead */
5000         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
5001
5002         /* Make sure there are no dirty buffers left. */
5003         code = cm_FSync(scp, userp, reqp, TRUE);
5004
5005         /* since scp->serverLock looked sane, we are going to assume
5006            that we have a valid server lock. */
5007         scp->lockDataVersion = scp->dataVersion;
5008         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
5009
5010         /* before we downgrade, make sure that we have enough
5011            permissions to get the read lock. */
5012         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5013         if (code != 0) {
5014
5015             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5016
5017             code = 0;
5018             goto done;
5019         }
5020
5021         code = cm_IntReleaseLock(scp, userp, reqp);
5022
5023         if (code) {
5024             /* so we couldn't release it.  Just let the lock be for now */
5025             code = 0;
5026             goto done;
5027         } else {
5028             scp->serverLock = -1;
5029         }
5030
5031         code = cm_IntSetLock(scp, userp, LockRead, reqp);
5032
5033         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5034             scp->serverLock = LockRead;
5035         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5036             /* We lost a race condition.  Although we have a valid
5037                lock on the file, the data has changed and essentially
5038                we have lost the lock we had during the transition. */
5039
5040             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5041             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
5042                      scp->lockDataVersion,
5043                      scp->dataVersion);
5044
5045             code = cm_IntReleaseLock(scp, userp, reqp);
5046
5047             code = CM_ERROR_INVAL;
5048             scp->serverLock = -1;
5049         }
5050
5051         if (code != 0 &&
5052             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5053                 (scp->serverLock == -1)) {
5054                 /* Oopsie */
5055                 cm_LockMarkSCacheLost(scp);
5056             }
5057
5058         /* failure here has no bearing on the return value of cm_Unlock() */
5059         code = 0;
5060
5061     } else if (scp->serverLock != (-1) &&
5062               scp->exclusiveLocks == 0 &&
5063               scp->sharedLocks == 0) {
5064         /* The serverLock should be released entirely */
5065
5066         if (scp->serverLock == LockWrite) {
5067             osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
5068
5069             /* Make sure there are no dirty buffers left. */
5070             code = cm_FSync(scp, userp, reqp, TRUE);
5071         } else {
5072             osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
5073         }
5074
5075         code = cm_IntReleaseLock(scp, userp, reqp);
5076
5077         if (code == 0)
5078             scp->serverLock = (-1);
5079     }
5080
5081   done:
5082     return code;
5083 }
5084 /* Called with scp->rw held */
5085 long cm_UnlockByKey(cm_scache_t * scp,
5086                     cm_key_t key,
5087                     afs_uint32 flags,
5088                     cm_user_t * userp,
5089                     cm_req_t * reqp)
5090 {
5091     long code = 0;
5092     cm_file_lock_t *fileLock;
5093     osi_queue_t *q, *qn;
5094     int n_unlocks = 0;
5095
5096     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5097              scp, key.process_id, key.session_id, key.file_id);
5098     osi_Log1(afsd_logp, "    flags=0x%x", flags);
5099
5100     lock_ObtainWrite(&cm_scacheLock);
5101
5102     for (q = scp->fileLocksH; q; q = qn) {
5103         qn = osi_QNext(q);
5104
5105         fileLock = (cm_file_lock_t *)
5106             ((char *) q - offsetof(cm_file_lock_t, fileq));
5107
5108 #ifdef DEBUG
5109         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
5110                  fileLock,
5111                  (unsigned long) fileLock->range.offset,
5112                  (unsigned long) fileLock->range.length,
5113                 fileLock->lockType);
5114         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5115                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5116                  fileLock->flags);
5117
5118         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5119             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5120             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5121                      fileLock->fid.cell,
5122                      fileLock->fid.volume,
5123                      fileLock->fid.vnode,
5124                      fileLock->fid.unique);
5125             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5126                      fileLock->scp->fid.cell,
5127                      fileLock->scp->fid.volume,
5128                      fileLock->scp->fid.vnode,
5129                      fileLock->scp->fid.unique);
5130             osi_assertx(FALSE, "invalid fid value");
5131         }
5132 #endif
5133
5134         if (!IS_LOCK_DELETED(fileLock) &&
5135             cm_KeyEquals(&fileLock->key, &key, flags)) {
5136             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5137                     fileLock->range.offset,
5138                     fileLock->range.length,
5139                     fileLock->lockType);
5140
5141             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5142
5143             if (IS_LOCK_CLIENTONLY(fileLock)) {
5144                 scp->clientLocks--;
5145             } else if (IS_LOCK_ACCEPTED(fileLock)) {
5146                 if (fileLock->lockType == LockRead)
5147                     scp->sharedLocks--;
5148                 else
5149                     scp->exclusiveLocks--;
5150             }
5151
5152             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5153
5154             cm_ReleaseUser(fileLock->userp);
5155             cm_ReleaseSCacheNoLock(scp);
5156
5157             fileLock->userp = NULL;
5158             fileLock->scp = NULL;
5159
5160             n_unlocks++;
5161         }
5162     }
5163
5164     lock_ReleaseWrite(&cm_scacheLock);
5165
5166     if (n_unlocks == 0) {
5167         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5168         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5169                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5170
5171         return 0;
5172     }
5173
5174     code = cm_IntUnlock(scp, userp, reqp);
5175     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5176
5177     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5178              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5179              (int)(signed char) scp->serverLock);
5180
5181     return code;
5182 }
5183
5184 /* Called with scp->rw held */
5185 long cm_Unlock(cm_scache_t *scp,
5186                unsigned char sLockType,
5187                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5188                cm_key_t key,
5189                afs_uint32 flags,
5190                cm_user_t *userp,
5191                cm_req_t *reqp)
5192 {
5193     long code = 0;
5194     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5195     cm_file_lock_t *fileLock;
5196     osi_queue_t *q;
5197     int release_userp = FALSE;
5198     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5199     int lock_found  = 0;
5200     LARGE_INTEGER RangeEnd;
5201
5202     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5203              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5204     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5205              key.process_id, key.session_id, key.file_id, flags);
5206
5207     if (!exact_match)
5208         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5209
5210   try_again:
5211     lock_ObtainRead(&cm_scacheLock);
5212
5213     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5214         fileLock = (cm_file_lock_t *)
5215             ((char *) q - offsetof(cm_file_lock_t, fileq));
5216
5217 #ifdef DEBUG
5218         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5219             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5220             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5221                      fileLock->fid.cell,
5222                      fileLock->fid.volume,
5223                      fileLock->fid.vnode,
5224                      fileLock->fid.unique);
5225             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5226                      fileLock->scp->fid.cell,
5227                      fileLock->scp->fid.volume,
5228                      fileLock->scp->fid.vnode,
5229                      fileLock->scp->fid.unique);
5230             osi_assertx(FALSE, "invalid fid value");
5231         }
5232 #endif
5233         if (exact_match) {
5234             if (!IS_LOCK_DELETED(fileLock) &&
5235                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5236                  fileLock->range.offset == LOffset.QuadPart &&
5237                  fileLock->range.length == LLength.QuadPart) {
5238                 lock_found = 1;
5239                 break;
5240             }
5241         } else {
5242
5243             if (!IS_LOCK_DELETED(fileLock) &&
5244                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5245                  fileLock->range.offset >= LOffset.QuadPart &&
5246                  fileLock->range.offset < RangeEnd.QuadPart &&
5247                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5248                 lock_found = 1;
5249                 break;
5250             }
5251         }
5252     }
5253
5254     if (!q) {
5255         lock_ReleaseRead(&cm_scacheLock);
5256
5257         if (lock_found && !exact_match) {
5258             code = 0;
5259             goto done;
5260         } else {
5261             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5262
5263             /* The lock didn't exist anyway. *shrug* */
5264             return CM_ERROR_RANGE_NOT_LOCKED;
5265         }
5266     }
5267
5268     /* discard lock record */
5269     lock_ConvertRToW(&cm_scacheLock);
5270     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5271
5272     /*
5273      * Don't delete it here; let the daemon delete it, to simplify
5274      * the daemon's traversal of the list.
5275      */
5276
5277     if (IS_LOCK_CLIENTONLY(fileLock)) {
5278         scp->clientLocks--;
5279     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5280         if (fileLock->lockType == LockRead)
5281             scp->sharedLocks--;
5282         else
5283             scp->exclusiveLocks--;
5284     }
5285
5286     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5287
5288     if (userp != NULL) {
5289         cm_ReleaseUser(fileLock->userp);
5290     } else {
5291         userp = fileLock->userp;
5292         release_userp = TRUE;
5293     }
5294     cm_ReleaseSCacheNoLock(scp);
5295     fileLock->userp = NULL;
5296     fileLock->scp = NULL;
5297     lock_ReleaseWrite(&cm_scacheLock);
5298
5299     code = cm_IntUnlock(scp, userp, reqp);
5300
5301     if (release_userp) {
5302         cm_ReleaseUser(userp);
5303         release_userp = FALSE;
5304     }
5305
5306     if (!exact_match) {
5307         osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5308         goto try_again;         /* might be more than one lock in the range */
5309     }
5310
5311  done:
5312
5313     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5314     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5315              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5316              (int)(signed char) scp->serverLock);
5317
5318     return code;
5319 }
5320
5321 /* called with scp->rw held */
5322 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5323 {
5324     cm_file_lock_t *fileLock;
5325     osi_queue_t *q;
5326
5327     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5328
5329     /* cm_scacheLock needed because we are modifying fileLock->flags */
5330     lock_ObtainWrite(&cm_scacheLock);
5331
5332     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5333         fileLock =
5334             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5335
5336         if (IS_LOCK_ACTIVE(fileLock) &&
5337             !IS_LOCK_CLIENTONLY(fileLock)) {
5338             if (fileLock->lockType == LockRead)
5339                 scp->sharedLocks--;
5340             else
5341                 scp->exclusiveLocks--;
5342
5343             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5344         }
5345     }
5346
5347     scp->serverLock = -1;
5348     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5349     lock_ReleaseWrite(&cm_scacheLock);
5350 }
5351
5352 /* Called with no relevant locks held */
5353 void cm_CheckLocks()
5354 {
5355     osi_queue_t *q, *nq;
5356     cm_file_lock_t *fileLock;
5357     cm_req_t req;
5358     AFSFid tfid;
5359     AFSVolSync volSync;
5360     cm_conn_t *connp;
5361     long code;
5362     struct rx_connection * rxconnp;
5363     cm_scache_t * scp;
5364
5365     memset(&volSync, 0, sizeof(volSync));
5366
5367     cm_InitReq(&req);
5368
5369     lock_ObtainWrite(&cm_scacheLock);
5370
5371     cm_lockRefreshCycle++;
5372
5373     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5374
5375     for (q = cm_allFileLocks; q; q = nq) {
5376         fileLock = (cm_file_lock_t *) q;
5377         nq = osi_QNext(q);
5378         code = -1;
5379
5380         if (IS_LOCK_DELETED(fileLock)) {
5381             cm_user_t *userp = fileLock->userp;
5382             cm_scache_t *scp = fileLock->scp;
5383             fileLock->userp = NULL;
5384             fileLock->scp = NULL;
5385
5386             if (scp && userp) {
5387                 lock_ReleaseWrite(&cm_scacheLock);
5388                 lock_ObtainWrite(&scp->rw);
5389                 code = cm_IntUnlock(scp, userp, &req);
5390                 lock_ReleaseWrite(&scp->rw);
5391
5392                 cm_ReleaseUser(userp);
5393                 lock_ObtainWrite(&cm_scacheLock);
5394                 cm_ReleaseSCacheNoLock(scp);
5395             }
5396             osi_QRemove(&cm_allFileLocks, q);
5397             cm_PutFileLock(fileLock);
5398
5399         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5400
5401             /* Server locks must have been enabled for us to have
5402                received an active non-client-only lock. */
5403             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5404
5405             scp = fileLock->scp;
5406             osi_assertx(scp != NULL, "null cm_scache_t");
5407
5408             cm_HoldSCacheNoLock(scp);
5409
5410 #ifdef DEBUG
5411             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5412                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5413                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5414                          fileLock->fid.cell,
5415                          fileLock->fid.volume,
5416                          fileLock->fid.vnode,
5417                          fileLock->fid.unique);
5418                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5419                          fileLock->scp->fid.cell,
5420                          fileLock->scp->fid.volume,
5421                          fileLock->scp->fid.vnode,
5422                          fileLock->scp->fid.unique);
5423                 osi_assertx(FALSE, "invalid fid");
5424             }
5425 #endif
5426             /* Server locks are extended once per scp per refresh
5427                cycle. */
5428             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5429
5430                 int scp_done = FALSE;
5431
5432                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5433
5434                 lock_ReleaseWrite(&cm_scacheLock);
5435                 lock_ObtainWrite(&scp->rw);
5436
5437                 /* did the lock change while we weren't holding the lock? */
5438                 if (!IS_LOCK_ACTIVE(fileLock))
5439                     goto post_syncopdone;
5440
5441                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5442                                  CM_SCACHESYNC_NEEDCALLBACK
5443                                  | CM_SCACHESYNC_GETSTATUS
5444                                  | CM_SCACHESYNC_LOCK);
5445
5446                 if (code) {
5447                     osi_Log1(afsd_logp,
5448                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5449                     goto post_syncopdone;
5450                 }
5451
5452                 /* cm_SyncOp releases scp->rw during which the lock
5453                    may get released. */
5454                 if (!IS_LOCK_ACTIVE(fileLock))
5455                     goto pre_syncopdone;
5456
5457                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5458                     cm_fid_t cfid;
5459                     cm_user_t * userp;
5460
5461                     tfid.Volume = scp->fid.volume;
5462                     tfid.Vnode = scp->fid.vnode;
5463                     tfid.Unique = scp->fid.unique;
5464                     cfid = scp->fid;
5465                     userp = fileLock->userp;
5466
5467                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5468                              fileLock,
5469                              scp,
5470                              (int) scp->serverLock);
5471
5472                     lock_ReleaseWrite(&scp->rw);
5473
5474                     do {
5475                         code = cm_ConnFromFID(&cfid, userp,
5476                                        &req, &connp);
5477                         if (code)
5478                             break;
5479
5480                         rxconnp = cm_GetRxConn(connp);
5481                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5482                                                 &volSync);
5483                         rx_PutConnection(rxconnp);
5484
5485                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5486
5487                     } while (cm_Analyze(connp, userp, &req,
5488                                         &cfid, &volSync, NULL, NULL,
5489                                         code));
5490
5491                     code = cm_MapRPCError(code, &req);
5492
5493                     lock_ObtainWrite(&scp->rw);
5494
5495                     if (code) {
5496                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5497                         scp->fsLockCount = 0;
5498                     } else {
5499                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5500                         scp->lockDataVersion = scp->dataVersion;
5501                     }
5502
5503                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5504                         scp->lockDataVersion == scp->dataVersion) {
5505                         int lockType;
5506
5507                         lockType =
5508                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5509
5510                         /* we might still have a chance to obtain a
5511                            new lock */
5512
5513                         code = cm_IntSetLock(scp, userp, lockType, &req);
5514
5515                         if (code) {
5516                             code = CM_ERROR_INVAL;
5517                         } else if (scp->lockDataVersion != scp->dataVersion) {
5518
5519                             /* now check if we still have the file at
5520                                the right data version. */
5521                             osi_Log1(afsd_logp,
5522                                      "Data version mismatch on scp 0x%p",
5523                                      scp);
5524                             osi_Log2(afsd_logp,
5525                                      "   Data versions: before=%I64d, after=%I64d",
5526                                      scp->lockDataVersion,
5527                                      scp->dataVersion);
5528
5529                             code = cm_IntReleaseLock(scp, userp, &req);
5530
5531                             code = CM_ERROR_INVAL;
5532                         }
5533                     }
5534
5535                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5536                         code == CM_ERROR_BADFD) {
5537                         cm_LockMarkSCacheLost(scp);
5538                     }
5539
5540                 } else {
5541                     /* interestingly, we have found an active lock
5542                        belonging to an scache that has no
5543                        serverLock */
5544                     cm_LockMarkSCacheLost(scp);
5545                 }
5546
5547                 scp_done = TRUE;
5548
5549             pre_syncopdone:
5550
5551                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5552
5553             post_syncopdone:
5554                 lock_ReleaseWrite(&scp->rw);
5555
5556                 lock_ObtainWrite(&cm_scacheLock);
5557
5558                 if (code == 0) {
5559                     fileLock->lastUpdate = time(NULL);
5560                 }
5561
5562                 if (scp_done)
5563                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5564
5565             } else {
5566                 /* we have already refreshed the locks on this scp */
5567                 fileLock->lastUpdate = time(NULL);
5568             }
5569
5570             cm_ReleaseSCacheNoLock(scp);
5571
5572         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5573             /* TODO: Check callbacks */
5574         }
5575     }
5576
5577     lock_ReleaseWrite(&cm_scacheLock);
5578     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5579 }
5580
5581 /* NOT called with scp->rw held. */
5582 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5583 {
5584     long code = 0;
5585     cm_scache_t *scp = NULL;
5586     cm_file_lock_t *fileLock;
5587     osi_queue_t *q;
5588     cm_req_t req;
5589     int newLock = -1;
5590     int force_client_lock = FALSE;
5591     int has_insert = FALSE;
5592     int check_data_version = FALSE;
5593
5594     cm_InitReq(&req);
5595
5596     if (client_is_dead) {
5597         code = CM_ERROR_TIMEDOUT;
5598         goto updateLock;
5599     }
5600
5601     lock_ObtainRead(&cm_scacheLock);
5602
5603     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5604     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5605              (unsigned)(oldFileLock->range.offset >> 32),
5606              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5607              (unsigned)(oldFileLock->range.length >> 32),
5608              (unsigned)(oldFileLock->range.length & 0xffffffff));
5609     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5610              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5611              (unsigned)(oldFileLock->flags));
5612
5613     /* if the lock has already been granted, then we have nothing to do */
5614     if (IS_LOCK_ACTIVE(oldFileLock)) {
5615         lock_ReleaseRead(&cm_scacheLock);
5616         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5617         return 0;
5618     }
5619
5620     /* we can't do anything with lost or deleted locks at the moment. */
5621     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5622         code = CM_ERROR_BADFD;
5623         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5624         lock_ReleaseRead(&cm_scacheLock);
5625         goto updateLock;
5626     }
5627
5628     scp = oldFileLock->scp;
5629
5630     osi_assertx(scp != NULL, "null cm_scache_t");
5631
5632     lock_ReleaseRead(&cm_scacheLock);
5633     lock_ObtainWrite(&scp->rw);
5634
5635     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5636                              oldFileLock->userp,
5637                              &req, &has_insert);
5638
5639     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5640         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5641         force_client_lock = TRUE;
5642         }
5643         code = 0;
5644     } else if (code) {
5645         lock_ReleaseWrite(&scp->rw);
5646         return code;
5647     }
5648
5649     lock_ObtainWrite(&cm_scacheLock);
5650
5651     /* Check if we already have a sufficient server lock to allow this
5652        lock to go through. */
5653     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5654         (!SERVERLOCKS_ENABLED(scp) ||
5655          scp->serverLock == oldFileLock->lockType ||
5656          scp->serverLock == LockWrite)) {
5657
5658         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5659
5660         if (SERVERLOCKS_ENABLED(scp)) {
5661             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5662                      (int) scp->serverLock);
5663         } else {
5664             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5665         }
5666
5667         lock_ReleaseWrite(&cm_scacheLock);
5668         lock_ReleaseWrite(&scp->rw);
5669
5670         return 0;
5671     }
5672
5673     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5674
5675         /* check if the conflicting locks have dissappeared already */
5676         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5677
5678             fileLock = (cm_file_lock_t *)
5679                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5680
5681             if (IS_LOCK_LOST(fileLock)) {
5682                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5683                     code = CM_ERROR_BADFD;
5684                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5685                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5686                              fileLock);
5687                     break;
5688                 } else if (fileLock->lockType == LockWrite &&
5689                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5690                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5691                     code = CM_ERROR_WOULDBLOCK;
5692                     break;
5693                 }
5694             }
5695
5696             if (IS_LOCK_ACCEPTED(fileLock) &&
5697                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5698
5699                 if (oldFileLock->lockType != LockRead ||
5700                    fileLock->lockType != LockRead) {
5701
5702                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5703                     code = CM_ERROR_WOULDBLOCK;
5704                     break;
5705                 }
5706             }
5707         }
5708     }
5709
5710     if (code != 0) {
5711         lock_ReleaseWrite(&cm_scacheLock);
5712         lock_ReleaseWrite(&scp->rw);
5713
5714         goto handleCode;
5715     }
5716
5717     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5718        If it is WAITUNLOCK, then we didn't find any conflicting lock
5719        but we haven't verfied whether the serverLock is sufficient to
5720        assert it.  If it is WAITLOCK, then the serverLock is
5721        insufficient to assert it. Eitherway, we are ready to accept
5722        the lock as either ACTIVE or WAITLOCK depending on the
5723        serverLock. */
5724
5725     /* First, promote the WAITUNLOCK to a WAITLOCK */
5726     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5727         if (oldFileLock->lockType == LockRead)
5728             scp->sharedLocks++;
5729         else
5730             scp->exclusiveLocks++;
5731
5732         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5733         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5734     }
5735
5736     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5737
5738     if (force_client_lock ||
5739         !SERVERLOCKS_ENABLED(scp) ||
5740         scp->serverLock == oldFileLock->lockType ||
5741         (oldFileLock->lockType == LockRead &&
5742          scp->serverLock == LockWrite)) {
5743
5744         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5745
5746         if ((force_client_lock ||
5747              !SERVERLOCKS_ENABLED(scp)) &&
5748             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5749
5750             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5751
5752             if (oldFileLock->lockType == LockRead)
5753                 scp->sharedLocks--;
5754             else
5755                 scp->exclusiveLocks--;
5756
5757             scp->clientLocks++;
5758         }
5759
5760         lock_ReleaseWrite(&cm_scacheLock);
5761         lock_ReleaseWrite(&scp->rw);
5762
5763         return 0;
5764
5765     } else {
5766         cm_user_t * userp;
5767
5768         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5769                          CM_SCACHESYNC_NEEDCALLBACK
5770                          | CM_SCACHESYNC_GETSTATUS
5771                          | CM_SCACHESYNC_LOCK);
5772         if (code) {
5773             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5774             lock_ReleaseWrite(&cm_scacheLock);
5775             goto post_syncopdone;
5776         }
5777
5778         if (!IS_LOCK_WAITLOCK(oldFileLock))
5779             goto pre_syncopdone;
5780
5781         userp = oldFileLock->userp;
5782
5783 #ifndef AGGRESSIVE_LOCKS
5784         newLock = oldFileLock->lockType;
5785 #else
5786         newLock = LockWrite;
5787 #endif
5788
5789         if (has_insert) {
5790             /* if has_insert is non-zero, then:
5791                - the lock a LockRead
5792                - we don't have permission to get a LockRead
5793                - we do have permission to get a LockWrite
5794                - the server supports VICED_CAPABILITY_WRITELOCKACL
5795             */
5796
5797             newLock = LockWrite;
5798         }
5799
5800         lock_ReleaseWrite(&cm_scacheLock);
5801
5802         /* when we get here, either we have a read-lock and want a
5803            write-lock or we don't have any locks and we want some
5804            lock. */
5805
5806         if (scp->serverLock == LockRead) {
5807
5808             osi_assertx(newLock == LockWrite, "!LockWrite");
5809
5810             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
5811
5812             scp->lockDataVersion = scp->dataVersion;
5813             check_data_version = TRUE;
5814
5815             code = cm_IntReleaseLock(scp, userp, &req);
5816
5817             if (code)
5818                 goto pre_syncopdone;
5819             else
5820                 scp->serverLock = -1;
5821         }
5822
5823         code = cm_IntSetLock(scp, userp, newLock, &req);
5824
5825         if (code == 0) {
5826             if (scp->dataVersion != scp->lockDataVersion) {
5827                 /* we lost a race.  too bad */
5828
5829                 osi_Log0(afsd_logp,
5830                          "  Data version mismatch while upgrading lock.");
5831                 osi_Log2(afsd_logp,
5832                          "  Data versions before=%I64d, after=%I64d",
5833                          scp->lockDataVersion,
5834                          scp->dataVersion);
5835                 osi_Log1(afsd_logp,
5836                          "  Releasing stale lock for scp 0x%x", scp);
5837
5838                 code = cm_IntReleaseLock(scp, userp, &req);
5839
5840                 scp->serverLock = -1;
5841
5842                 code = CM_ERROR_INVAL;
5843
5844                 cm_LockMarkSCacheLost(scp);
5845             } else {
5846                 scp->serverLock = newLock;
5847             }
5848         }
5849
5850     pre_syncopdone:
5851         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5852     post_syncopdone:
5853         ;
5854     }
5855
5856   handleCode:
5857     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
5858         lock_ObtainWrite(&cm_scacheLock);
5859         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
5860         lock_ReleaseWrite(&cm_scacheLock);
5861     }
5862     lock_ReleaseWrite(&scp->rw);
5863
5864   updateLock:
5865     lock_ObtainWrite(&cm_scacheLock);
5866     if (code == 0) {
5867         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5868     } else if (code != CM_ERROR_WOULDBLOCK) {
5869         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5870         cm_ReleaseUser(oldFileLock->userp);
5871         oldFileLock->userp = NULL;
5872         if (oldFileLock->scp) {
5873             cm_ReleaseSCacheNoLock(oldFileLock->scp);
5874             oldFileLock->scp = NULL;
5875         }
5876     }
5877     lock_ReleaseWrite(&cm_scacheLock);
5878
5879     return code;
5880 }
5881
5882 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint16 file_id)
5883 {
5884     cm_key_t key;
5885
5886     key.process_id = process_id;
5887     key.session_id = session_id;
5888     key.file_id = file_id;
5889
5890     return key;
5891 }
5892
5893 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
5894 {
5895     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
5896         ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
5897 }
5898
5899 void cm_ReleaseAllLocks(void)
5900 {
5901     cm_scache_t *scp;
5902     cm_req_t req;
5903     cm_user_t *userp;
5904     cm_key_t   key;
5905     cm_file_lock_t *fileLock;
5906     unsigned int i;
5907
5908     for (i = 0; i < cm_data.scacheHashTableSize; i++)
5909     {
5910         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
5911             while (scp->fileLocksH != NULL) {
5912                 lock_ObtainWrite(&scp->rw);
5913                 lock_ObtainWrite(&cm_scacheLock);
5914                 if (!scp->fileLocksH) {
5915                     lock_ReleaseWrite(&cm_scacheLock);
5916                     lock_ReleaseWrite(&scp->rw);
5917                     break;
5918                 }
5919                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
5920                 userp = fileLock->userp;
5921                 cm_HoldUser(userp);
5922                 key = fileLock->key;
5923                 cm_HoldSCacheNoLock(scp);
5924                 lock_ReleaseWrite(&cm_scacheLock);
5925                 cm_UnlockByKey(scp, key, 0, userp, &req);
5926                 cm_ReleaseSCache(scp);
5927                 cm_ReleaseUser(userp);
5928                 lock_ReleaseWrite(&scp->rw);
5929             }
5930         }
5931     }
5932 }