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