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