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