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