Windows: [Inline]BulkStat VolSync not accurate?
[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, NULL, 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     cm_fid_t fids[MAX_FID_COUNT]; /* array of fids processed in this path walk */
1973     int fid_count = 0;          /* number of fids processed in this path walk */
1974     int i;
1975
1976     *outScpp = NULL;
1977
1978 #ifdef DEBUG_REFCOUNT
1979     afsi_log("%s:%d cm_NameI rootscp 0x%p ref %d", file, line, rootSCachep, rootSCachep->refCount);
1980     osi_Log4(afsd_logp,"cm_NameI rootscp 0x%p path %S tidpath %S flags 0x%x",
1981              rootSCachep, pathp ? pathp : L"<NULL>", tidPathp ? tidPathp : L"<NULL>",
1982              flags);
1983 #endif
1984
1985     tp = tidPathp;
1986     if (tp == NULL) {
1987         tp = pathp;
1988         phase = 2;
1989     }
1990     if (tp == NULL) {
1991         tp = _C("");
1992     }
1993     haveComponent = 0;
1994     psp = NULL;
1995     tscp = rootSCachep;
1996     cm_HoldSCache(tscp);
1997     symlinkCount = 0;
1998     dirScp = NULL;
1999
2000
2001     while (1) {
2002         tc = *tp++;
2003
2004         /* map Unix slashes into DOS ones so we can interpret Unix
2005          * symlinks properly
2006          */
2007         if (tc == '/')
2008             tc = '\\';
2009
2010         if (!haveComponent) {
2011             if (tc == '\\') {
2012                 continue;
2013             } else if (tc == 0) {
2014                 if (phase == 1) {
2015                     phase = 2;
2016                     tp = pathp;
2017                     continue;
2018                 }
2019                 code = 0;
2020                 break;
2021             } else {
2022                 haveComponent = 1;
2023                 cp = component;
2024                 *cp++ = tc;
2025             }
2026         } else {
2027             /* we have a component here */
2028             if (tc == 0 || tc == '\\') {
2029                 /* end of the component; we're at the last
2030                  * component if tc == 0.  However, if the last
2031                  * is a symlink, we have more to do.
2032                  */
2033                 *cp++ = 0;      /* add null termination */
2034                 extraFlag = 0;
2035                 if ((flags & CM_FLAG_DIRSEARCH) && tc == 0)
2036                     extraFlag = CM_FLAG_NOMOUNTCHASE;
2037                 code = cm_Lookup(tscp, component,
2038                                  flags | extraFlag,
2039                                  userp, reqp, &nscp);
2040
2041                 if (code == 0) {
2042                     if (!cm_ClientStrCmp(component,_C("..")) ||
2043                         !cm_ClientStrCmp(component,_C("."))) {
2044                         /*
2045                          * roll back the fid list until we find the
2046                          * fid that matches where we are now.  Its not
2047                          * necessarily one or two fids because they
2048                          * might have been symlinks or mount points or
2049                          * both that were crossed.
2050                          */
2051                         for ( i=fid_count-1; i>=0; i--) {
2052                             if (!cm_FidCmp(&nscp->fid, &fids[i]))
2053                                 break;
2054                         }
2055                         fid_count = i+1;
2056                     } else {
2057                         /* add the new fid to the list */
2058                         if (fid_count == MAX_FID_COUNT) {
2059                             code = CM_ERROR_TOO_MANY_SYMLINKS;
2060                             cm_ReleaseSCache(nscp);
2061                             nscp = NULL;
2062                             break;
2063                         }
2064                         fids[fid_count++] = nscp->fid;
2065                     }
2066                 }
2067
2068                 if (code) {
2069                     cm_ReleaseSCache(tscp);
2070                     if (dirScp)
2071                         cm_ReleaseSCache(dirScp);
2072                     if (psp)
2073                         cm_FreeSpace(psp);
2074                     if ((code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) &&
2075                         tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2076                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_NOSUCHPATH");
2077                         return CM_ERROR_NOSUCHPATH;
2078                     } else {
2079                         osi_Log1(afsd_logp,"cm_NameI code 0x%x", code);
2080                         return code;
2081                     }
2082                 }
2083
2084                 haveComponent = 0;      /* component done */
2085                 if (dirScp)
2086                     cm_ReleaseSCache(dirScp);
2087                 dirScp = tscp;          /* for some symlinks */
2088                 tscp = nscp;            /* already held */
2089                 nscp = NULL;
2090                 if (tc == 0 && !(flags & CM_FLAG_FOLLOW) && phase == 2) {
2091                     code = 0;
2092                     if (dirScp) {
2093                         cm_ReleaseSCache(dirScp);
2094                         dirScp = NULL;
2095                     }
2096                     break;
2097                 }
2098
2099                 /* now, if tscp is a symlink, we should follow it and
2100                  * assemble the path again.
2101                  */
2102                 lock_ObtainWrite(&tscp->rw);
2103                 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
2104                                   CM_SCACHESYNC_GETSTATUS
2105                                   | CM_SCACHESYNC_NEEDCALLBACK);
2106                 if (code) {
2107                     lock_ReleaseWrite(&tscp->rw);
2108                     cm_ReleaseSCache(tscp);
2109                     tscp = NULL;
2110                     if (dirScp) {
2111                         cm_ReleaseSCache(dirScp);
2112                         dirScp = NULL;
2113                     }
2114                     break;
2115                 }
2116                 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2117
2118                 if (tscp->fileType == CM_SCACHETYPE_SYMLINK) {
2119                     /* this is a symlink; assemble a new buffer */
2120                     lock_ReleaseWrite(&tscp->rw);
2121                     if (symlinkCount++ >= MAX_SYMLINK_COUNT) {
2122                         cm_ReleaseSCache(tscp);
2123                         tscp = NULL;
2124                         if (dirScp) {
2125                             cm_ReleaseSCache(dirScp);
2126                             dirScp = NULL;
2127                         }
2128                         if (psp)
2129                             cm_FreeSpace(psp);
2130                         osi_Log0(afsd_logp,"cm_NameI code CM_ERROR_TOO_MANY_SYMLINKS");
2131                         return CM_ERROR_TOO_MANY_SYMLINKS;
2132                     }
2133                     if (tc == 0)
2134                         restp = _C("");
2135                     else
2136                         restp = tp;
2137
2138                     {
2139                         fschar_t * frestp;
2140
2141                         /* TODO: make this better */
2142                         frestp = cm_ClientStringToFsStringAlloc(restp, -1, NULL);
2143                         code = cm_AssembleLink(tscp, frestp, &linkScp, &tempsp, userp, reqp);
2144                         free(frestp);
2145                     }
2146
2147                     if (code == 0 && linkScp != NULL) {
2148                         if (linkScp == cm_data.rootSCachep) {
2149                             fid_count = 0;
2150                             i = 0;
2151                         } else {
2152                             for ( i=0; i<fid_count; i++) {
2153                                 if ( !cm_FidCmp(&linkScp->fid, &fids[i]) ) {
2154                                     code = CM_ERROR_TOO_MANY_SYMLINKS;
2155                                     cm_ReleaseSCache(linkScp);
2156                                     nscp = NULL;
2157                                     break;
2158                                 }
2159                             }
2160                         }
2161                         if (i == fid_count && fid_count < MAX_FID_COUNT) {
2162                             fids[fid_count++] = linkScp->fid;
2163                         }
2164                     }
2165
2166                     if (code) {
2167                         /* something went wrong */
2168                         cm_ReleaseSCache(tscp);
2169                         tscp = NULL;
2170                         if (dirScp) {
2171                             cm_ReleaseSCache(dirScp);
2172                             dirScp = NULL;
2173                         }
2174                         break;
2175                     }
2176
2177                     /* otherwise, tempsp has the new path,
2178                      * and linkScp is the new root from
2179                      * which to interpret that path.
2180                      * Continue with the namei processing,
2181                      * also doing the bookkeeping for the
2182                      * space allocation and tracking the
2183                      * vnode reference counts.
2184                      */
2185                     if (psp)
2186                         cm_FreeSpace(psp);
2187                     psp = tempsp;
2188                     tp = psp->wdata;
2189                     cm_ReleaseSCache(tscp);
2190                     tscp = linkScp;
2191                     linkScp = NULL;
2192                     /* already held
2193                      * by AssembleLink
2194                      * now, if linkScp is null, that's
2195                      * AssembleLink's way of telling us that
2196                      * the sym link is relative to the dir
2197                      * containing the link.  We have a ref
2198                      * to it in dirScp, and we hold it now
2199                      * and reuse it as the new spot in the
2200                      * dir hierarchy.
2201                      */
2202                     if (tscp == NULL) {
2203                         tscp = dirScp;
2204                         dirScp = NULL;
2205                     }
2206                 } else {
2207                     /* not a symlink, we may be done */
2208                     lock_ReleaseWrite(&tscp->rw);
2209                     if (tc == 0) {
2210                         if (phase == 1) {
2211                             phase = 2;
2212                             tp = pathp;
2213                             continue;
2214                         }
2215                         if (dirScp) {
2216                             cm_ReleaseSCache(dirScp);
2217                             dirScp = NULL;
2218                         }
2219                         code = 0;
2220                         break;
2221                     }
2222                 }
2223                 if (dirScp) {
2224                     cm_ReleaseSCache(dirScp);
2225                     dirScp = NULL;
2226                 }
2227             } /* end of a component */
2228             else
2229                 *cp++ = tc;
2230         } /* we have a component */
2231     } /* big while loop over all components */
2232
2233     /* already held */
2234     if (dirScp)
2235         cm_ReleaseSCache(dirScp);
2236     if (psp)
2237         cm_FreeSpace(psp);
2238     if (code == 0)
2239         *outScpp = tscp;
2240     else if (tscp)
2241         cm_ReleaseSCache(tscp);
2242
2243 #ifdef DEBUG_REFCOUNT
2244     afsi_log("%s:%d cm_NameI code 0x%x outScpp 0x%p ref %d", file, line, code, *outScpp, (*outScpp) ? (*outScpp)->refCount : 0);
2245 #endif
2246     osi_Log2(afsd_logp,"cm_NameI code 0x%x outScpp 0x%p", code, *outScpp);
2247     return code;
2248 }
2249
2250 /* called with a dir, and a vnode within the dir that happens to be a symlink.
2251  * We chase the link, and return a held pointer to the target, if it exists,
2252  * in *outScpp.  If we succeed, we return 0, otherwise we return an error code
2253  * and do not hold or return a target vnode.
2254  *
2255  * This is very similar to calling cm_NameI with the last component of a name,
2256  * which happens to be a symlink, except that we've already passed by the name.
2257  *
2258  * This function is typically called by the directory listing functions, which
2259  * encounter symlinks but need to return the proper file length so programs
2260  * like "more" work properly when they make use of the attributes retrieved from
2261  * the dir listing.
2262  *
2263  * The input vnode should not be locked when this function is called.
2264  */
2265 long cm_EvaluateSymLink(cm_scache_t *dscp, cm_scache_t *linkScp,
2266                          cm_scache_t **outScpp, cm_user_t *userp, cm_req_t *reqp)
2267 {
2268     long code;
2269     cm_space_t *spacep;
2270     cm_scache_t *newRootScp;
2271
2272     *outScpp = NULL;
2273
2274     osi_Log1(afsd_logp, "Evaluating symlink scp 0x%p", linkScp);
2275
2276     code = cm_AssembleLink(linkScp, "", &newRootScp, &spacep, userp, reqp);
2277     if (code)
2278         return code;
2279
2280     /* now, if newRootScp is NULL, we're really being told that the symlink
2281      * is relative to the current directory (dscp).
2282      */
2283     if (newRootScp == NULL) {
2284         newRootScp = dscp;
2285         cm_HoldSCache(dscp);
2286     }
2287
2288     code = cm_NameI(newRootScp, spacep->wdata,
2289                     CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW | CM_FLAG_DIRSEARCH,
2290                     userp, NULL, reqp, outScpp);
2291
2292     if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH)
2293         code = CM_ERROR_NOSUCHPATH;
2294
2295     /* this stuff is allocated no matter what happened on the namei call,
2296      * so free it */
2297     cm_FreeSpace(spacep);
2298     cm_ReleaseSCache(newRootScp);
2299
2300     if (linkScp == *outScpp) {
2301         cm_ReleaseSCache(*outScpp);
2302         *outScpp = NULL;
2303         code = CM_ERROR_NOSUCHPATH;
2304     }
2305
2306     return code;
2307 }
2308
2309 /* for a given entry, make sure that it isn't in the stat cache, and then
2310  * add it to the list of file IDs to be obtained.
2311  *
2312  * Don't bother adding it if we already have a vnode.  Note that the dir
2313  * is locked, so we have to be careful checking the vnode we're thinking of
2314  * processing, to avoid deadlocks.
2315  */
2316 long cm_TryBulkProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
2317                      osi_hyper_t *offp)
2318 {
2319     osi_hyper_t thyper;
2320     cm_bulkStat_t *bsp;
2321     int i;
2322     cm_scache_t *tscp;
2323     cm_fid_t tfid;
2324
2325     bsp = rockp;
2326
2327     /* Don't overflow bsp. */
2328     if (bsp->counter >= CM_BULKMAX)
2329         return CM_ERROR_STOPNOW;
2330
2331     thyper.LowPart = cm_data.buf_blockSize;
2332     thyper.HighPart = 0;
2333     thyper = LargeIntegerAdd(thyper, bsp->bufOffset);
2334
2335     /* thyper is now the first byte past the end of the record we're
2336      * interested in, and bsp->bufOffset is the first byte of the record
2337      * we're interested in.
2338      * Skip data in the others.
2339      * Skip '.' and '..'
2340      */
2341     if (LargeIntegerLessThan(*offp, bsp->bufOffset))
2342         return 0;
2343     if (LargeIntegerGreaterThanOrEqualTo(*offp, thyper))
2344         return CM_ERROR_STOPNOW;
2345     if (strcmp(dep->name, ".") == 0 || strcmp(dep->name, "..") == 0)
2346         return 0;
2347
2348     cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
2349     tscp = cm_FindSCache(&tfid);
2350     if (tscp) {
2351         if (lock_TryWrite(&tscp->rw)) {
2352             /* we have an entry that we can look at */
2353             if (!cm_EAccesFindEntry(bsp->userp, &tscp->fid) && cm_HaveCallback(tscp)) {
2354                 /* we have a callback on it.  Don't bother
2355                  * fetching this stat entry, since we're happy
2356                  * with the info we have.
2357                  */
2358                 lock_ReleaseWrite(&tscp->rw);
2359                 cm_ReleaseSCache(tscp);
2360                 return 0;
2361             }
2362             lock_ReleaseWrite(&tscp->rw);
2363         }       /* got lock */
2364         cm_ReleaseSCache(tscp);
2365     }   /* found entry */
2366
2367 #ifdef AFS_FREELANCE_CLIENT
2368     // yj: if this is a mountpoint under root.afs then we don't want it
2369     // to be bulkstat-ed, instead, we call getSCache directly and under
2370     // getSCache, it is handled specially.
2371     if  ( cm_freelanceEnabled &&
2372           tfid.cell==AFS_FAKE_ROOT_CELL_ID &&
2373           tfid.volume==AFS_FAKE_ROOT_VOL_ID &&
2374           !(tfid.vnode==0x1 && tfid.unique==0x1) )
2375     {
2376         osi_Log0(afsd_logp, "cm_TryBulkProc Freelance calls cm_SCache on root.afs mountpoint");
2377         return cm_GetSCache(&tfid, NULL, &tscp, NULL, NULL);
2378     }
2379 #endif /* AFS_FREELANCE_CLIENT */
2380
2381     i = bsp->counter++;
2382     bsp->fids[i].Volume = scp->fid.volume;
2383     bsp->fids[i].Vnode = tfid.vnode;
2384     bsp->fids[i].Unique = tfid.unique;
2385     return 0;
2386 }
2387
2388 afs_int32
2389 cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_req_t *reqp)
2390 {
2391     afs_int32 code = 0;
2392     AFSCBFids fidStruct;
2393     AFSBulkStats statStruct;
2394     cm_conn_t *connp;
2395     AFSCBs callbackStruct;
2396     long filex;
2397     AFSVolSync volSync;
2398     cm_callbackRequest_t cbReq;
2399     int lostRace;
2400     long filesThisCall;
2401     long i;
2402     long j;
2403     cm_scache_t *scp;
2404     cm_fid_t tfid;
2405     struct rx_connection * rxconnp;
2406     int inlinebulk;             /* Did we use InlineBulkStatus RPC or not? */
2407
2408     memset(&volSync, 0, sizeof(volSync));
2409
2410     /* otherwise, we may have one or more bulk stat's worth of stuff in bb;
2411      * make the calls to create the entries.  Handle AFSCBMAX files at a
2412      * time.
2413      */
2414     for (filex = 0; filex < bbp->counter; filex += filesThisCall) {
2415         filesThisCall = bbp->counter - filex;
2416         if (filesThisCall > AFSCBMAX)
2417             filesThisCall = AFSCBMAX;
2418
2419         fidStruct.AFSCBFids_len = filesThisCall;
2420         fidStruct.AFSCBFids_val = &bbp->fids[filex];
2421         statStruct.AFSBulkStats_len = filesThisCall;
2422         statStruct.AFSBulkStats_val = &bbp->stats[filex];
2423         callbackStruct.AFSCBs_len = filesThisCall;
2424         callbackStruct.AFSCBs_val = &bbp->callbacks[filex];
2425         cm_StartCallbackGrantingCall(NULL, &cbReq);
2426         osi_Log1(afsd_logp, "CALL BulkStatus, %d entries", filesThisCall);
2427
2428         /*
2429          * Whenever cm_Analyze is called for a RXAFS_ RPC there must
2430          * be a FID provided.  However, the error code from RXAFS_BulkStatus
2431          * or RXAFS_InlinkBulkStatus does not apply to any FID.  Therefore,
2432          * we generate an invalid FID to match with the RPC error.
2433          */
2434         cm_SetFid(&tfid, dscp->fid.cell, dscp->fid.volume, 0, 0);
2435
2436         do {
2437             inlinebulk = 0;
2438
2439             code = cm_ConnFromFID(&tfid, userp, reqp, &connp);
2440             if (code)
2441                 continue;
2442
2443             rxconnp = cm_GetRxConn(connp);
2444             if (!(connp->serverp->flags & CM_SERVERFLAG_NOINLINEBULK)) {
2445                 code = RXAFS_InlineBulkStatus(rxconnp, &fidStruct,
2446                                               &statStruct, &callbackStruct, &volSync);
2447                 if (code == RXGEN_OPCODE) {
2448                     cm_SetServerNoInlineBulk(connp->serverp, 0);
2449                 } else {
2450                     inlinebulk = 1;
2451                 }
2452             }
2453             if (!inlinebulk) {
2454                 code = RXAFS_BulkStatus(rxconnp, &fidStruct,
2455                                         &statStruct, &callbackStruct, &volSync);
2456             }
2457             rx_PutConnection(rxconnp);
2458
2459             /*
2460              * If InlineBulk RPC was called and it succeeded,
2461              * then pull out the return code from the status info
2462              * and use it for cm_Analyze so that we can failover to other
2463              * .readonly volume instances.  But only do it for errors that
2464              * are volume global.
2465              */
2466             if (inlinebulk && code == 0 && (&bbp->stats[0])->errorCode) {
2467                 osi_Log1(afsd_logp, "cm_TryBulkStat inline-bulk stat error: %d",
2468                           (&bbp->stats[0])->errorCode);
2469                 switch ((&bbp->stats[0])->errorCode) {
2470                 case VBUSY:
2471                 case VRESTARTING:
2472                 case VNOVOL:
2473                 case VMOVED:
2474                 case VOFFLINE:
2475                 case VSALVAGE:
2476                 case VNOSERVICE:
2477                 case VIO:
2478                     code = (&bbp->stats[0])->errorCode;
2479                     break;
2480                 default:
2481                     /* Rx and Rxkad errors are volume global */
2482                     if ( (&bbp->stats[0])->errorCode >= -64 && (&bbp->stats[0])->errorCode < 0 ||
2483                          (&bbp->stats[0])->errorCode >= ERROR_TABLE_BASE_RXK && (&bbp->stats[0])->errorCode < ERROR_TABLE_BASE_RXK + 256)
2484                         code = (&bbp->stats[0])->errorCode;
2485                 }
2486             }
2487         } while (cm_Analyze(connp, userp, reqp, &tfid, NULL, 0, &volSync, NULL, &cbReq, code));
2488         code = cm_MapRPCError(code, reqp);
2489
2490         /*
2491          * might as well quit on an error, since we're not going to do
2492          * much better on the next immediate call, either.
2493          */
2494         if (code) {
2495             osi_Log2(afsd_logp, "CALL %sBulkStatus FAILURE code 0x%x",
2496                       inlinebulk ? "Inline" : "", code);
2497             cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2498             break;
2499         }
2500
2501         /*
2502          * The bulk RPC has succeeded or at least not failed with a
2503          * volume global error result.  For items that have inlineBulk
2504          * errors we must call cm_Analyze in order to perform required
2505          * logging of errors.
2506          *
2507          * If the RPC was not inline bulk or the entry either has no error
2508          * the status must be merged.
2509          */
2510         osi_Log1(afsd_logp, "CALL %sBulkStatus SUCCESS", inlinebulk ? "Inline" : "");
2511
2512         for (i = 0; i<filesThisCall; i++) {
2513             j = filex + i;
2514             cm_SetFid(&tfid, dscp->fid.cell, bbp->fids[j].Volume, bbp->fids[j].Vnode, bbp->fids[j].Unique);
2515
2516             if (inlinebulk && (&bbp->stats[j])->errorCode) {
2517                 cm_req_t treq = *reqp;
2518                 cm_Analyze(NULL, userp, &treq, &tfid, NULL, 0, &volSync, NULL, &cbReq, (&bbp->stats[j])->errorCode);
2519                 switch ((&bbp->stats[j])->errorCode) {
2520                 case EACCES:
2521                 case UAEACCES:
2522                 case EPERM:
2523                 case UAEPERM:
2524                     cm_EAccesAddEntry(userp, &tfid, &dscp->fid);
2525                 }
2526             } else {
2527                 code = cm_GetSCache(&tfid, &dscp->fid, &scp, userp, reqp);
2528                 if (code != 0)
2529                     continue;
2530
2531                 /*
2532                  * otherwise, if this entry has no callback info,
2533                  * merge in this.  If there is existing callback info
2534                  * we skip the merge because the existing data must be
2535                  * current (we have a callback) and the response from
2536                  * a non-inline bulk rpc might actually be wrong.
2537                  *
2538                  * now, we have to be extra paranoid on merging in this
2539                  * information, since we didn't use cm_SyncOp before
2540                  * starting the fetch to make sure that no bad races
2541                  * were occurring.  Specifically, we need to make sure
2542                  * we don't obliterate any newer information in the
2543                  * vnode than have here.
2544                  *
2545                  * Right now, be pretty conservative: if there's a
2546                  * callback or a pending call, skip it.
2547                  * However, if the prior attempt to obtain status
2548                  * was refused access or the volume is .readonly,
2549                  * take the data in any case since we have nothing
2550                  * better for the in flight directory enumeration that
2551                  * resulted in this function being called.
2552                  */
2553                 lock_ObtainRead(&scp->rw);
2554                 if ((scp->cbServerp == NULL &&
2555                      !(scp->flags & (CM_SCACHEFLAG_FETCHING | CM_SCACHEFLAG_STORING | CM_SCACHEFLAG_SIZESTORING))) ||
2556                      (scp->flags & CM_SCACHEFLAG_PURERO) ||
2557                      cm_EAccesFindEntry(userp, &scp->fid))
2558                 {
2559                     lock_ConvertRToW(&scp->rw);
2560                     lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
2561                                                           &bbp->callbacks[j],
2562                                                           &volSync,
2563                                                           CM_CALLBACK_MAINTAINCOUNT|CM_CALLBACK_BULKSTAT);
2564                     InterlockedIncrement(&scp->activeRPCs);
2565                     if (!lostRace)
2566                         cm_MergeStatus(dscp, scp, &bbp->stats[j], &volSync, userp, reqp, CM_MERGEFLAG_BULKSTAT);
2567                     lock_ReleaseWrite(&scp->rw);
2568                 } else {
2569                     lock_ReleaseRead(&scp->rw);
2570                 }
2571                 cm_ReleaseSCache(scp);
2572             }
2573         } /* all files in the response */
2574         /* now tell it to drop the count,
2575          * after doing the vnode processing above */
2576         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
2577     }   /* while there are still more files to process */
2578
2579     return code;
2580 }
2581
2582 /* called with a write locked scp and a pointer to a buffer.  Make bulk stat
2583  * calls on all undeleted files in the page of the directory specified.
2584  */
2585 afs_int32
2586 cm_TryBulkStat(cm_scache_t *dscp, osi_hyper_t *offsetp, cm_user_t *userp,
2587                cm_req_t *reqp)
2588 {
2589     afs_int32 code;
2590     cm_bulkStat_t *bbp;
2591
2592     osi_Log1(afsd_logp, "cm_TryBulkStat dir 0x%p", dscp);
2593
2594     /* should be on a buffer boundary */
2595     osi_assertx((offsetp->LowPart & (cm_data.buf_blockSize - 1)) == 0, "invalid offset");
2596
2597     bbp = malloc(sizeof(cm_bulkStat_t));
2598     memset(bbp, 0, sizeof(cm_bulkStat_t));
2599     bbp->userp = userp;
2600     bbp->bufOffset = *offsetp;
2601
2602     lock_ReleaseWrite(&dscp->rw);
2603     /* first, assemble the file IDs we need to stat */
2604     code = cm_ApplyDir(dscp, cm_TryBulkProc, (void *) bbp, offsetp, userp, reqp, NULL);
2605
2606     /* if we failed, bail out early */
2607     if (code && code != CM_ERROR_STOPNOW) {
2608         free(bbp);
2609         lock_ObtainWrite(&dscp->rw);
2610         return code;
2611     }
2612
2613     code = cm_TryBulkStatRPC(dscp, bbp, userp, reqp);
2614     osi_Log1(afsd_logp, "END cm_TryBulkStat code 0x%x", code);
2615
2616     lock_ObtainWrite(&dscp->rw);
2617     free(bbp);
2618     return 0;
2619 }
2620
2621 void cm_StatusFromAttr(AFSStoreStatus *statusp, cm_scache_t *scp, cm_attr_t *attrp)
2622 {
2623     long mask;
2624
2625     /* initialize store back mask as inexpensive local variable */
2626     mask = 0;
2627     memset(statusp, 0, sizeof(AFSStoreStatus));
2628
2629     /* copy out queued info from scache first, if scp passed in */
2630     if (scp) {
2631         if (scp->mask & CM_SCACHEMASK_CLIENTMODTIME) {
2632             statusp->ClientModTime = scp->clientModTime;
2633             mask |= AFS_SETMODTIME;
2634             scp->mask &= ~CM_SCACHEMASK_CLIENTMODTIME;
2635         }
2636     }
2637
2638     if (attrp) {
2639         /* now add in our locally generated request */
2640         if (attrp->mask & CM_ATTRMASK_CLIENTMODTIME) {
2641             statusp->ClientModTime = attrp->clientModTime;
2642             mask |= AFS_SETMODTIME;
2643         }
2644         if (attrp->mask & CM_ATTRMASK_UNIXMODEBITS) {
2645             statusp->UnixModeBits = attrp->unixModeBits;
2646             mask |= AFS_SETMODE;
2647         }
2648         if (attrp->mask & CM_ATTRMASK_OWNER) {
2649             statusp->Owner = attrp->owner;
2650             mask |= AFS_SETOWNER;
2651         }
2652         if (attrp->mask & CM_ATTRMASK_GROUP) {
2653             statusp->Group = attrp->group;
2654             mask |= AFS_SETGROUP;
2655         }
2656     }
2657     statusp->Mask = mask;
2658 }
2659
2660 int
2661 cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_req_t *reqp)
2662 {
2663     int spaceAvail = 1;
2664     afs_uint32  code;
2665     cm_conn_t *connp;
2666     struct rx_connection * rxconnp;
2667     AFSFetchVolumeStatus volStat;
2668     cm_volume_t *volp = NULL;
2669     afs_uint32   volType;
2670     char *Name;
2671     char *OfflineMsg;
2672     char *MOTD;
2673     char volName[32]="(unknown)";
2674     char offLineMsg[256]="server temporarily inaccessible";
2675     char motd[256]="server temporarily inaccessible";
2676     osi_hyper_t freespace;
2677
2678     if (fidp->cell==AFS_FAKE_ROOT_CELL_ID &&
2679         fidp->volume==AFS_FAKE_ROOT_VOL_ID)
2680     {
2681         goto _done;
2682     }
2683
2684     volp = cm_GetVolumeByFID(fidp);
2685     if (!volp) {
2686         spaceAvail = 0;
2687         goto _done;
2688     }
2689     volType = cm_VolumeType(volp, fidp->volume);
2690     if (volType == ROVOL || volType == BACKVOL) {
2691         spaceAvail = 0;
2692         goto _done;
2693     }
2694
2695     Name = volName;
2696     OfflineMsg = offLineMsg;
2697     MOTD = motd;
2698
2699     do {
2700         code = cm_ConnFromFID(fidp, userp, reqp, &connp);
2701         if (code) continue;
2702
2703         rxconnp = cm_GetRxConn(connp);
2704         code = RXAFS_GetVolumeStatus(rxconnp, fidp->volume,
2705                                      &volStat, &Name, &OfflineMsg, &MOTD);
2706         rx_PutConnection(rxconnp);
2707
2708     } while (cm_Analyze(connp, userp, reqp, fidp, NULL, 0, NULL, NULL, NULL, code));
2709     code = cm_MapRPCError(code, reqp);
2710     if (code == 0) {
2711         if (volStat.MaxQuota) {
2712             freespace.QuadPart = 1024 * (afs_int64)min(volStat.MaxQuota - volStat.BlocksInUse, volStat.PartBlocksAvail);
2713         } else {
2714             freespace.QuadPart = 1024 * (afs_int64)volStat.PartBlocksAvail;
2715         }
2716         spaceAvail = LargeIntegerGreaterThanOrEqualTo(freespace, *sizep);
2717     }
2718     /* the rpc failed, assume there is space and we can fail it later. */
2719
2720   _done:
2721     if (volp)
2722         cm_PutVolume(volp);
2723
2724     return spaceAvail;
2725 }
2726
2727 /* set the file size, and make sure that all relevant buffers have been
2728  * truncated.  Ensure that any partially truncated buffers have been zeroed
2729  * to the end of the buffer.
2730  */
2731 long cm_SetLength(cm_scache_t *scp, osi_hyper_t *sizep, cm_user_t *userp,
2732                    cm_req_t *reqp)
2733 {
2734     long code;
2735     int shrinking;
2736
2737     /* start by locking out buffer creation */
2738     lock_ObtainWrite(&scp->bufCreateLock);
2739
2740     /* verify that this is a file, not a dir or a symlink */
2741     lock_ObtainWrite(&scp->rw);
2742     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2743                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2744     if (code)
2745         goto done;
2746     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2747
2748     if (scp->fileType != CM_SCACHETYPE_FILE) {
2749         code = CM_ERROR_ISDIR;
2750         goto done;
2751     }
2752
2753   startover:
2754     if (LargeIntegerLessThan(*sizep, scp->length))
2755         shrinking = 1;
2756     else
2757         shrinking = 0;
2758
2759     lock_ReleaseWrite(&scp->rw);
2760
2761     /* can't hold scp->rw lock here, since we may wait for a storeback to
2762      * finish if the buffer package is cleaning a buffer by storing it to
2763      * the server.
2764      */
2765     if (shrinking)
2766         buf_Truncate(scp, userp, reqp, sizep);
2767
2768     /* now ensure that file length is short enough, and update truncPos */
2769     lock_ObtainWrite(&scp->rw);
2770
2771     /* make sure we have a callback (so we have the right value for the
2772      * length), and wait for it to be safe to do a truncate.
2773      */
2774     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2775                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2776                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2777
2778     /* If we only have 'i' bits, then we should still be able to set
2779        the size of a file we created. */
2780     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2781         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2782                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2783                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2784     }
2785
2786     if (code)
2787         goto done;
2788
2789     if (LargeIntegerLessThan(*sizep, scp->length)) {
2790         /* a real truncation.  If truncPos is not set yet, or is bigger
2791          * than where we're truncating the file, set truncPos to this
2792          * new value.
2793          */
2794         if (!shrinking)
2795             goto startover;
2796         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2797              || LargeIntegerLessThan(*sizep, scp->length)) {
2798             /* set trunc pos */
2799             scp->truncPos = *sizep;
2800             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2801         }
2802         /* in either case, the new file size has been changed */
2803         scp->length = *sizep;
2804         scp->mask |= CM_SCACHEMASK_LENGTH;
2805     }
2806     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2807         /* really extending the file */
2808         /* Check to see if we have sufficient quota */
2809         if (cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp)) {
2810             scp->length = *sizep;
2811             scp->mask |= CM_SCACHEMASK_LENGTH;
2812         } else {
2813             code = CM_ERROR_SPACE;
2814             goto syncopdone;
2815         }
2816     }
2817
2818     /* done successfully */
2819     code = 0;
2820
2821   syncopdone:
2822     cm_SyncOpDone(scp, NULL,
2823                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2824                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2825
2826   done:
2827     lock_ReleaseWrite(&scp->rw);
2828     lock_ReleaseWrite(&scp->bufCreateLock);
2829
2830     return code;
2831 }
2832
2833 /* set the file size or other attributes (but not both at once) */
2834 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2835                 cm_req_t *reqp)
2836 {
2837     long code;
2838     AFSFetchStatus afsOutStatus;
2839     AFSVolSync volSync;
2840     cm_conn_t *connp;
2841     AFSFid tfid;
2842     AFSStoreStatus afsInStatus;
2843     struct rx_connection * rxconnp;
2844
2845     memset(&volSync, 0, sizeof(volSync));
2846
2847     /* handle file length setting */
2848     if (attrp->mask & CM_ATTRMASK_LENGTH)
2849         return cm_SetLength(scp, &attrp->length, userp, reqp);
2850
2851     lock_ObtainWrite(&scp->rw);
2852     /* Check for RO volume */
2853     if (scp->flags & CM_SCACHEFLAG_RO) {
2854         code = CM_ERROR_READONLY;
2855         lock_ReleaseWrite(&scp->rw);
2856         return code;
2857     }
2858
2859     /* otherwise, we have to make an RPC to get the status */
2860     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2861     if (code) {
2862         lock_ReleaseWrite(&scp->rw);
2863         return code;
2864     }
2865     lock_ConvertWToR(&scp->rw);
2866
2867     /* make the attr structure */
2868     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2869
2870     tfid.Volume = scp->fid.volume;
2871     tfid.Vnode = scp->fid.vnode;
2872     tfid.Unique = scp->fid.unique;
2873     lock_ReleaseRead(&scp->rw);
2874
2875     /* now make the RPC */
2876     InterlockedIncrement(&scp->activeRPCs);
2877
2878     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2879     do {
2880         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2881         if (code)
2882             continue;
2883
2884         rxconnp = cm_GetRxConn(connp);
2885         code = RXAFS_StoreStatus(rxconnp, &tfid,
2886                                   &afsInStatus, &afsOutStatus, &volSync);
2887         rx_PutConnection(rxconnp);
2888
2889     } while (cm_Analyze(connp, userp, reqp,
2890                          &scp->fid, NULL, 1, &volSync, NULL, NULL, code));
2891     code = cm_MapRPCError(code, reqp);
2892
2893     if (code)
2894         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
2895     else
2896         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
2897
2898     lock_ObtainWrite(&scp->rw);
2899     if (code == 0)
2900         cm_MergeStatus(NULL, scp, &afsOutStatus, &volSync, userp, reqp,
2901                         CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
2902     else
2903         InterlockedDecrement(&scp->activeRPCs);
2904     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
2905
2906     /* if we're changing the mode bits, discard the ACL cache,
2907      * since we changed the mode bits.
2908      */
2909     if (afsInStatus.Mask & AFS_SETMODE)
2910         cm_FreeAllACLEnts(scp);
2911     lock_ReleaseWrite(&scp->rw);
2912     return code;
2913 }
2914
2915 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
2916                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
2917 {
2918     cm_conn_t *connp;
2919     long code;
2920     AFSFid dirAFSFid;
2921     cm_callbackRequest_t cbReq;
2922     AFSFid newAFSFid;
2923     cm_fid_t newFid;
2924     cm_scache_t *scp = NULL;
2925     int didEnd;
2926     int lostRace;
2927     AFSStoreStatus inStatus;
2928     AFSFetchStatus updatedDirStatus;
2929     AFSFetchStatus newFileStatus;
2930     AFSCallBack newFileCallback;
2931     AFSVolSync volSync;
2932     struct rx_connection * rxconnp;
2933     cm_dirOp_t dirop;
2934     fschar_t * fnamep = NULL;
2935
2936     memset(&volSync, 0, sizeof(volSync));
2937
2938     /* can't create names with @sys in them; must expand it manually first.
2939      * return "invalid request" if they try.
2940      */
2941     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
2942         return CM_ERROR_ATSYS;
2943     }
2944
2945 #ifdef AFS_FREELANCE_CLIENT
2946     /* Freelance root volume does not hold files */
2947     if (cm_freelanceEnabled &&
2948         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
2949         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
2950     {
2951         return CM_ERROR_NOACCESS;
2952     }
2953 #endif /* AFS_FREELANCE_CLIENT */
2954
2955     /* Check for RO volume */
2956     if (dscp->flags & CM_SCACHEFLAG_RO)
2957         return CM_ERROR_READONLY;
2958
2959     /* before starting the RPC, mark that we're changing the file data, so
2960      * that someone who does a chmod will know to wait until our call
2961      * completes.
2962      */
2963     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
2964                   &dirop);
2965     lock_ObtainWrite(&dscp->rw);
2966     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
2967     lock_ReleaseWrite(&dscp->rw);
2968     if (code == 0) {
2969         cm_StartCallbackGrantingCall(NULL, &cbReq);
2970     } else {
2971         cm_EndDirOp(&dirop);
2972     }
2973     if (code) {
2974         return code;
2975     }
2976     didEnd = 0;
2977
2978     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
2979
2980     cm_StatusFromAttr(&inStatus, NULL, attrp);
2981
2982     /* try the RPC now */
2983     InterlockedIncrement(&dscp->activeRPCs);
2984     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
2985     do {
2986         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
2987         if (code)
2988             continue;
2989
2990         dirAFSFid.Volume = dscp->fid.volume;
2991         dirAFSFid.Vnode = dscp->fid.vnode;
2992         dirAFSFid.Unique = dscp->fid.unique;
2993
2994         rxconnp = cm_GetRxConn(connp);
2995         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
2996                                  &inStatus, &newAFSFid, &newFileStatus,
2997                                  &updatedDirStatus, &newFileCallback,
2998                                  &volSync);
2999         rx_PutConnection(rxconnp);
3000
3001     } while (cm_Analyze(connp, userp, reqp,
3002                          &dscp->fid, NULL, 1, &volSync, NULL, &cbReq, code));
3003     code = cm_MapRPCError(code, reqp);
3004
3005     if (code)
3006         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3007     else
3008         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3009
3010     if (dirop.scp) {
3011         lock_ObtainWrite(&dirop.scp->dirlock);
3012         dirop.lockType = CM_DIRLOCK_WRITE;
3013     }
3014     lock_ObtainWrite(&dscp->rw);
3015     if (code == 0) {
3016         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3017         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3018         if (cm_CheckDirOpForSingleChange(&dirop)) {
3019             lock_ReleaseWrite(&dscp->rw);
3020             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3021 #ifdef USE_BPLUS
3022             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3023 #endif
3024             lock_ObtainWrite(&dscp->rw);
3025         }
3026     } else {
3027         InterlockedDecrement(&dscp->activeRPCs);
3028     }
3029     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3030     lock_ReleaseWrite(&dscp->rw);
3031
3032     /* now try to create the file's entry, too, but be careful to
3033      * make sure that we don't merge in old info.  Since we weren't locking
3034      * out any requests during the file's creation, we may have pretty old
3035      * info.
3036      */
3037     if (code == 0) {
3038         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3039         if (code == 0) {
3040             lock_ObtainWrite(&scp->rw);
3041             scp->creator = userp;               /* remember who created it */
3042             if (!cm_HaveCallback(scp)) {
3043                 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3044                                                       &newFileCallback, &volSync, 0);
3045                 InterlockedIncrement(&scp->activeRPCs);
3046                 if (!lostRace)
3047                     cm_MergeStatus(dscp, scp, &newFileStatus, &volSync,
3048                                    userp, reqp, 0);
3049                 didEnd = 1;
3050             }
3051             lock_ReleaseWrite(&scp->rw);
3052         }
3053     }
3054
3055     /* make sure we end things properly */
3056     if (!didEnd)
3057         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3058
3059     cm_EndDirOp(&dirop);
3060
3061     if (fnamep)
3062         free(fnamep);
3063
3064     if (scp) {
3065         if (scpp)
3066             *scpp = scp;
3067         else
3068             cm_ReleaseSCache(scp);
3069     }
3070     return code;
3071 }
3072
3073 /*
3074  * locked if TRUE means write-locked
3075  * else the cm_scache_t rw must not be held
3076  */
3077 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3078 {
3079     long code;
3080
3081     if (locked)
3082         lock_ReleaseWrite(&scp->rw);
3083
3084     osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3085
3086     code = buf_CleanVnode(scp, userp, reqp);
3087     if (code == 0) {
3088         lock_ObtainWrite(&scp->rw);
3089
3090         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3091                           | CM_SCACHEMASK_CLIENTMODTIME
3092                           | CM_SCACHEMASK_LENGTH))
3093             code = cm_StoreMini(scp, userp, reqp);
3094
3095         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3096             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3097             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3098         }
3099
3100         if (!locked)
3101             lock_ReleaseWrite(&scp->rw);
3102     } else if (locked) {
3103         lock_ObtainWrite(&scp->rw);
3104     }
3105     return code;
3106 }
3107
3108 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3109                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3110 {
3111     cm_conn_t *connp;
3112     long code;
3113     AFSFid dirAFSFid;
3114     cm_callbackRequest_t cbReq;
3115     AFSFid newAFSFid;
3116     cm_fid_t newFid;
3117     cm_scache_t *scp = NULL;
3118     int didEnd;
3119     int lostRace;
3120     AFSStoreStatus inStatus;
3121     AFSFetchStatus updatedDirStatus;
3122     AFSFetchStatus newDirStatus;
3123     AFSCallBack newDirCallback;
3124     AFSVolSync volSync;
3125     struct rx_connection * rxconnp;
3126     cm_dirOp_t dirop;
3127     fschar_t * fnamep = NULL;
3128
3129     memset(&volSync, 0, sizeof(volSync));
3130
3131     /* can't create names with @sys in them; must expand it manually first.
3132      * return "invalid request" if they try.
3133      */
3134     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3135         return CM_ERROR_ATSYS;
3136     }
3137
3138 #ifdef AFS_FREELANCE_CLIENT
3139     /* Freelance root volume does not hold subdirectories */
3140     if (cm_freelanceEnabled &&
3141         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3142         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3143     {
3144         return CM_ERROR_NOACCESS;
3145     }
3146 #endif /* AFS_FREELANCE_CLIENT */
3147
3148     /* Check for RO volume */
3149     if (dscp->flags & CM_SCACHEFLAG_RO)
3150         return CM_ERROR_READONLY;
3151
3152     /* before starting the RPC, mark that we're changing the directory
3153      * data, so that someone who does a chmod on the dir will wait until
3154      * our call completes.
3155      */
3156     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3157                   &dirop);
3158     lock_ObtainWrite(&dscp->rw);
3159     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3160     lock_ReleaseWrite(&dscp->rw);
3161     if (code == 0) {
3162         cm_StartCallbackGrantingCall(NULL, &cbReq);
3163     } else {
3164         cm_EndDirOp(&dirop);
3165     }
3166     if (code) {
3167         return code;
3168     }
3169     didEnd = 0;
3170
3171     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3172     cm_StatusFromAttr(&inStatus, NULL, attrp);
3173
3174     /* try the RPC now */
3175     InterlockedIncrement(&dscp->activeRPCs);
3176     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3177     do {
3178         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3179         if (code)
3180             continue;
3181
3182         dirAFSFid.Volume = dscp->fid.volume;
3183         dirAFSFid.Vnode = dscp->fid.vnode;
3184         dirAFSFid.Unique = dscp->fid.unique;
3185
3186         rxconnp = cm_GetRxConn(connp);
3187         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3188                               &inStatus, &newAFSFid, &newDirStatus,
3189                               &updatedDirStatus, &newDirCallback,
3190                               &volSync);
3191         rx_PutConnection(rxconnp);
3192
3193     } while (cm_Analyze(connp, userp, reqp,
3194                         &dscp->fid, NULL, 1, &volSync, NULL, &cbReq, code));
3195     code = cm_MapRPCError(code, reqp);
3196
3197     if (code)
3198         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3199     else
3200         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3201
3202     if (dirop.scp) {
3203         lock_ObtainWrite(&dirop.scp->dirlock);
3204         dirop.lockType = CM_DIRLOCK_WRITE;
3205     }
3206     lock_ObtainWrite(&dscp->rw);
3207     if (code == 0) {
3208         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3209         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3210         if (cm_CheckDirOpForSingleChange(&dirop)) {
3211             lock_ReleaseWrite(&dscp->rw);
3212             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3213 #ifdef USE_BPLUS
3214             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3215 #endif
3216             lock_ObtainWrite(&dscp->rw);
3217         }
3218     } else {
3219         InterlockedDecrement(&dscp->activeRPCs);
3220     }
3221     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3222     lock_ReleaseWrite(&dscp->rw);
3223
3224     /* now try to create the new dir's entry, too, but be careful to
3225      * make sure that we don't merge in old info.  Since we weren't locking
3226      * out any requests during the file's creation, we may have pretty old
3227      * info.
3228      */
3229     if (code == 0) {
3230         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3231         if (code == 0) {
3232             lock_ObtainWrite(&scp->rw);
3233             if (!cm_HaveCallback(scp)) {
3234                 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3235                                                       &newDirCallback, &volSync, 0);
3236                 InterlockedIncrement(&scp->activeRPCs);
3237                 if (!lostRace)
3238                     cm_MergeStatus(dscp, scp, &newDirStatus, &volSync,
3239                                    userp, reqp, 0);
3240                 didEnd = 1;
3241             }
3242             lock_ReleaseWrite(&scp->rw);
3243         }
3244     }
3245
3246     /* make sure we end things properly */
3247     if (!didEnd)
3248         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3249
3250     cm_EndDirOp(&dirop);
3251
3252     free(fnamep);
3253
3254     if (scp) {
3255         if (scpp)
3256             *scpp = scp;
3257         else
3258             cm_ReleaseSCache(scp);
3259     }
3260
3261     /* and return error code */
3262     return code;
3263 }
3264
3265 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3266              cm_user_t *userp, cm_req_t *reqp)
3267 {
3268     cm_conn_t *connp;
3269     long code = 0;
3270     AFSFid dirAFSFid;
3271     AFSFid existingAFSFid;
3272     AFSFetchStatus updatedDirStatus;
3273     AFSFetchStatus newLinkStatus;
3274     AFSVolSync volSync;
3275     struct rx_connection * rxconnp;
3276     cm_dirOp_t dirop;
3277     fschar_t * fnamep = NULL;
3278     int invalidate = 0;
3279
3280     memset(&volSync, 0, sizeof(volSync));
3281
3282     if (dscp->fid.cell != sscp->fid.cell ||
3283         dscp->fid.volume != sscp->fid.volume) {
3284         return CM_ERROR_CROSSDEVLINK;
3285     }
3286
3287     /* Check for RO volume */
3288     if (dscp->flags & CM_SCACHEFLAG_RO)
3289         return CM_ERROR_READONLY;
3290
3291     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3292                   &dirop);
3293     lock_ObtainWrite(&dscp->rw);
3294     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3295     lock_ReleaseWrite(&dscp->rw);
3296     if (code != 0)
3297         cm_EndDirOp(&dirop);
3298
3299     if (code)
3300         return code;
3301
3302     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3303
3304     /* try the RPC now */
3305     InterlockedIncrement(&dscp->activeRPCs);
3306     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3307     do {
3308         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3309         if (code) continue;
3310
3311         dirAFSFid.Volume = dscp->fid.volume;
3312         dirAFSFid.Vnode = dscp->fid.vnode;
3313         dirAFSFid.Unique = dscp->fid.unique;
3314
3315         existingAFSFid.Volume = sscp->fid.volume;
3316         existingAFSFid.Vnode = sscp->fid.vnode;
3317         existingAFSFid.Unique = sscp->fid.unique;
3318
3319         rxconnp = cm_GetRxConn(connp);
3320         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3321             &newLinkStatus, &updatedDirStatus, &volSync);
3322         rx_PutConnection(rxconnp);
3323         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3324
3325     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3326
3327     code = cm_MapRPCError(code, reqp);
3328
3329     if (code)
3330         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3331     else
3332         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3333
3334     if (dirop.scp) {
3335         lock_ObtainWrite(&dirop.scp->dirlock);
3336         dirop.lockType = CM_DIRLOCK_WRITE;
3337     }
3338     lock_ObtainWrite(&dscp->rw);
3339     if (code == 0) {
3340         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3341         invalidate = 1;
3342
3343         if (cm_CheckDirOpForSingleChange(&dirop)) {
3344             lock_ReleaseWrite(&dscp->rw);
3345             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3346 #ifdef USE_BPLUS
3347             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3348 #endif
3349             lock_ObtainWrite(&dscp->rw);
3350         }
3351     } else {
3352         InterlockedDecrement(&dscp->activeRPCs);
3353     }
3354     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3355     lock_ReleaseWrite(&dscp->rw);
3356
3357     cm_EndDirOp(&dirop);
3358
3359     if (invalidate && RDR_Initialized)
3360         RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3361                              dscp->fid.unique, dscp->fid.hash,
3362                              dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3363
3364     /* Update the linked object status */
3365     if (code == 0) {
3366         lock_ObtainWrite(&sscp->rw);
3367         InterlockedIncrement(&sscp->activeRPCs);
3368         cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3369         lock_ReleaseWrite(&sscp->rw);
3370     }
3371
3372     free(fnamep);
3373
3374     return code;
3375 }
3376
3377 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3378                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3379 {
3380     cm_conn_t *connp;
3381     long code;
3382     AFSFid dirAFSFid;
3383     AFSFid newAFSFid;
3384     cm_fid_t newFid;
3385     cm_scache_t *scp;
3386     AFSStoreStatus inStatus;
3387     AFSFetchStatus updatedDirStatus;
3388     AFSFetchStatus newLinkStatus;
3389     AFSVolSync volSync;
3390     struct rx_connection * rxconnp;
3391     cm_dirOp_t dirop;
3392     fschar_t *fnamep = NULL;
3393
3394     if (scpp)
3395         *scpp = NULL;
3396
3397     /* Check for RO volume */
3398     if (dscp->flags & CM_SCACHEFLAG_RO)
3399         return CM_ERROR_READONLY;
3400
3401     memset(&volSync, 0, sizeof(volSync));
3402
3403     /* before starting the RPC, mark that we're changing the directory data,
3404      * so that someone who does a chmod on the dir will wait until our
3405      * call completes.
3406      */
3407     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3408                   &dirop);
3409     lock_ObtainWrite(&dscp->rw);
3410     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3411     lock_ReleaseWrite(&dscp->rw);
3412     if (code != 0)
3413         cm_EndDirOp(&dirop);
3414     if (code) {
3415         return code;
3416     }
3417
3418     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3419
3420     cm_StatusFromAttr(&inStatus, NULL, attrp);
3421
3422     /* try the RPC now */
3423     InterlockedIncrement(&dscp->activeRPCs);
3424     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3425     do {
3426         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3427         if (code)
3428             continue;
3429
3430         dirAFSFid.Volume = dscp->fid.volume;
3431         dirAFSFid.Vnode = dscp->fid.vnode;
3432         dirAFSFid.Unique = dscp->fid.unique;
3433
3434         rxconnp = cm_GetRxConn(connp);
3435         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3436                               &inStatus, &newAFSFid, &newLinkStatus,
3437                               &updatedDirStatus, &volSync);
3438         rx_PutConnection(rxconnp);
3439
3440     } while (cm_Analyze(connp, userp, reqp,
3441                          &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3442     code = cm_MapRPCError(code, reqp);
3443
3444     if (code)
3445         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3446     else
3447         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3448
3449     if (dirop.scp) {
3450         lock_ObtainWrite(&dirop.scp->dirlock);
3451         dirop.lockType = CM_DIRLOCK_WRITE;
3452     }
3453     lock_ObtainWrite(&dscp->rw);
3454     if (code == 0) {
3455         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3456         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3457         if (cm_CheckDirOpForSingleChange(&dirop)) {
3458             lock_ReleaseWrite(&dscp->rw);
3459             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3460
3461             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3462 #ifdef USE_BPLUS
3463             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3464 #endif
3465             lock_ObtainWrite(&dscp->rw);
3466         }
3467     } else {
3468         InterlockedDecrement(&dscp->activeRPCs);
3469     }
3470     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3471     lock_ReleaseWrite(&dscp->rw);
3472
3473     cm_EndDirOp(&dirop);
3474
3475     /* now try to create the new dir's entry, too, but be careful to
3476      * make sure that we don't merge in old info.  Since we weren't locking
3477      * out any requests during the file's creation, we may have pretty old
3478      * info.
3479      */
3480     if (code == 0) {
3481         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3482         if (code == 0) {
3483             lock_ObtainWrite(&scp->rw);
3484             if (!cm_HaveCallback(scp)) {
3485                 InterlockedIncrement(&scp->activeRPCs);
3486                 cm_MergeStatus(dscp, scp, &newLinkStatus, &volSync,
3487                                 userp, reqp, 0);
3488             }
3489             lock_ReleaseWrite(&scp->rw);
3490
3491             if (scpp) {
3492                 *scpp = scp;
3493             } else {
3494                 cm_ReleaseSCache(scp);
3495             }
3496         }
3497     }
3498
3499     free(fnamep);
3500
3501     /* and return error code */
3502     return code;
3503 }
3504
3505 /*! \brief Remove a directory
3506
3507   Encapsulates a call to RXAFS_RemoveDir().
3508
3509   \param[in] dscp cm_scache_t for the directory containing the
3510       directory to be removed.
3511
3512   \param[in] fnamep This will be the original name of the directory
3513       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3514       This parameter is optional.  If it is not provided the value
3515       will be looked up.
3516
3517   \param[in] cnamep Normalized name used to update the local
3518       directory caches.
3519
3520   \param[in] userp cm_user_t for the request.
3521
3522   \param[in] reqp Request tracker.
3523 */
3524 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3525 {
3526     cm_conn_t *connp;
3527     long code;
3528     AFSFid dirAFSFid;
3529     int didEnd;
3530     AFSFetchStatus updatedDirStatus;
3531     AFSVolSync volSync;
3532     struct rx_connection * rxconnp;
3533     cm_dirOp_t dirop;
3534     cm_scache_t *scp = NULL;
3535     int free_fnamep = FALSE;
3536
3537     memset(&volSync, 0, sizeof(volSync));
3538
3539     if (fnamep == NULL) {
3540         code = -1;
3541 #ifdef USE_BPLUS
3542         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3543                              CM_DIROP_FLAG_NONE, &dirop);
3544         if (code == 0) {
3545             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3546             if (code == 0)
3547                 free_fnamep = TRUE;
3548             cm_EndDirOp(&dirop);
3549         }
3550 #endif
3551         if (code)
3552             goto done;
3553     }
3554
3555     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3556     if (code)
3557         goto done;
3558
3559     /* Check for RO volume */
3560     if (dscp->flags & CM_SCACHEFLAG_RO) {
3561         code = CM_ERROR_READONLY;
3562         goto done;
3563     }
3564
3565     /* before starting the RPC, mark that we're changing the directory data,
3566      * so that someone who does a chmod on the dir will wait until our
3567      * call completes.
3568      */
3569     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3570                   &dirop);
3571     lock_ObtainWrite(&dscp->rw);
3572     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3573     lock_ReleaseWrite(&dscp->rw);
3574     if (code) {
3575         cm_EndDirOp(&dirop);
3576         goto done;
3577     }
3578     didEnd = 0;
3579
3580     /* try the RPC now */
3581     InterlockedIncrement(&dscp->activeRPCs);
3582     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3583     do {
3584         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3585         if (code)
3586             continue;
3587
3588         dirAFSFid.Volume = dscp->fid.volume;
3589         dirAFSFid.Vnode = dscp->fid.vnode;
3590         dirAFSFid.Unique = dscp->fid.unique;
3591
3592         rxconnp = cm_GetRxConn(connp);
3593         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3594                                &updatedDirStatus, &volSync);
3595         rx_PutConnection(rxconnp);
3596
3597     } while (cm_Analyze(connp, userp, reqp,
3598                         &dscp->fid, NULL, 1, &volSync, NULL, NULL, code));
3599     code = cm_MapRPCErrorRmdir(code, reqp);
3600
3601     if (code)
3602         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3603     else
3604         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3605
3606     if (dirop.scp) {
3607         lock_ObtainWrite(&dirop.scp->dirlock);
3608         dirop.lockType = CM_DIRLOCK_WRITE;
3609     }
3610     lock_ObtainWrite(&dscp->rw);
3611     if (code == 0) {
3612         cm_dnlcRemove(dscp, cnamep);
3613         cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3614         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3615             lock_ReleaseWrite(&dscp->rw);
3616             cm_DirDeleteEntry(&dirop, fnamep);
3617 #ifdef USE_BPLUS
3618             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3619 #endif
3620             lock_ObtainWrite(&dscp->rw);
3621         }
3622     } else {
3623         InterlockedDecrement(&dscp->activeRPCs);
3624     }
3625     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3626     lock_ReleaseWrite(&dscp->rw);
3627
3628     cm_EndDirOp(&dirop);
3629
3630     if (scp) {
3631         cm_ReleaseSCache(scp);
3632         if (code == 0) {
3633             lock_ObtainWrite(&scp->rw);
3634             scp->flags |= CM_SCACHEFLAG_DELETED;
3635             lock_ObtainWrite(&cm_scacheLock);
3636             cm_AdjustScacheLRU(scp);
3637             cm_RemoveSCacheFromHashTable(scp);
3638             lock_ReleaseWrite(&cm_scacheLock);
3639             lock_ReleaseWrite(&scp->rw);
3640             if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR) &&
3641                 !RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3642                                       scp->fid.unique, scp->fid.hash,
3643                                       scp->fileType, AFS_INVALIDATE_DELETED))
3644                 buf_ClearRDRFlag(scp, "rmdir");
3645         }
3646     }
3647
3648   done:
3649     if (free_fnamep)
3650         free(fnamep);
3651
3652     /* and return error code */
3653     return code;
3654 }
3655
3656 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3657 {
3658     /* grab mutex on contents */
3659     lock_ObtainWrite(&scp->rw);
3660
3661     /* reset the prefetch info */
3662     scp->prefetch.base.LowPart = 0;             /* base */
3663     scp->prefetch.base.HighPart = 0;
3664     scp->prefetch.end.LowPart = 0;              /* and end */
3665     scp->prefetch.end.HighPart = 0;
3666
3667     /* release mutex on contents */
3668     lock_ReleaseWrite(&scp->rw);
3669
3670     /* we're done */
3671     return 0;
3672 }
3673
3674 /*! \brief Rename a file or directory
3675
3676   Encapsulates a RXAFS_Rename() call.
3677
3678   \param[in] oldDscp cm_scache_t for the directory containing the old
3679       name.
3680
3681   \param[in] oldNamep The original old name known to the file server.
3682       This is the name that will be passed into the RXAFS_Rename().
3683       If it is not provided, it will be looked up.
3684
3685   \param[in] normalizedOldNamep Normalized old name.  This is used for
3686   updating local directory caches.
3687
3688   \param[in] newDscp cm_scache_t for the directory containing the new
3689   name.
3690
3691   \param[in] newNamep New name. Normalized.
3692
3693   \param[in] userp cm_user_t for the request.
3694
3695   \param[in,out] reqp Request tracker.
3696
3697 */
3698 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3699                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3700                cm_req_t *reqp)
3701 {
3702     cm_conn_t *connp;
3703     long code = 0;
3704     AFSFid oldDirAFSFid;
3705     AFSFid newDirAFSFid;
3706     AFSFetchStatus updatedOldDirStatus;
3707     AFSFetchStatus updatedNewDirStatus;
3708     AFSVolSync volSync;
3709     int oneDir = 0;
3710     int bTargetExists = 0;
3711     struct rx_connection * rxconnp;
3712     cm_dirOp_t oldDirOp;
3713     cm_fid_t   fileFid;
3714     int        diropCode = -1;
3715     cm_dirOp_t newDirOp;
3716     fschar_t * newNamep = NULL;
3717     int free_oldNamep = FALSE;
3718     cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3719     int rpc_skipped = 0;
3720
3721     memset(&volSync, 0, sizeof(volSync));
3722
3723     if (cOldNamep == NULL || cNewNamep == NULL ||
3724         cm_ClientStrLen(cOldNamep) == 0 ||
3725         cm_ClientStrLen(cNewNamep) == 0)
3726         return CM_ERROR_INVAL;
3727
3728     /* check for identical names */
3729     if (oldDscp == newDscp &&
3730         cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3731         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3732                   oldDscp, newDscp);
3733         return CM_ERROR_RENAME_IDENTICAL;
3734     }
3735
3736     /* Check for RO volume */
3737     if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3738         return CM_ERROR_READONLY;
3739     }
3740
3741     if (oldNamep == NULL) {
3742         code = -1;
3743 #ifdef USE_BPLUS
3744         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3745                              CM_DIROP_FLAG_NONE, &oldDirOp);
3746         if (code == 0) {
3747             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3748             if (code == 0)
3749                 free_oldNamep = TRUE;
3750             cm_EndDirOp(&oldDirOp);
3751         }
3752 #endif
3753         if (code) {
3754             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3755                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3756             goto done;
3757         }
3758     }
3759
3760     /* before starting the RPC, mark that we're changing the directory data,
3761      * so that someone who does a chmod on the dir will wait until our call
3762      * completes.  We do this in vnode order so that we don't deadlock,
3763      * which makes the code a little verbose.
3764      */
3765     if (oldDscp == newDscp) {
3766         oneDir = 1;
3767         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3768                       CM_DIROP_FLAG_NONE, &oldDirOp);
3769         lock_ObtainWrite(&oldDscp->rw);
3770         cm_dnlcRemove(oldDscp, cOldNamep);
3771         cm_dnlcRemove(oldDscp, cNewNamep);
3772         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3773                           CM_SCACHESYNC_STOREDATA);
3774         lock_ReleaseWrite(&oldDscp->rw);
3775         if (code != 0) {
3776             cm_EndDirOp(&oldDirOp);
3777         }
3778     }
3779     else {
3780         /* two distinct dir vnodes */
3781         oneDir = 0;
3782         if (oldDscp->fid.cell != newDscp->fid.cell ||
3783              oldDscp->fid.volume != newDscp->fid.volume) {
3784             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3785                       oldDscp, newDscp);
3786             code = CM_ERROR_CROSSDEVLINK;
3787             goto done;
3788         }
3789
3790         /* shouldn't happen that we have distinct vnodes for two
3791          * different files, but could due to deliberate attack, or
3792          * stale info.  Avoid deadlocks and quit now.
3793          */
3794         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3795             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3796                       oldDscp, newDscp);
3797             code = CM_ERROR_CROSSDEVLINK;
3798             goto done;
3799         }
3800
3801         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3802             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3803                           CM_DIROP_FLAG_NONE, &oldDirOp);
3804             lock_ObtainWrite(&oldDscp->rw);
3805             cm_dnlcRemove(oldDscp, cOldNamep);
3806             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3807                              CM_SCACHESYNC_STOREDATA);
3808             lock_ReleaseWrite(&oldDscp->rw);
3809             if (code != 0)
3810                 cm_EndDirOp(&oldDirOp);
3811             if (code == 0) {
3812                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3813                               CM_DIROP_FLAG_NONE, &newDirOp);
3814                 lock_ObtainWrite(&newDscp->rw);
3815                 cm_dnlcRemove(newDscp, cNewNamep);
3816                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3817                                  CM_SCACHESYNC_STOREDATA);
3818                 lock_ReleaseWrite(&newDscp->rw);
3819                 if (code) {
3820                     cm_EndDirOp(&newDirOp);
3821
3822                     /* cleanup first one */
3823                     lock_ObtainWrite(&oldDscp->rw);
3824                     cm_SyncOpDone(oldDscp, NULL,
3825                                    CM_SCACHESYNC_STOREDATA);
3826                     lock_ReleaseWrite(&oldDscp->rw);
3827                     cm_EndDirOp(&oldDirOp);
3828                 }
3829             }
3830         }
3831         else {
3832             /* lock the new vnode entry first */
3833             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3834                           CM_DIROP_FLAG_NONE, &newDirOp);
3835             lock_ObtainWrite(&newDscp->rw);
3836             cm_dnlcRemove(newDscp, cNewNamep);
3837             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3838                               CM_SCACHESYNC_STOREDATA);
3839             lock_ReleaseWrite(&newDscp->rw);
3840             if (code != 0)
3841                 cm_EndDirOp(&newDirOp);
3842             if (code == 0) {
3843                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3844                               CM_DIROP_FLAG_NONE, &oldDirOp);
3845                 lock_ObtainWrite(&oldDscp->rw);
3846                 cm_dnlcRemove(oldDscp, cOldNamep);
3847                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3848                                   CM_SCACHESYNC_STOREDATA);
3849                 lock_ReleaseWrite(&oldDscp->rw);
3850                 if (code != 0)
3851                     cm_EndDirOp(&oldDirOp);
3852                 if (code) {
3853                     /* cleanup first one */
3854                     lock_ObtainWrite(&newDscp->rw);
3855                     cm_SyncOpDone(newDscp, NULL,
3856                                    CM_SCACHESYNC_STOREDATA);
3857                     lock_ReleaseWrite(&newDscp->rw);
3858                     cm_EndDirOp(&newDirOp);
3859                 }
3860             }
3861         }
3862     }   /* two distinct vnodes */
3863
3864     if (code)
3865         goto done;
3866
3867     /*
3868      * The source and destination directories are now locked and no other local
3869      * changes can occur.
3870      *
3871      * Before we permit the operation, make sure that we do not already have
3872      * an object in the destination directory that has a case-insensitive match
3873      * for this name UNLESS the matching object is the object we are renaming.
3874      */
3875     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3876     if (code) {
3877         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3878                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3879         rpc_skipped = 1;
3880         goto post_rpc;
3881     }
3882
3883     /* Case sensitive lookup.  If this succeeds we are done. */
3884     code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3885     if (code) {
3886         /*
3887          * Case insensitive lookup.  If this succeeds, it could have found the
3888          * same file with a name that differs only by case or it could be a
3889          * different file entirely.
3890          */
3891         code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3892         if (code == 0) {
3893             /* found a matching object with the new name */
3894             if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
3895                 /* and they don't match so return an error */
3896                 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
3897                           newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
3898                 code = CM_ERROR_EXISTS;
3899             }
3900             cm_ReleaseSCache(oldTargetScp);
3901             oldTargetScp = NULL;
3902         } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
3903             code = CM_ERROR_EXISTS;
3904         } else {
3905             /* The target does not exist.  Clear the error and perform the rename. */
3906             code = 0;
3907         }
3908     } else {
3909         bTargetExists = 1;
3910     }
3911
3912     if (code) {
3913         rpc_skipped = 1;
3914         goto post_rpc;
3915     }
3916
3917     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
3918
3919     /* try the RPC now */
3920     InterlockedIncrement(&oldDscp->activeRPCs);
3921     if (!oneDir)
3922         InterlockedIncrement(&newDscp->activeRPCs);
3923     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
3924               oldDscp, newDscp);
3925     do {
3926         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
3927         if (code)
3928             continue;
3929
3930         oldDirAFSFid.Volume = oldDscp->fid.volume;
3931         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
3932         oldDirAFSFid.Unique = oldDscp->fid.unique;
3933         newDirAFSFid.Volume = newDscp->fid.volume;
3934         newDirAFSFid.Vnode = newDscp->fid.vnode;
3935         newDirAFSFid.Unique = newDscp->fid.unique;
3936
3937         rxconnp = cm_GetRxConn(connp);
3938         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
3939                             &newDirAFSFid, newNamep,
3940                             &updatedOldDirStatus, &updatedNewDirStatus,
3941                             &volSync);
3942         rx_PutConnection(rxconnp);
3943
3944     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
3945                          &volSync, NULL, NULL, code));
3946     code = cm_MapRPCError(code, reqp);
3947
3948     if (code)
3949         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
3950     else
3951         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
3952
3953   post_rpc:
3954     /* update the individual stat cache entries for the directories */
3955     if (oldDirOp.scp) {
3956         lock_ObtainWrite(&oldDirOp.scp->dirlock);
3957         oldDirOp.lockType = CM_DIRLOCK_WRITE;
3958     }
3959
3960     lock_ObtainWrite(&oldDscp->rw);
3961     if (code == 0) {
3962         cm_MergeStatus(NULL, oldDscp, &updatedOldDirStatus, &volSync,
3963                        userp, reqp, CM_MERGEFLAG_DIROP);
3964         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
3965             lock_ReleaseWrite(&oldDscp->rw);
3966             if (bTargetExists && oneDir) {
3967                 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
3968 #ifdef USE_BPLUS
3969                 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
3970 #endif
3971             }
3972
3973 #ifdef USE_BPLUS
3974             diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
3975             if (diropCode == CM_ERROR_INEXACT_MATCH)
3976                 diropCode = 0;
3977             else if (diropCode == EINVAL)
3978 #endif
3979                 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
3980
3981             if (diropCode == 0) {
3982                 if (oneDir) {
3983                     diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
3984 #ifdef USE_BPLUS
3985                     cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
3986 #endif
3987                 }
3988
3989                 if (diropCode == 0) {
3990                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
3991 #ifdef USE_BPLUS
3992                     cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
3993 #endif
3994                 }
3995             }
3996             lock_ObtainWrite(&oldDscp->rw);
3997         }
3998     } else {
3999         if (!rpc_skipped)
4000             InterlockedDecrement(&oldDscp->activeRPCs);
4001     }
4002     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4003     lock_ReleaseWrite(&oldDscp->rw);
4004
4005     cm_EndDirOp(&oldDirOp);
4006
4007     /* and update it for the new one, too, if necessary */
4008     if (!oneDir) {
4009         if (newDirOp.scp) {
4010             lock_ObtainWrite(&newDirOp.scp->dirlock);
4011             newDirOp.lockType = CM_DIRLOCK_WRITE;
4012         }
4013         lock_ObtainWrite(&newDscp->rw);
4014         if (code == 0) {
4015             cm_MergeStatus(NULL, newDscp, &updatedNewDirStatus, &volSync,
4016                             userp, reqp, CM_MERGEFLAG_DIROP);
4017
4018             /*
4019              * we only make the local change if we successfully made
4020              * the change in the old directory AND there was only one
4021              * change in the new directory
4022              */
4023             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
4024                 lock_ReleaseWrite(&newDscp->rw);
4025
4026                 if (bTargetExists && !oneDir) {
4027                     diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
4028 #ifdef USE_BPLUS
4029                     cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
4030 #endif
4031                 }
4032
4033                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
4034 #ifdef USE_BPLUS
4035                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
4036 #endif
4037                 lock_ObtainWrite(&newDscp->rw);
4038             }
4039         } else {
4040             if (!rpc_skipped)
4041                 InterlockedIncrement(&newDscp->activeRPCs);
4042         }
4043         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
4044         lock_ReleaseWrite(&newDscp->rw);
4045
4046         cm_EndDirOp(&newDirOp);
4047     }
4048
4049     if (code == 0) {
4050         /*
4051          * After the rename the file server has invalidated the callbacks
4052          * on the file that was moved and destroyed any target file.
4053          */
4054         lock_ObtainWrite(&oldScp->rw);
4055         cm_DiscardSCache(oldScp);
4056         lock_ReleaseWrite(&oldScp->rw);
4057
4058         if (RDR_Initialized)
4059             RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
4060                                   oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
4061
4062         if (oldTargetScp) {
4063             lock_ObtainWrite(&oldTargetScp->rw);
4064             cm_DiscardSCache(oldTargetScp);
4065             lock_ReleaseWrite(&oldTargetScp->rw);
4066
4067             if (RDR_Initialized)
4068                 RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
4069                                      oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
4070         }
4071     }
4072
4073   done:
4074     if (oldScp)
4075         cm_ReleaseSCache(oldScp);
4076
4077     if (oldTargetScp)
4078         cm_ReleaseSCache(oldTargetScp);
4079
4080     if (free_oldNamep)
4081         free(oldNamep);
4082
4083     free(newNamep);
4084
4085     /* and return error code */
4086     return code;
4087 }
4088
4089 /* Byte range locks:
4090
4091    The OpenAFS Windows client has to fake byte range locks given no
4092    server side support for such locks.  This is implemented as keyed
4093    byte range locks on the cache manager.
4094
4095    Keyed byte range locks:
4096
4097    Each cm_scache_t structure keeps track of a list of keyed locks.
4098    The key for a lock identifies an owner of a set of locks (referred
4099    to as a client).  Each key is represented by a value.  The set of
4100    key values used within a specific cm_scache_t structure form a
4101    namespace that has a scope of just that cm_scache_t structure.  The
4102    same key value can be used with another cm_scache_t structure and
4103    correspond to a completely different client.  However it is
4104    advantageous for the SMB or IFS layer to make sure that there is a
4105    1-1 mapping between client and keys over all cm_scache_t objects.
4106
4107    Assume a client C has key Key(C) (although, since the scope of the
4108    key is a cm_scache_t, the key can be Key(C,S), where S is the
4109    cm_scache_t.  But assume a 1-1 relation between keys and clients).
4110    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
4111    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
4112    through cm_generateKey() function for both SMB and IFS.
4113
4114    The list of locks for a cm_scache_t object S is maintained in
4115    S->fileLocks.  The cache manager will set a lock on the AFS file
4116    server in order to assert the locks in S->fileLocks.  If only
4117    shared locks are in place for S, then the cache manager will obtain
4118    a LockRead lock, while if there are any exclusive locks, it will
4119    obtain a LockWrite lock.  If the exclusive locks are all released
4120    while the shared locks remain, then the cache manager will
4121    downgrade the lock from LockWrite to LockRead.  Similarly, if an
4122    exclusive lock is obtained when only shared locks exist, then the
4123    cache manager will try to upgrade the lock from LockRead to
4124    LockWrite.
4125
4126    Each lock L owned by client C maintains a key L->key such that
4127    L->key == Key(C), the effective range defined by L->LOffset and
4128    L->LLength such that the range of bytes affected by the lock is
4129    (L->LOffset, +L->LLength), a type maintained in L->LockType which
4130    is either exclusive or shared.
4131
4132    Lock states:
4133
4134    A lock exists iff it is in S->fileLocks for some cm_scache_t
4135    S. Existing locks are in one of the following states: ACTIVE,
4136    WAITLOCK, WAITUNLOCK, LOST, DELETED.
4137
4138    The following sections describe each lock and the associated
4139    transitions.
4140
4141    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4142       the lock with the AFS file server.  This type of lock can be
4143       exercised by a client to read or write to the locked region (as
4144       the lock allows).
4145
4146       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4147         server lock that was required to assert the lock.  Before
4148         marking the lock as lost, the cache manager checks if the file
4149         has changed on the server.  If the file has not changed, then
4150         the cache manager will attempt to obtain a new server lock
4151         that is sufficient to assert the client side locks for the
4152         file.  If any of these fail, the lock is marked as LOST.
4153         Otherwise, it is left as ACTIVE.
4154
4155       1.2 ACTIVE->DELETED: Lock is released.
4156
4157    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4158       grants the lock but the lock is yet to be asserted with the AFS
4159       file server.  Once the file server grants the lock, the state
4160       will transition to an ACTIVE lock.
4161
4162       2.1 WAITLOCK->ACTIVE: The server granted the lock.
4163
4164       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4165         waiting.
4166
4167       2.3 WAITLOCK->LOST: One or more locks from this client were
4168         marked as LOST.  No further locks will be granted to this
4169         client until all lost locks are removed.
4170
4171    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4172       receives a request for a lock that conflicts with an existing
4173       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
4174       and will be granted at such time the conflicting locks are
4175       removed, at which point the state will transition to either
4176       WAITLOCK or ACTIVE.
4177
4178       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
4179         current serverLock is sufficient to assert this lock, or a
4180         sufficient serverLock is obtained.
4181
4182       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4183         however the required serverLock is yet to be asserted with the
4184         server.
4185
4186       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4187         released.
4188
4189       3.5 WAITUNLOCK->LOST: One or more locks from this client were
4190         marked as LOST.  No further locks will be granted to this
4191         client until all lost locks are removed.
4192
4193    4. LOST: A lock L is LOST if the server lock that was required to
4194       assert the lock could not be obtained or if it could not be
4195       extended, or if other locks by the same client were LOST.
4196       Essentially, once a lock is LOST, the contract between the cache
4197       manager and that specific client is no longer valid.
4198
4199       The cache manager rechecks the server lock once every minute and
4200       extends it as appropriate.  If this is not done for 5 minutes,
4201       the AFS file server will release the lock (the 5 minute timeout
4202       is based on current file server code and is fairly arbitrary).
4203       Once released, the lock cannot be re-obtained without verifying
4204       that the contents of the file hasn't been modified since the
4205       time the lock was released.  Re-obtaining the lock without
4206       verifying this may lead to data corruption.  If the lock can not
4207       be obtained safely, then all active locks for the cm_scache_t
4208       are marked as LOST.
4209
4210       4.1 LOST->DELETED: The lock is released.
4211
4212    5. DELETED: The lock is no longer relevant.  Eventually, it will
4213       get removed from the cm_scache_t. In the meantime, it will be
4214       treated as if it does not exist.
4215
4216       5.1 DELETED->not exist: The lock is removed from the
4217         cm_scache_t.
4218
4219    The following are classifications of locks based on their state.
4220
4221    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
4222       have been accepted by the cache manager, but may or may not have
4223       been granted back to the client.
4224
4225    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4226
4227    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4228
4229    Lock operation:
4230
4231    A client C can READ range (Offset,+Length) of a file represented by
4232    cm_scache_t S iff (1):
4233
4234    1. for all _a_ in (Offset,+Length), all of the following is true:
4235
4236        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4237          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4238          shared.
4239
4240        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4241          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4242          Key(C)
4243
4244        (When locks are lost on an cm_scache_t, all locks are lost.  By
4245        4.2 (below), if there is an exclusive LOST lock, then there
4246        can't be any overlapping ACTIVE locks.)
4247
4248    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4249
4250    2. for all _a_ in (Offset,+Length), one of the following is true:
4251
4252        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4253          does not exist a LOST lock L such that _a_ in
4254          (L->LOffset,+L->LLength).
4255
4256        2.2 Byte _a_ of S is owned by C under lock L (as specified in
4257          1.2) AND L->LockType is exclusive.
4258
4259    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4260
4261    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4262       true:
4263
4264        3.1 If L->LockType is exclusive then there does NOT exist a
4265          ACCEPTED lock M in S->fileLocks such that _a_ in
4266          (M->LOffset,+M->LLength).
4267
4268          (If we count all QUEUED locks then we hit cases such as
4269          cascading waiting locks where the locks later on in the queue
4270          can be granted without compromising file integrity.  On the
4271          other hand if only ACCEPTED locks are considered, then locks
4272          that were received earlier may end up waiting for locks that
4273          were received later to be unlocked. The choice of ACCEPTED
4274          locks was made to mimic the Windows byte range lock
4275          semantics.)
4276
4277        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4278          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4279          M->LockType is shared.
4280
4281    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4282
4283        4.1 M->key != Key(C)
4284
4285        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4286          and (M->LOffset,+M->LLength) do not intersect.
4287
4288          (Note: If a client loses a lock, it loses all locks.
4289          Subsequently, it will not be allowed to obtain any more locks
4290          until all existing LOST locks that belong to the client are
4291          released.  Once all locks are released by a single client,
4292          there exists no further contract between the client and AFS
4293          about the contents of the file, hence the client can then
4294          proceed to obtain new locks and establish a new contract.
4295
4296          This doesn't quite work as you think it should, because most
4297          applications aren't built to deal with losing locks they
4298          thought they once had.  For now, we don't have a good
4299          solution to lost locks.
4300
4301          Also, for consistency reasons, we have to hold off on
4302          granting locks that overlap exclusive LOST locks.)
4303
4304    A client C can only unlock locks L in S->fileLocks which have
4305    L->key == Key(C).
4306
4307    The representation and invariants are as follows:
4308
4309    - Each cm_scache_t structure keeps:
4310
4311        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4312          are of type cm_file_lock_t.
4313
4314        - A record of the highest server-side lock that has been
4315          obtained for this object (cm_scache_t::serverLock), which is
4316          one of (-1), LockRead, LockWrite.
4317
4318        - A count of ACCEPTED exclusive and shared locks that are in the
4319          queue (cm_scache_t::sharedLocks and
4320          cm_scache_t::exclusiveLocks)
4321
4322    - Each cm_file_lock_t structure keeps:
4323
4324        - The type of lock (cm_file_lock_t::LockType)
4325
4326        - The key associated with the lock (cm_file_lock_t::key)
4327
4328        - The offset and length of the lock (cm_file_lock_t::LOffset
4329          and cm_file_lock_t::LLength)
4330
4331        - The state of the lock.
4332
4333        - Time of issuance or last successful extension
4334
4335    Semantic invariants:
4336
4337        I1. The number of ACCEPTED locks in S->fileLocks are
4338            (S->sharedLocks + S->exclusiveLocks)
4339
4340    External invariants:
4341
4342        I3. S->serverLock is the lock that we have asserted with the
4343            AFS file server for this cm_scache_t.
4344
4345        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4346            shared lock, but no ACTIVE exclusive locks.
4347
4348        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4349            exclusive lock.
4350
4351        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4352            M->key == L->key IMPLIES M is LOST or DELETED.
4353
4354    --asanka
4355  */
4356
4357 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4358
4359 #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)
4360
4361 #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)
4362
4363 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4364
4365 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4366
4367 /* unsafe */
4368 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4369
4370 /* unsafe */
4371 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4372
4373 /* unsafe */
4374 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4375
4376 /* unsafe */
4377 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4378
4379 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4380 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4381 #else
4382 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4383 #endif
4384
4385 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4386
4387 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4388 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4389 #else
4390 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4391
4392 /* This should really be defined in any build that this code is being
4393    compiled. */
4394 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4395 #endif
4396
4397 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4398 {
4399     afs_int64 int_begin;
4400     afs_int64 int_end;
4401
4402     int_begin = max(pos->offset, neg->offset);
4403     int_end = min(pos->offset+pos->length, neg->offset+neg->length);
4404
4405     if (int_begin < int_end) {
4406         if (int_begin == pos->offset) {
4407             pos->length = pos->offset + pos->length - int_end;
4408             pos->offset = int_end;
4409         } else if (int_end == pos->offset + pos->length) {
4410             pos->length = int_begin - pos->offset;
4411         }
4412
4413         /* We only subtract ranges if the resulting range is
4414            contiguous.  If we try to support non-contigous ranges, we
4415            aren't actually improving performance. */
4416     }
4417 }
4418
4419 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4420    specified range by the client identified by key.
4421  */
4422 long cm_LockCheckRead(cm_scache_t *scp,
4423                       LARGE_INTEGER LOffset,
4424                       LARGE_INTEGER LLength,
4425                       cm_key_t key)
4426 {
4427 #ifndef ADVISORY_LOCKS
4428
4429     cm_file_lock_t *fileLock;
4430     osi_queue_t *q;
4431     long code = 0;
4432     cm_range_t range;
4433     int substract_ranges = FALSE;
4434
4435     range.offset = LOffset.QuadPart;
4436     range.length = LLength.QuadPart;
4437
4438     /*
4439
4440      1. for all _a_ in (Offset,+Length), all of the following is true:
4441
4442        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4443          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4444          shared.
4445
4446        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4447          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4448          Key(C)
4449
4450     */
4451
4452     lock_ObtainRead(&cm_scacheLock);
4453
4454     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4455         fileLock =
4456             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4457
4458         if (INTERSECT_RANGE(range, fileLock->range)) {
4459             if (IS_LOCK_ACTIVE(fileLock)) {
4460                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4461
4462                     /* If there is an active lock for this client, it
4463                        is safe to substract ranges.*/
4464                     cm_LockRangeSubtract(&range, &fileLock->range);
4465                     substract_ranges = TRUE;
4466                 } else {
4467                     if (fileLock->lockType != LockRead) {
4468                         code = CM_ERROR_LOCK_CONFLICT;
4469                         break;
4470                     }
4471
4472                     /* even if the entire range is locked for reading,
4473                        we still can't grant the lock at this point
4474                        because the client may have lost locks. That
4475                        is, unless we have already seen an active lock
4476                        belonging to the client, in which case there
4477                        can't be any lost locks for this client. */
4478                     if (substract_ranges)
4479                         cm_LockRangeSubtract(&range, &fileLock->range);
4480                 }
4481             } else if (IS_LOCK_LOST(fileLock) &&
4482                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4483                 code = CM_ERROR_BADFD;
4484                 break;
4485             }
4486         }
4487     }
4488
4489     lock_ReleaseRead(&cm_scacheLock);
4490
4491     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4492               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4493
4494     return code;
4495
4496 #else
4497
4498     return 0;
4499
4500 #endif
4501 }
4502
4503 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4504    specified range by the client identified by key.
4505  */
4506 long cm_LockCheckWrite(cm_scache_t *scp,
4507                        LARGE_INTEGER LOffset,
4508                        LARGE_INTEGER LLength,
4509                        cm_key_t key)
4510 {
4511 #ifndef ADVISORY_LOCKS
4512
4513     cm_file_lock_t *fileLock;
4514     osi_queue_t *q;
4515     long code = 0;
4516     cm_range_t range;
4517
4518     range.offset = LOffset.QuadPart;
4519     range.length = LLength.QuadPart;
4520
4521     /*
4522    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4523
4524    2. for all _a_ in (Offset,+Length), one of the following is true:
4525
4526        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4527          lock L such that _a_ in (L->LOffset,+L->LLength).
4528
4529        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4530          exclusive.
4531     */
4532
4533     lock_ObtainRead(&cm_scacheLock);
4534
4535     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4536         fileLock =
4537             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4538
4539         if (INTERSECT_RANGE(range, fileLock->range)) {
4540             if (IS_LOCK_ACTIVE(fileLock)) {
4541                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4542                     if (fileLock->lockType == LockWrite) {
4543
4544                         /* if there is an active lock for this client, it
4545                            is safe to substract ranges */
4546                         cm_LockRangeSubtract(&range, &fileLock->range);
4547                     } else {
4548                         code = CM_ERROR_LOCK_CONFLICT;
4549                         break;
4550                     }
4551                 } else {
4552                     code = CM_ERROR_LOCK_CONFLICT;
4553                     break;
4554                 }
4555             } else if (IS_LOCK_LOST(fileLock)) {
4556                 code = CM_ERROR_BADFD;
4557                 break;
4558             }
4559         }
4560     }
4561
4562     lock_ReleaseRead(&cm_scacheLock);
4563
4564     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4565               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4566
4567     return code;
4568
4569 #else
4570
4571     return 0;
4572
4573 #endif
4574 }
4575
4576 /* Called with cm_scacheLock write locked */
4577 static cm_file_lock_t * cm_GetFileLock(void) {
4578     cm_file_lock_t * l;
4579
4580     l = (cm_file_lock_t *) cm_freeFileLocks;
4581     if (l) {
4582         osi_QRemove(&cm_freeFileLocks, &l->q);
4583     } else {
4584         l = malloc(sizeof(cm_file_lock_t));
4585         osi_assertx(l, "null cm_file_lock_t");
4586     }
4587
4588     memset(l, 0, sizeof(cm_file_lock_t));
4589
4590     return l;
4591 }
4592
4593 /* Called with cm_scacheLock write locked */
4594 static void cm_PutFileLock(cm_file_lock_t *l) {
4595     osi_QAdd(&cm_freeFileLocks, &l->q);
4596 }
4597
4598 /* called with scp->rw held.  May release it during processing, but
4599    leaves it held on exit. */
4600 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4601                    cm_req_t * reqp) {
4602     long code = 0;
4603     AFSFid tfid;
4604     cm_fid_t cfid;
4605     cm_conn_t * connp;
4606     struct rx_connection * rxconnp;
4607     AFSVolSync volSync;
4608     afs_uint32 reqflags = reqp->flags;
4609
4610     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4611
4612 #if 0
4613     /*
4614      * The file server prior to 1.6.2 does not report an accurate value
4615      * and callbacks are not issued if the lock is dropped due to expiration.
4616      */
4617     if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4618          (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4619     {
4620         code = CM_ERROR_LOCK_NOT_GRANTED;
4621         osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4622         return code;
4623     }
4624 #endif
4625
4626     memset(&volSync, 0, sizeof(volSync));
4627
4628     tfid.Volume = scp->fid.volume;
4629     tfid.Vnode = scp->fid.vnode;
4630     tfid.Unique = scp->fid.unique;
4631     cfid = scp->fid;
4632
4633     reqp->flags |= CM_REQ_NORETRY;
4634     lock_ReleaseWrite(&scp->rw);
4635
4636     do {
4637         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4638         if (code)
4639             break;
4640
4641         rxconnp = cm_GetRxConn(connp);
4642         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4643                              &volSync);
4644         rx_PutConnection(rxconnp);
4645
4646     } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, &volSync,
4647                         NULL, NULL, code));
4648
4649     code = cm_MapRPCError(code, reqp);
4650     if (code) {
4651         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4652     } else {
4653         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4654     }
4655
4656     reqp->flags = reqflags;
4657
4658     lock_ObtainWrite(&scp->rw);
4659     if (code == 0) {
4660         /*
4661          * The file server does not return a status structure so we must
4662          * locally track the file server lock count to the best of our
4663          * ability.
4664          */
4665         if (lockType == LockWrite)
4666             scp->fsLockCount = -1;
4667         else
4668             scp->fsLockCount++;
4669     }
4670     return code;
4671 }
4672
4673 /* called with scp->rw held.  Releases it during processing */
4674 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4675                        cm_req_t * reqp) {
4676     long code = 0;
4677     AFSFid tfid;
4678     cm_fid_t cfid;
4679     cm_conn_t * connp;
4680     struct rx_connection * rxconnp;
4681     AFSVolSync volSync;
4682
4683     if (scp->flags & CM_SCACHEFLAG_DELETED) {
4684         osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4685         return 0;
4686     }
4687
4688     memset(&volSync, 0, sizeof(volSync));
4689
4690     tfid.Volume = scp->fid.volume;
4691     tfid.Vnode = scp->fid.vnode;
4692     tfid.Unique = scp->fid.unique;
4693     cfid = scp->fid;
4694
4695     lock_ReleaseWrite(&scp->rw);
4696
4697     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4698
4699     do {
4700         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4701         if (code)
4702             break;
4703
4704         rxconnp = cm_GetRxConn(connp);
4705         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4706         rx_PutConnection(rxconnp);
4707
4708     } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, &volSync,
4709                         NULL, NULL, code));
4710     code = cm_MapRPCError(code, reqp);
4711     if (code)
4712         osi_Log1(afsd_logp,
4713                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4714     else
4715         osi_Log0(afsd_logp,
4716                  "CALL ReleaseLock SUCCESS");
4717
4718     lock_ObtainWrite(&scp->rw);
4719     if (code == 0) {
4720         /*
4721          * The file server does not return a status structure so we must
4722          * locally track the file server lock count to the best of our
4723          * ability.
4724          */
4725         scp->fsLockCount--;
4726         if (scp->fsLockCount < 0)
4727             scp->fsLockCount = 0;
4728     }
4729
4730     return (code != CM_ERROR_BADFD ? code : 0);
4731 }
4732
4733 /* called with scp->rw held.  May release it during processing, but
4734    will exit with lock held.
4735
4736    This will return:
4737
4738    - 0 if the user has permission to get the specified lock for the scp
4739
4740    - CM_ERROR_NOACCESS if not
4741
4742    Any other error from cm_SyncOp will be sent down untranslated.
4743
4744    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4745    phas_insert (if non-NULL) will receive a boolean value indicating
4746    whether the user has INSERT permission or not.
4747 */
4748 long cm_LockCheckPerms(cm_scache_t * scp,
4749                        int lock_type,
4750                        cm_user_t * userp,
4751                        cm_req_t * reqp,
4752                        int * phas_insert)
4753 {
4754     long rights = 0;
4755     long code = 0, code2 = 0;
4756
4757     /* lock permissions are slightly tricky because of the 'i' bit.
4758        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4759        user has PRSFS_WRITE, she can write-lock the file.  However, if
4760        the user has PRSFS_INSERT, then she can write-lock new files,
4761        but not old ones.  Since we don't have information about
4762        whether a file is new or not, we assume that if the user owns
4763        the scp, then she has the permissions that are granted by
4764        PRSFS_INSERT. */
4765
4766     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4767              scp, lock_type, userp);
4768
4769     if (lock_type == LockRead)
4770         rights |= PRSFS_LOCK;
4771     else if (lock_type == LockWrite)
4772         rights |= PRSFS_WRITE | PRSFS_LOCK;
4773     else {
4774         /* hmmkay */
4775         osi_assertx(FALSE, "invalid lock type");
4776         return 0;
4777     }
4778
4779     if (phas_insert)
4780         *phas_insert = FALSE;
4781
4782     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4783                      CM_SCACHESYNC_GETSTATUS |
4784                      CM_SCACHESYNC_NEEDCALLBACK);
4785
4786     if (phas_insert && scp->creator == userp) {
4787
4788         /* If this file was created by the user, then we check for
4789            PRSFS_INSERT.  If the file server is recent enough, then
4790            this should be sufficient for her to get a write-lock (but
4791            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4792            indicates whether a file server supports getting write
4793            locks when the user only has PRSFS_INSERT.
4794
4795            If the file was not created by the user we skip the check
4796            because the INSERT bit will not apply to this user even
4797            if it is set.
4798          */
4799
4800         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4801                          CM_SCACHESYNC_GETSTATUS |
4802                          CM_SCACHESYNC_NEEDCALLBACK);
4803
4804         if (code2 == CM_ERROR_NOACCESS) {
4805             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4806         } else {
4807             *phas_insert = TRUE;
4808             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4809         }
4810     }
4811
4812     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4813
4814     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4815
4816     return code;
4817 }
4818
4819 /* called with scp->rw held */
4820 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4821              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4822              cm_key_t key,
4823              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4824              cm_file_lock_t **lockpp)
4825 {
4826     long code = 0;
4827     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4828     cm_file_lock_t *fileLock;
4829     osi_queue_t *q;
4830     cm_range_t range;
4831     int wait_unlock = FALSE;
4832     int force_client_lock = FALSE;
4833
4834     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4835              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4836     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4837              key.process_id, key.session_id, key.file_id);
4838
4839     /*
4840    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4841
4842    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4843       true:
4844
4845        3.1 If L->LockType is exclusive then there does NOT exist a
4846          ACCEPTED lock M in S->fileLocks such that _a_ in
4847          (M->LOffset,+M->LLength).
4848
4849        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4850          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4851          M->LockType is shared.
4852
4853    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4854
4855        4.1 M->key != Key(C)
4856
4857        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4858          and (M->LOffset,+M->LLength) do not intersect.
4859     */
4860
4861     range.offset = LOffset.QuadPart;
4862     range.length = LLength.QuadPart;
4863
4864     lock_ObtainRead(&cm_scacheLock);
4865
4866     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4867         fileLock =
4868             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4869
4870         if (IS_LOCK_LOST(fileLock)) {
4871             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4872                 code = CM_ERROR_BADFD;
4873                 break;
4874             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4875                 code = CM_ERROR_WOULDBLOCK;
4876                 wait_unlock = TRUE;
4877                 break;
4878             }
4879         }
4880
4881         /* we don't need to check for deleted locks here since deleted
4882            locks are dequeued from scp->fileLocks */
4883         if (IS_LOCK_ACCEPTED(fileLock) &&
4884            INTERSECT_RANGE(range, fileLock->range)) {
4885
4886             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4887                 fileLock->lockType != LockRead) {
4888                 wait_unlock = TRUE;
4889                 code = CM_ERROR_WOULDBLOCK;
4890                 break;
4891             }
4892         }
4893     }
4894
4895     lock_ReleaseRead(&cm_scacheLock);
4896
4897     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
4898         if (Which == scp->serverLock ||
4899            (Which == LockRead && scp->serverLock == LockWrite)) {
4900
4901             int has_insert = 0;
4902
4903             /* we already have the lock we need */
4904             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
4905                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4906
4907             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4908
4909             /* special case: if we don't have permission to read-lock
4910                the file, then we force a clientside lock.  This is to
4911                compensate for applications that obtain a read-lock for
4912                reading files off of directories that don't grant
4913                read-locks to the user. */
4914             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4915
4916                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4917                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
4918                     code = 0;
4919                 } else {
4920                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
4921                     force_client_lock = TRUE;
4922                 }
4923             }
4924
4925         } else if ((scp->exclusiveLocks > 0) ||
4926                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
4927             int has_insert = 0;
4928
4929             /* We are already waiting for some other lock.  We should
4930                wait for the daemon to catch up instead of generating a
4931                flood of SetLock calls. */
4932             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
4933                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
4934
4935             /* see if we have permission to create the lock in the
4936                first place. */
4937             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4938             if (code == 0)
4939                 code = CM_ERROR_WOULDBLOCK;
4940             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
4941
4942                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
4943                     osi_Log0(afsd_logp,
4944                              "   User has no read-lock perms, but has INSERT perms.");
4945                     code = CM_ERROR_WOULDBLOCK;
4946                 } else {
4947                     osi_Log0(afsd_logp,
4948                              "   User has no read-lock perms. Forcing client-side lock");
4949                     force_client_lock = TRUE;
4950                 }
4951             }
4952
4953             /* leave any other codes as-is */
4954
4955         } else {
4956             int newLock;
4957             int check_data_version = FALSE;
4958             int has_insert = 0;
4959
4960             /* first check if we have permission to elevate or obtain
4961                the lock. */
4962             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
4963             if (code) {
4964                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
4965                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
4966                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
4967                     force_client_lock = TRUE;
4968                 }
4969                 goto check_code;
4970             }
4971
4972             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
4973
4974             if (scp->serverLock == LockRead && Which == LockWrite) {
4975
4976                 /* We want to escalate the lock to a LockWrite.
4977                  * Unfortunately that's not really possible without
4978                  * letting go of the current lock.  But for now we do
4979                  * it anyway. */
4980
4981                 osi_Log0(afsd_logp,
4982                          "   attempting to UPGRADE from LockRead to LockWrite.");
4983                 osi_Log1(afsd_logp,
4984                          "   dataVersion on scp: %I64d", scp->dataVersion);
4985
4986                 /* we assume at this point (because scp->serverLock
4987                    was valid) that we had a valid server lock. */
4988                 scp->lockDataVersion = scp->dataVersion;
4989                 check_data_version = TRUE;
4990
4991                 code = cm_IntReleaseLock(scp, userp, reqp);
4992
4993                 if (code) {
4994                     /* We couldn't release the lock */
4995                     goto check_code;
4996                 } else {
4997                     scp->serverLock = -1;
4998                 }
4999             }
5000
5001             /* We need to obtain a server lock of type Which in order
5002              * to assert this file lock */
5003 #ifndef AGGRESSIVE_LOCKS
5004             newLock = Which;
5005 #else
5006             newLock = LockWrite;
5007 #endif
5008
5009             code = cm_IntSetLock(scp, userp, newLock, reqp);
5010
5011 #ifdef AGGRESSIVE_LOCKS
5012             if ((code == CM_ERROR_WOULDBLOCK ||
5013                  code == CM_ERROR_NOACCESS) && newLock != Which) {
5014                 /* we wanted LockRead.  We tried LockWrite. Now try
5015                  * LockRead again */
5016                 newLock = Which;
5017
5018                 /* am I sane? */
5019                 osi_assertx(newLock == LockRead, "lock type not read");
5020
5021                 code = cm_IntSetLock(scp, userp, newLock, reqp);
5022             }
5023 #endif
5024
5025             if (code == CM_ERROR_NOACCESS) {
5026                 if (Which == LockRead) {
5027                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5028                         long tcode;
5029                         /* We requested a read-lock, but we have permission to
5030                          * get a write-lock. Try that */
5031
5032                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
5033
5034                         if (tcode == 0) {
5035                             newLock = LockWrite;
5036
5037                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
5038
5039                             code = cm_IntSetLock(scp, userp, newLock, reqp);
5040                         }
5041                     } else {
5042                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
5043                         force_client_lock = TRUE;
5044                     }
5045                 } else if (Which == LockWrite &&
5046                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5047                     long tcode;
5048
5049                     /* Special case: if the lock request was for a
5050                      * LockWrite and the user owns the file and we weren't
5051                      * allowed to obtain the serverlock, we either lost a
5052                      * race (the permissions changed from under us), or we
5053                      * have 'i' bits, but we aren't allowed to lock the
5054                      * file. */
5055
5056                     /* check if we lost a race... */
5057                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
5058
5059                     if (tcode == 0) {
5060                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
5061                         force_client_lock = TRUE;
5062                     }
5063                 }
5064             }
5065
5066             if (code == 0 && check_data_version &&
5067                scp->dataVersion != scp->lockDataVersion) {
5068                 /* We lost a race.  Although we successfully obtained
5069                  * a lock, someone modified the file in between.  The
5070                  * locks have all been technically lost. */
5071
5072                 osi_Log0(afsd_logp,
5073                          "  Data version mismatch while upgrading lock.");
5074                 osi_Log2(afsd_logp,
5075                          "  Data versions before=%I64d, after=%I64d",
5076                          scp->lockDataVersion,
5077                          scp->dataVersion);
5078                 osi_Log1(afsd_logp,
5079                          "  Releasing stale lock for scp 0x%x", scp);
5080
5081                 code = cm_IntReleaseLock(scp, userp, reqp);
5082
5083                 scp->serverLock = -1;
5084
5085                 code = CM_ERROR_INVAL;
5086             } else if (code == 0) {
5087                 scp->serverLock = newLock;
5088                 scp->lockDataVersion = scp->dataVersion;
5089             }
5090
5091             if (code != 0 &&
5092                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5093                 scp->serverLock == -1) {
5094                 /* Oops. We lost the lock. */
5095                 cm_LockMarkSCacheLost(scp);
5096             }
5097         }
5098     } else if (code == 0) {     /* server locks not enabled */
5099         osi_Log0(afsd_logp,
5100                  "  Skipping server lock for scp");
5101     }
5102
5103  check_code:
5104
5105     if (code != 0 && !force_client_lock) {
5106         /* Special case error translations
5107
5108            Applications don't expect certain errors from a
5109            LockFile/UnlockFile call.  We need to translate some error
5110            code to codes that apps expect and handle. */
5111
5112         /* We shouldn't actually need to handle this case since we
5113            simulate locks for RO scps anyway. */
5114         if (code == CM_ERROR_READONLY) {
5115             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
5116             code = CM_ERROR_NOACCESS;
5117         }
5118     }
5119
5120     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
5121         force_client_lock) {
5122
5123         /* clear the error if we are forcing a client lock, so we
5124            don't get confused later. */
5125         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
5126             code = 0;
5127
5128         cm_HoldUser(userp);
5129
5130         lock_ObtainWrite(&cm_scacheLock);
5131         fileLock = cm_GetFileLock();
5132 #ifdef DEBUG
5133         fileLock->fid = scp->fid;
5134 #endif
5135         fileLock->key = key;
5136         fileLock->lockType = Which;
5137         fileLock->userp = userp;
5138         fileLock->range = range;
5139         fileLock->flags = (code == 0 ? 0 :
5140                            ((wait_unlock)?
5141                             CM_FILELOCK_FLAG_WAITUNLOCK :
5142                             CM_FILELOCK_FLAG_WAITLOCK));
5143
5144         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5145             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5146
5147         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5148
5149         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5150         cm_HoldSCacheNoLock(scp);
5151         fileLock->scp = scp;
5152         osi_QAdd(&cm_allFileLocks, &fileLock->q);
5153         lock_ReleaseWrite(&cm_scacheLock);
5154
5155         if (code != 0) {
5156             *lockpp = fileLock;
5157         }
5158
5159         if (IS_LOCK_CLIENTONLY(fileLock)) {
5160             scp->clientLocks++;
5161         } else if (IS_LOCK_ACCEPTED(fileLock)) {
5162             if (Which == LockRead)
5163                 scp->sharedLocks++;
5164             else
5165                 scp->exclusiveLocks++;
5166         }
5167
5168         osi_Log3(afsd_logp,
5169                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5170                  fileLock, fileLock->flags, scp);
5171         osi_Log4(afsd_logp,
5172                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5173                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5174                  (int)(signed char) scp->serverLock);
5175     } else {
5176         osi_Log1(afsd_logp,
5177                  "cm_Lock Rejecting lock (code = 0x%x)", code);
5178     }
5179
5180     /* Convert from would block to lock not granted */
5181     if (code == CM_ERROR_WOULDBLOCK)
5182         code = CM_ERROR_LOCK_NOT_GRANTED;
5183
5184     return code;
5185 }
5186
5187 static long
5188 cm_IntUnlock(cm_scache_t * scp,
5189              cm_user_t * userp,
5190              cm_req_t *  reqp)
5191 {
5192     long code = 0;
5193
5194     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5195     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5196     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5197
5198     if (!SERVERLOCKS_ENABLED(scp)) {
5199         osi_Log0(afsd_logp, "  Skipping server lock for scp");
5200         goto done;
5201     }
5202
5203     /* Ideally we would go through the rest of the locks to determine
5204      * if one or more locks that were formerly in WAITUNLOCK can now
5205      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5206      * scp->sharedLocks accordingly.  However, the retrying of locks
5207      * in that manner is done cm_RetryLock() manually.
5208      */
5209
5210     if (scp->serverLock == LockWrite &&
5211         scp->exclusiveLocks == 0 &&
5212         scp->sharedLocks > 0) {
5213         /* The serverLock should be downgraded to LockRead */
5214         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
5215
5216         /* Make sure there are no dirty buffers left. */
5217         code = cm_FSync(scp, userp, reqp, TRUE);
5218
5219         /* since scp->serverLock looked sane, we are going to assume
5220            that we have a valid server lock. */
5221         scp->lockDataVersion = scp->dataVersion;
5222         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
5223
5224         /* before we downgrade, make sure that we have enough
5225            permissions to get the read lock. */
5226         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5227         if (code != 0) {
5228
5229             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5230
5231             code = 0;
5232             goto done;
5233         }
5234
5235         code = cm_IntReleaseLock(scp, userp, reqp);
5236
5237         if (code) {
5238             /* so we couldn't release it.  Just let the lock be for now */
5239             code = 0;
5240             goto done;
5241         } else {
5242             scp->serverLock = -1;
5243         }
5244
5245         code = cm_IntSetLock(scp, userp, LockRead, reqp);
5246
5247         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5248             scp->serverLock = LockRead;
5249         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5250             /* We lost a race condition.  Although we have a valid
5251                lock on the file, the data has changed and essentially
5252                we have lost the lock we had during the transition. */
5253
5254             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5255             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
5256                      scp->lockDataVersion,
5257                      scp->dataVersion);
5258
5259             code = cm_IntReleaseLock(scp, userp, reqp);
5260
5261             code = CM_ERROR_INVAL;
5262             scp->serverLock = -1;
5263         }
5264
5265         if (code != 0 &&
5266             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5267                 (scp->serverLock == -1)) {
5268                 /* Oopsie */
5269                 cm_LockMarkSCacheLost(scp);
5270             }
5271
5272         /* failure here has no bearing on the return value of cm_Unlock() */
5273         code = 0;
5274
5275     } else if (scp->serverLock != (-1) &&
5276               scp->exclusiveLocks == 0 &&
5277               scp->sharedLocks == 0) {
5278         /* The serverLock should be released entirely */
5279
5280         if (scp->serverLock == LockWrite) {
5281             osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
5282
5283             /* Make sure there are no dirty buffers left. */
5284             code = cm_FSync(scp, userp, reqp, TRUE);
5285         } else {
5286             osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
5287         }
5288
5289         code = cm_IntReleaseLock(scp, userp, reqp);
5290
5291         if (code == 0)
5292             scp->serverLock = (-1);
5293     }
5294
5295   done:
5296     return code;
5297 }
5298 /* Called with scp->rw held */
5299 long cm_UnlockByKey(cm_scache_t * scp,
5300                     cm_key_t key,
5301                     afs_uint32 flags,
5302                     cm_user_t * userp,
5303                     cm_req_t * reqp)
5304 {
5305     long code = 0;
5306     cm_file_lock_t *fileLock;
5307     osi_queue_t *q, *qn;
5308     int n_unlocks = 0;
5309
5310     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5311              scp, key.process_id, key.session_id, key.file_id);
5312     osi_Log1(afsd_logp, "    flags=0x%x", flags);
5313
5314     lock_ObtainWrite(&cm_scacheLock);
5315
5316     for (q = scp->fileLocksH; q; q = qn) {
5317         qn = osi_QNext(q);
5318
5319         fileLock = (cm_file_lock_t *)
5320             ((char *) q - offsetof(cm_file_lock_t, fileq));
5321
5322 #ifdef DEBUG
5323         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
5324                  fileLock,
5325                  (unsigned long) fileLock->range.offset,
5326                  (unsigned long) fileLock->range.length,
5327                 fileLock->lockType);
5328         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5329                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5330                  fileLock->flags);
5331
5332         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5333             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5334             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5335                      fileLock->fid.cell,
5336                      fileLock->fid.volume,
5337                      fileLock->fid.vnode,
5338                      fileLock->fid.unique);
5339             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5340                      fileLock->scp->fid.cell,
5341                      fileLock->scp->fid.volume,
5342                      fileLock->scp->fid.vnode,
5343                      fileLock->scp->fid.unique);
5344             osi_assertx(FALSE, "invalid fid value");
5345         }
5346 #endif
5347
5348         if (!IS_LOCK_DELETED(fileLock) &&
5349             cm_KeyEquals(&fileLock->key, &key, flags)) {
5350             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5351                     fileLock->range.offset,
5352                     fileLock->range.length,
5353                     fileLock->lockType);
5354
5355             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5356
5357             if (IS_LOCK_CLIENTONLY(fileLock)) {
5358                 scp->clientLocks--;
5359             } else if (IS_LOCK_ACCEPTED(fileLock)) {
5360                 if (fileLock->lockType == LockRead)
5361                     scp->sharedLocks--;
5362                 else
5363                     scp->exclusiveLocks--;
5364             }
5365
5366             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5367
5368             cm_ReleaseUser(fileLock->userp);
5369             cm_ReleaseSCacheNoLock(scp);
5370
5371             fileLock->userp = NULL;
5372             fileLock->scp = NULL;
5373
5374             n_unlocks++;
5375         }
5376     }
5377
5378     lock_ReleaseWrite(&cm_scacheLock);
5379
5380     if (n_unlocks == 0) {
5381         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5382         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5383                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5384
5385         return 0;
5386     }
5387
5388     code = cm_IntUnlock(scp, userp, reqp);
5389     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5390
5391     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5392              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5393              (int)(signed char) scp->serverLock);
5394
5395     return code;
5396 }
5397
5398 /* Called with scp->rw held */
5399 long cm_Unlock(cm_scache_t *scp,
5400                unsigned char sLockType,
5401                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5402                cm_key_t key,
5403                afs_uint32 flags,
5404                cm_user_t *userp,
5405                cm_req_t *reqp)
5406 {
5407     long code = 0;
5408     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5409     cm_file_lock_t *fileLock;
5410     osi_queue_t *q;
5411     int release_userp = FALSE;
5412     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5413     int lock_found  = 0;
5414     LARGE_INTEGER RangeEnd;
5415
5416     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5417              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5418     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5419              key.process_id, key.session_id, key.file_id, flags);
5420
5421     if (!exact_match)
5422         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5423
5424   try_again:
5425     lock_ObtainRead(&cm_scacheLock);
5426
5427     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5428         fileLock = (cm_file_lock_t *)
5429             ((char *) q - offsetof(cm_file_lock_t, fileq));
5430
5431 #ifdef DEBUG
5432         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5433             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5434             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5435                      fileLock->fid.cell,
5436                      fileLock->fid.volume,
5437                      fileLock->fid.vnode,
5438                      fileLock->fid.unique);
5439             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5440                      fileLock->scp->fid.cell,
5441                      fileLock->scp->fid.volume,
5442                      fileLock->scp->fid.vnode,
5443                      fileLock->scp->fid.unique);
5444             osi_assertx(FALSE, "invalid fid value");
5445         }
5446 #endif
5447         if (exact_match) {
5448             if (!IS_LOCK_DELETED(fileLock) &&
5449                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5450                  fileLock->range.offset == LOffset.QuadPart &&
5451                  fileLock->range.length == LLength.QuadPart) {
5452                 lock_found = 1;
5453                 break;
5454             }
5455         } else {
5456
5457             if (!IS_LOCK_DELETED(fileLock) &&
5458                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5459                  fileLock->range.offset >= LOffset.QuadPart &&
5460                  fileLock->range.offset < RangeEnd.QuadPart &&
5461                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5462                 lock_found = 1;
5463                 break;
5464             }
5465         }
5466     }
5467
5468     if (!q) {
5469         lock_ReleaseRead(&cm_scacheLock);
5470
5471         if (lock_found && !exact_match) {
5472             code = 0;
5473             goto done;
5474         } else {
5475             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5476
5477             /* The lock didn't exist anyway. *shrug* */
5478             return CM_ERROR_RANGE_NOT_LOCKED;
5479         }
5480     }
5481
5482     /* discard lock record */
5483     lock_ConvertRToW(&cm_scacheLock);
5484     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5485
5486     /*
5487      * Don't delete it here; let the daemon delete it, to simplify
5488      * the daemon's traversal of the list.
5489      */
5490
5491     if (IS_LOCK_CLIENTONLY(fileLock)) {
5492         scp->clientLocks--;
5493     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5494         if (fileLock->lockType == LockRead)
5495             scp->sharedLocks--;
5496         else
5497             scp->exclusiveLocks--;
5498     }
5499
5500     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5501
5502     if (userp != NULL) {
5503         cm_ReleaseUser(fileLock->userp);
5504     } else {
5505         userp = fileLock->userp;
5506         release_userp = TRUE;
5507     }
5508     cm_ReleaseSCacheNoLock(scp);
5509     fileLock->userp = NULL;
5510     fileLock->scp = NULL;
5511     lock_ReleaseWrite(&cm_scacheLock);
5512
5513     code = cm_IntUnlock(scp, userp, reqp);
5514
5515     if (release_userp) {
5516         cm_ReleaseUser(userp);
5517         release_userp = FALSE;
5518     }
5519
5520     if (!exact_match) {
5521         osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5522         goto try_again;         /* might be more than one lock in the range */
5523     }
5524
5525  done:
5526
5527     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5528     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5529              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5530              (int)(signed char) scp->serverLock);
5531
5532     return code;
5533 }
5534
5535 /* called with scp->rw held */
5536 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5537 {
5538     cm_file_lock_t *fileLock;
5539     osi_queue_t *q;
5540
5541     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5542
5543     /* cm_scacheLock needed because we are modifying fileLock->flags */
5544     lock_ObtainWrite(&cm_scacheLock);
5545
5546     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5547         fileLock =
5548             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5549
5550         if (IS_LOCK_ACTIVE(fileLock) &&
5551             !IS_LOCK_CLIENTONLY(fileLock)) {
5552             if (fileLock->lockType == LockRead)
5553                 scp->sharedLocks--;
5554             else
5555                 scp->exclusiveLocks--;
5556
5557             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5558         }
5559     }
5560
5561     scp->serverLock = -1;
5562     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5563     lock_ReleaseWrite(&cm_scacheLock);
5564 }
5565
5566 /* Called with no relevant locks held */
5567 void cm_CheckLocks()
5568 {
5569     osi_queue_t *q, *nq;
5570     cm_file_lock_t *fileLock;
5571     cm_req_t req;
5572     AFSFid tfid;
5573     AFSVolSync volSync;
5574     cm_conn_t *connp;
5575     long code;
5576     struct rx_connection * rxconnp;
5577     cm_scache_t * scp;
5578
5579     memset(&volSync, 0, sizeof(volSync));
5580
5581     cm_InitReq(&req);
5582
5583     lock_ObtainWrite(&cm_scacheLock);
5584
5585     cm_lockRefreshCycle++;
5586
5587     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5588
5589     for (q = cm_allFileLocks; q; q = nq) {
5590         fileLock = (cm_file_lock_t *) q;
5591         nq = osi_QNext(q);
5592         code = -1;
5593
5594         if (IS_LOCK_DELETED(fileLock)) {
5595             cm_user_t *userp = fileLock->userp;
5596             cm_scache_t *scp = fileLock->scp;
5597             fileLock->userp = NULL;
5598             fileLock->scp = NULL;
5599
5600             if (scp && userp) {
5601                 lock_ReleaseWrite(&cm_scacheLock);
5602                 lock_ObtainWrite(&scp->rw);
5603                 code = cm_IntUnlock(scp, userp, &req);
5604                 lock_ReleaseWrite(&scp->rw);
5605
5606                 cm_ReleaseUser(userp);
5607                 lock_ObtainWrite(&cm_scacheLock);
5608                 cm_ReleaseSCacheNoLock(scp);
5609             }
5610             osi_QRemove(&cm_allFileLocks, q);
5611             cm_PutFileLock(fileLock);
5612
5613         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5614
5615             /* Server locks must have been enabled for us to have
5616                received an active non-client-only lock. */
5617             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5618
5619             scp = fileLock->scp;
5620             osi_assertx(scp != NULL, "null cm_scache_t");
5621
5622             cm_HoldSCacheNoLock(scp);
5623
5624 #ifdef DEBUG
5625             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5626                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5627                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5628                          fileLock->fid.cell,
5629                          fileLock->fid.volume,
5630                          fileLock->fid.vnode,
5631                          fileLock->fid.unique);
5632                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5633                          fileLock->scp->fid.cell,
5634                          fileLock->scp->fid.volume,
5635                          fileLock->scp->fid.vnode,
5636                          fileLock->scp->fid.unique);
5637                 osi_assertx(FALSE, "invalid fid");
5638             }
5639 #endif
5640             /* Server locks are extended once per scp per refresh
5641                cycle. */
5642             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5643
5644                 int scp_done = FALSE;
5645
5646                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5647
5648                 lock_ReleaseWrite(&cm_scacheLock);
5649                 lock_ObtainWrite(&scp->rw);
5650
5651                 /* did the lock change while we weren't holding the lock? */
5652                 if (!IS_LOCK_ACTIVE(fileLock))
5653                     goto post_syncopdone;
5654
5655                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5656                                  CM_SCACHESYNC_NEEDCALLBACK
5657                                  | CM_SCACHESYNC_GETSTATUS
5658                                  | CM_SCACHESYNC_LOCK);
5659
5660                 if (code) {
5661                     osi_Log1(afsd_logp,
5662                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5663                     goto post_syncopdone;
5664                 }
5665
5666                 /* cm_SyncOp releases scp->rw during which the lock
5667                    may get released. */
5668                 if (!IS_LOCK_ACTIVE(fileLock))
5669                     goto pre_syncopdone;
5670
5671                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5672                     cm_fid_t cfid;
5673                     cm_user_t * userp;
5674
5675                     tfid.Volume = scp->fid.volume;
5676                     tfid.Vnode = scp->fid.vnode;
5677                     tfid.Unique = scp->fid.unique;
5678                     cfid = scp->fid;
5679                     userp = fileLock->userp;
5680
5681                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5682                              fileLock,
5683                              scp,
5684                              (int) scp->serverLock);
5685
5686                     lock_ReleaseWrite(&scp->rw);
5687
5688                     do {
5689                         code = cm_ConnFromFID(&cfid, userp,
5690                                        &req, &connp);
5691                         if (code)
5692                             break;
5693
5694                         rxconnp = cm_GetRxConn(connp);
5695                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5696                                                 &volSync);
5697                         rx_PutConnection(rxconnp);
5698
5699                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5700
5701                     } while (cm_Analyze(connp, userp, &req,
5702                                         &cfid, NULL, 1, &volSync, NULL, NULL,
5703                                         code));
5704
5705                     code = cm_MapRPCError(code, &req);
5706
5707                     lock_ObtainWrite(&scp->rw);
5708
5709                     if (code) {
5710                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5711                         scp->fsLockCount = 0;
5712                     } else {
5713                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5714                         scp->lockDataVersion = scp->dataVersion;
5715                     }
5716
5717                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5718                         scp->lockDataVersion == scp->dataVersion) {
5719                         int lockType;
5720
5721                         lockType =
5722                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5723
5724                         /* we might still have a chance to obtain a
5725                            new lock */
5726
5727                         code = cm_IntSetLock(scp, userp, lockType, &req);
5728
5729                         if (code) {
5730                             code = CM_ERROR_INVAL;
5731                         } else if (scp->lockDataVersion != scp->dataVersion) {
5732
5733                             /* now check if we still have the file at
5734                                the right data version. */
5735                             osi_Log1(afsd_logp,
5736                                      "Data version mismatch on scp 0x%p",
5737                                      scp);
5738                             osi_Log2(afsd_logp,
5739                                      "   Data versions: before=%I64d, after=%I64d",
5740                                      scp->lockDataVersion,
5741                                      scp->dataVersion);
5742
5743                             code = cm_IntReleaseLock(scp, userp, &req);
5744
5745                             code = CM_ERROR_INVAL;
5746                         }
5747                     }
5748
5749                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5750                         code == CM_ERROR_BADFD) {
5751                         cm_LockMarkSCacheLost(scp);
5752                     }
5753
5754                 } else {
5755                     /* interestingly, we have found an active lock
5756                        belonging to an scache that has no
5757                        serverLock */
5758                     cm_LockMarkSCacheLost(scp);
5759                 }
5760
5761                 scp_done = TRUE;
5762
5763             pre_syncopdone:
5764
5765                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5766
5767             post_syncopdone:
5768                 lock_ReleaseWrite(&scp->rw);
5769
5770                 lock_ObtainWrite(&cm_scacheLock);
5771
5772                 if (code == 0) {
5773                     fileLock->lastUpdate = time(NULL);
5774                 }
5775
5776                 if (scp_done)
5777                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5778
5779             } else {
5780                 /* we have already refreshed the locks on this scp */
5781                 fileLock->lastUpdate = time(NULL);
5782             }
5783
5784             cm_ReleaseSCacheNoLock(scp);
5785
5786         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5787             /* TODO: Check callbacks */
5788         }
5789     }
5790
5791     lock_ReleaseWrite(&cm_scacheLock);
5792     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5793 }
5794
5795 /* NOT called with scp->rw held. */
5796 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5797 {
5798     long code = 0;
5799     cm_scache_t *scp = NULL;
5800     cm_file_lock_t *fileLock;
5801     osi_queue_t *q;
5802     cm_req_t req;
5803     int newLock = -1;
5804     int force_client_lock = FALSE;
5805     int has_insert = FALSE;
5806     int check_data_version = FALSE;
5807
5808     cm_InitReq(&req);
5809
5810     if (client_is_dead) {
5811         code = CM_ERROR_TIMEDOUT;
5812         goto updateLock;
5813     }
5814
5815     lock_ObtainRead(&cm_scacheLock);
5816
5817     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5818     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5819              (unsigned)(oldFileLock->range.offset >> 32),
5820              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5821              (unsigned)(oldFileLock->range.length >> 32),
5822              (unsigned)(oldFileLock->range.length & 0xffffffff));
5823     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5824              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5825              (unsigned)(oldFileLock->flags));
5826
5827     /* if the lock has already been granted, then we have nothing to do */
5828     if (IS_LOCK_ACTIVE(oldFileLock)) {
5829         lock_ReleaseRead(&cm_scacheLock);
5830         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5831         return 0;
5832     }
5833
5834     /* we can't do anything with lost or deleted locks at the moment. */
5835     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5836         code = CM_ERROR_BADFD;
5837         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5838         lock_ReleaseRead(&cm_scacheLock);
5839         goto updateLock;
5840     }
5841
5842     scp = oldFileLock->scp;
5843
5844     osi_assertx(scp != NULL, "null cm_scache_t");
5845
5846     lock_ReleaseRead(&cm_scacheLock);
5847     lock_ObtainWrite(&scp->rw);
5848
5849     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5850                              oldFileLock->userp,
5851                              &req, &has_insert);
5852
5853     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5854         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5855         force_client_lock = TRUE;
5856         }
5857         code = 0;
5858     } else if (code) {
5859         lock_ReleaseWrite(&scp->rw);
5860         return code;
5861     }
5862
5863     lock_ObtainWrite(&cm_scacheLock);
5864
5865     /* Check if we already have a sufficient server lock to allow this
5866        lock to go through. */
5867     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5868         (!SERVERLOCKS_ENABLED(scp) ||
5869          scp->serverLock == oldFileLock->lockType ||
5870          scp->serverLock == LockWrite)) {
5871
5872         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5873
5874         if (SERVERLOCKS_ENABLED(scp)) {
5875             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5876                      (int) scp->serverLock);
5877         } else {
5878             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5879         }
5880
5881         lock_ReleaseWrite(&cm_scacheLock);
5882         lock_ReleaseWrite(&scp->rw);
5883
5884         return 0;
5885     }
5886
5887     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5888
5889         /* check if the conflicting locks have dissappeared already */
5890         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5891
5892             fileLock = (cm_file_lock_t *)
5893                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5894
5895             if (IS_LOCK_LOST(fileLock)) {
5896                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
5897                     code = CM_ERROR_BADFD;
5898                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
5899                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
5900                              fileLock);
5901                     break;
5902                 } else if (fileLock->lockType == LockWrite &&
5903                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5904                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
5905                     code = CM_ERROR_WOULDBLOCK;
5906                     break;
5907                 }
5908             }
5909
5910             if (IS_LOCK_ACCEPTED(fileLock) &&
5911                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
5912
5913                 if (oldFileLock->lockType != LockRead ||
5914                    fileLock->lockType != LockRead) {
5915
5916                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
5917                     code = CM_ERROR_WOULDBLOCK;
5918                     break;
5919                 }
5920             }
5921         }
5922     }
5923
5924     if (code != 0) {
5925         lock_ReleaseWrite(&cm_scacheLock);
5926         lock_ReleaseWrite(&scp->rw);
5927
5928         goto handleCode;
5929     }
5930
5931     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
5932        If it is WAITUNLOCK, then we didn't find any conflicting lock
5933        but we haven't verfied whether the serverLock is sufficient to
5934        assert it.  If it is WAITLOCK, then the serverLock is
5935        insufficient to assert it. Eitherway, we are ready to accept
5936        the lock as either ACTIVE or WAITLOCK depending on the
5937        serverLock. */
5938
5939     /* First, promote the WAITUNLOCK to a WAITLOCK */
5940     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5941         if (oldFileLock->lockType == LockRead)
5942             scp->sharedLocks++;
5943         else
5944             scp->exclusiveLocks++;
5945
5946         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
5947         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
5948     }
5949
5950     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
5951
5952     if (force_client_lock ||
5953         !SERVERLOCKS_ENABLED(scp) ||
5954         scp->serverLock == oldFileLock->lockType ||
5955         (oldFileLock->lockType == LockRead &&
5956          scp->serverLock == LockWrite)) {
5957
5958         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5959
5960         if ((force_client_lock ||
5961              !SERVERLOCKS_ENABLED(scp)) &&
5962             !IS_LOCK_CLIENTONLY(oldFileLock)) {
5963
5964             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5965
5966             if (oldFileLock->lockType == LockRead)
5967                 scp->sharedLocks--;
5968             else
5969                 scp->exclusiveLocks--;
5970
5971             scp->clientLocks++;
5972         }
5973
5974         lock_ReleaseWrite(&cm_scacheLock);
5975         lock_ReleaseWrite(&scp->rw);
5976
5977         return 0;
5978
5979     } else {
5980         cm_user_t * userp;
5981
5982         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
5983                          CM_SCACHESYNC_NEEDCALLBACK
5984                          | CM_SCACHESYNC_GETSTATUS
5985                          | CM_SCACHESYNC_LOCK);
5986         if (code) {
5987             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
5988             lock_ReleaseWrite(&cm_scacheLock);
5989             goto post_syncopdone;
5990         }
5991
5992         if (!IS_LOCK_WAITLOCK(oldFileLock))
5993             goto pre_syncopdone;
5994
5995         userp = oldFileLock->userp;
5996
5997 #ifndef AGGRESSIVE_LOCKS
5998         newLock = oldFileLock->lockType;
5999 #else
6000         newLock = LockWrite;
6001 #endif
6002
6003         if (has_insert) {
6004             /* if has_insert is non-zero, then:
6005                - the lock a LockRead
6006                - we don't have permission to get a LockRead
6007                - we do have permission to get a LockWrite
6008                - the server supports VICED_CAPABILITY_WRITELOCKACL
6009             */
6010
6011             newLock = LockWrite;
6012         }
6013
6014         lock_ReleaseWrite(&cm_scacheLock);
6015
6016         /* when we get here, either we have a read-lock and want a
6017            write-lock or we don't have any locks and we want some
6018            lock. */
6019
6020         if (scp->serverLock == LockRead) {
6021
6022             osi_assertx(newLock == LockWrite, "!LockWrite");
6023
6024             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
6025
6026             scp->lockDataVersion = scp->dataVersion;
6027             check_data_version = TRUE;
6028
6029             code = cm_IntReleaseLock(scp, userp, &req);
6030
6031             if (code)
6032                 goto pre_syncopdone;
6033             else
6034                 scp->serverLock = -1;
6035         }
6036
6037         code = cm_IntSetLock(scp, userp, newLock, &req);
6038
6039         if (code == 0) {
6040             if (scp->dataVersion != scp->lockDataVersion) {
6041                 /* we lost a race.  too bad */
6042
6043                 osi_Log0(afsd_logp,
6044                          "  Data version mismatch while upgrading lock.");
6045                 osi_Log2(afsd_logp,
6046                          "  Data versions before=%I64d, after=%I64d",
6047                          scp->lockDataVersion,
6048                          scp->dataVersion);
6049                 osi_Log1(afsd_logp,
6050                          "  Releasing stale lock for scp 0x%x", scp);
6051
6052                 code = cm_IntReleaseLock(scp, userp, &req);
6053
6054                 scp->serverLock = -1;
6055
6056                 code = CM_ERROR_INVAL;
6057
6058                 cm_LockMarkSCacheLost(scp);
6059             } else {
6060                 scp->serverLock = newLock;
6061             }
6062         }
6063
6064     pre_syncopdone:
6065         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6066     post_syncopdone:
6067         ;
6068     }
6069
6070   handleCode:
6071     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
6072         lock_ObtainWrite(&cm_scacheLock);
6073         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
6074         lock_ReleaseWrite(&cm_scacheLock);
6075     }
6076     lock_ReleaseWrite(&scp->rw);
6077
6078   updateLock:
6079     lock_ObtainWrite(&cm_scacheLock);
6080     if (code == 0) {
6081         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6082     } else if (code != CM_ERROR_WOULDBLOCK) {
6083         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
6084         cm_ReleaseUser(oldFileLock->userp);
6085         oldFileLock->userp = NULL;
6086         if (oldFileLock->scp) {
6087             cm_ReleaseSCacheNoLock(oldFileLock->scp);
6088             oldFileLock->scp = NULL;
6089         }
6090     }
6091     lock_ReleaseWrite(&cm_scacheLock);
6092
6093     return code;
6094 }
6095
6096 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
6097 {
6098     cm_key_t key;
6099
6100     key.process_id = process_id;
6101     key.session_id = session_id;
6102     key.file_id = file_id;
6103
6104     return key;
6105 }
6106
6107 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
6108 {
6109     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
6110         ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
6111 }
6112
6113 void cm_ReleaseAllLocks(void)
6114 {
6115     cm_scache_t *scp;
6116     cm_req_t req;
6117     cm_user_t *userp;
6118     cm_key_t   key;
6119     cm_file_lock_t *fileLock;
6120     unsigned int i;
6121
6122     for (i = 0; i < cm_data.scacheHashTableSize; i++)
6123     {
6124         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
6125             while (scp->fileLocksH != NULL) {
6126                 lock_ObtainWrite(&scp->rw);
6127                 lock_ObtainWrite(&cm_scacheLock);
6128                 if (!scp->fileLocksH) {
6129                     lock_ReleaseWrite(&cm_scacheLock);
6130                     lock_ReleaseWrite(&scp->rw);
6131                     break;
6132                 }
6133                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
6134                 userp = fileLock->userp;
6135                 cm_HoldUser(userp);
6136                 key = fileLock->key;
6137                 cm_HoldSCacheNoLock(scp);
6138                 lock_ReleaseWrite(&cm_scacheLock);
6139                 cm_UnlockByKey(scp, key, 0, userp, &req);
6140                 cm_ReleaseSCache(scp);
6141                 cm_ReleaseUser(userp);
6142                 lock_ReleaseWrite(&scp->rw);
6143             }
6144         }
6145     }
6146 }