963140e63cb9fa13876d13dd97bdbae7d66de7ae
[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     int available;
2832
2833     /* start by locking out buffer creation */
2834     lock_ObtainWrite(&scp->bufCreateLock);
2835
2836     /* verify that this is a file, not a dir or a symlink */
2837     lock_ObtainWrite(&scp->rw);
2838     code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2839                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2840     if (code)
2841         goto done;
2842     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2843
2844     if (scp->fileType != CM_SCACHETYPE_FILE) {
2845         code = CM_ERROR_ISDIR;
2846         goto done;
2847     }
2848
2849   startover:
2850     if (LargeIntegerLessThan(*sizep, scp->length))
2851         shrinking = 1;
2852     else
2853         shrinking = 0;
2854
2855     lock_ReleaseWrite(&scp->rw);
2856
2857     /* can't hold scp->rw lock here, since we may wait for a storeback to
2858      * finish if the buffer package is cleaning a buffer by storing it to
2859      * the server.
2860      */
2861     if (shrinking)
2862         buf_Truncate(scp, userp, reqp, sizep);
2863
2864     /* now ensure that file length is short enough, and update truncPos */
2865     lock_ObtainWrite(&scp->rw);
2866
2867     /* make sure we have a callback (so we have the right value for the
2868      * length), and wait for it to be safe to do a truncate.
2869      */
2870     code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_WRITE,
2871                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2872                       | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2873
2874     /* If we only have 'i' bits, then we should still be able to set
2875        the size of a file we created. */
2876     if (code == CM_ERROR_NOACCESS && scp->creator == userp) {
2877         code = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
2878                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2879                          | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2880     }
2881
2882     if (code)
2883         goto done;
2884
2885     if (LargeIntegerLessThan(*sizep, scp->length)) {
2886         /* a real truncation.  If truncPos is not set yet, or is bigger
2887          * than where we're truncating the file, set truncPos to this
2888          * new value.
2889          */
2890         if (!shrinking)
2891             goto startover;
2892         if (!(scp->mask & CM_SCACHEMASK_TRUNCPOS)
2893              || LargeIntegerLessThan(*sizep, scp->length)) {
2894             /* set trunc pos */
2895             scp->truncPos = *sizep;
2896             scp->mask |= CM_SCACHEMASK_TRUNCPOS;
2897         }
2898         /* in either case, the new file size has been changed */
2899         scp->length = *sizep;
2900         scp->mask |= CM_SCACHEMASK_LENGTH;
2901     }
2902     else if (LargeIntegerGreaterThan(*sizep, scp->length)) {
2903         /*
2904          * Really extending the file so must check to see if we
2905          * have sufficient quota.  cm_IsSpaceAvailable() obtains
2906          * the cm_scache.rw lock on the volume root directory.
2907          * vnode 1 < scp->fid.vnode therefore calling cm_IsSpaceAvailable
2908          * while holding scp->rw is a lock order violation.
2909          * Dropping it is ok because we are holding scp->bufCreateLock
2910          * which prevents the size of the file from changing.
2911          */
2912         lock_ReleaseWrite(&scp->rw);
2913         available = cm_IsSpaceAvailable(&scp->fid, sizep, userp, reqp);
2914         lock_ObtainWrite(&scp->rw);
2915         if (available) {
2916             scp->length = *sizep;
2917             scp->mask |= CM_SCACHEMASK_LENGTH;
2918         } else {
2919             code = CM_ERROR_SPACE;
2920             goto syncopdone;
2921         }
2922     }
2923
2924     /* done successfully */
2925     code = 0;
2926
2927   syncopdone:
2928     cm_SyncOpDone(scp, NULL,
2929                    CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS
2930                    | CM_SCACHESYNC_SETSTATUS | CM_SCACHESYNC_SETSIZE);
2931
2932   done:
2933     lock_ReleaseWrite(&scp->rw);
2934     lock_ReleaseWrite(&scp->bufCreateLock);
2935
2936     return code;
2937 }
2938
2939 /* set the file size or other attributes (but not both at once) */
2940 long cm_SetAttr(cm_scache_t *scp, cm_attr_t *attrp, cm_user_t *userp,
2941                 cm_req_t *reqp)
2942 {
2943     long code;
2944     AFSFetchStatus afsOutStatus;
2945     AFSVolSync volSync;
2946     cm_conn_t *connp;
2947     AFSFid tfid;
2948     AFSStoreStatus afsInStatus;
2949     struct rx_connection * rxconnp;
2950
2951     memset(&volSync, 0, sizeof(volSync));
2952
2953     /* handle file length setting */
2954     if (attrp->mask & CM_ATTRMASK_LENGTH)
2955         return cm_SetLength(scp, &attrp->length, userp, reqp);
2956
2957     lock_ObtainWrite(&scp->rw);
2958     /* Check for RO volume */
2959     if (scp->flags & CM_SCACHEFLAG_RO) {
2960         code = CM_ERROR_READONLY;
2961         lock_ReleaseWrite(&scp->rw);
2962         return code;
2963     }
2964
2965     /* otherwise, we have to make an RPC to get the status */
2966     code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_STORESTATUS);
2967     if (code) {
2968         lock_ReleaseWrite(&scp->rw);
2969         return code;
2970     }
2971     lock_ConvertWToR(&scp->rw);
2972
2973     /* make the attr structure */
2974     cm_StatusFromAttr(&afsInStatus, scp, attrp);
2975
2976     tfid.Volume = scp->fid.volume;
2977     tfid.Vnode = scp->fid.vnode;
2978     tfid.Unique = scp->fid.unique;
2979     lock_ReleaseRead(&scp->rw);
2980
2981     /* now make the RPC */
2982     InterlockedIncrement(&scp->activeRPCs);
2983
2984     osi_Log1(afsd_logp, "CALL StoreStatus scp 0x%p", scp);
2985     do {
2986         code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
2987         if (code)
2988             continue;
2989
2990         rxconnp = cm_GetRxConn(connp);
2991         code = RXAFS_StoreStatus(rxconnp, &tfid,
2992                                   &afsInStatus, &afsOutStatus, &volSync);
2993         rx_PutConnection(rxconnp);
2994
2995     } while (cm_Analyze(connp, userp, reqp,
2996                          &scp->fid, NULL, 1, &afsOutStatus, &volSync, NULL, NULL, code));
2997     code = cm_MapRPCError(code, reqp);
2998
2999     if (code)
3000         osi_Log1(afsd_logp, "CALL StoreStatus FAILURE, code 0x%x", code);
3001     else
3002         osi_Log0(afsd_logp, "CALL StoreStatus SUCCESS");
3003
3004     lock_ObtainWrite(&scp->rw);
3005     if (code == 0)
3006         code = cm_MergeStatus( NULL, scp, &afsOutStatus, &volSync, userp, reqp,
3007                                CM_MERGEFLAG_FORCE|CM_MERGEFLAG_STOREDATA);
3008     else
3009         InterlockedDecrement(&scp->activeRPCs);
3010     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_STORESTATUS);
3011
3012     /* if we're changing the mode bits, discard the ACL cache,
3013      * since we changed the mode bits.
3014      */
3015     if (afsInStatus.Mask & AFS_SETMODE)
3016         cm_FreeAllACLEnts(scp);
3017     lock_ReleaseWrite(&scp->rw);
3018     return code;
3019 }
3020
3021 long cm_Create(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3022                cm_scache_t **scpp, cm_user_t *userp, cm_req_t *reqp)
3023 {
3024     cm_conn_t *connp;
3025     long code;
3026     AFSFid dirAFSFid;
3027     cm_callbackRequest_t cbReq;
3028     AFSFid newAFSFid;
3029     cm_fid_t newFid;
3030     cm_scache_t *scp = NULL;
3031     int didEnd;
3032     int lostRace;
3033     AFSStoreStatus inStatus;
3034     AFSFetchStatus updatedDirStatus;
3035     AFSFetchStatus newFileStatus;
3036     AFSCallBack newFileCallback;
3037     AFSVolSync volSync;
3038     struct rx_connection * rxconnp;
3039     cm_dirOp_t dirop;
3040     fschar_t * fnamep = NULL;
3041
3042     memset(&volSync, 0, sizeof(volSync));
3043
3044     /* can't create names with @sys in them; must expand it manually first.
3045      * return "invalid request" if they try.
3046      */
3047     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3048         return CM_ERROR_ATSYS;
3049     }
3050
3051 #ifdef AFS_FREELANCE_CLIENT
3052     /* Freelance root volume does not hold files */
3053     if (cm_freelanceEnabled &&
3054         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3055         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3056     {
3057         return CM_ERROR_NOACCESS;
3058     }
3059 #endif /* AFS_FREELANCE_CLIENT */
3060
3061     /* Check for RO volume */
3062     if (dscp->flags & CM_SCACHEFLAG_RO)
3063         return CM_ERROR_READONLY;
3064
3065     /* before starting the RPC, mark that we're changing the file data, so
3066      * that someone who does a chmod will know to wait until our call
3067      * completes.
3068      */
3069     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3070                   &dirop);
3071     lock_ObtainWrite(&dscp->rw);
3072     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3073     lock_ReleaseWrite(&dscp->rw);
3074     if (code == 0) {
3075         cm_StartCallbackGrantingCall(NULL, &cbReq);
3076     } else {
3077         cm_EndDirOp(&dirop);
3078     }
3079     if (code) {
3080         return code;
3081     }
3082     didEnd = 0;
3083
3084     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3085
3086     cm_StatusFromAttr(&inStatus, NULL, attrp);
3087
3088     /* try the RPC now */
3089     InterlockedIncrement(&dscp->activeRPCs);
3090     osi_Log1(afsd_logp, "CALL CreateFile scp 0x%p", dscp);
3091     do {
3092         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3093         if (code)
3094             continue;
3095
3096         dirAFSFid.Volume = dscp->fid.volume;
3097         dirAFSFid.Vnode = dscp->fid.vnode;
3098         dirAFSFid.Unique = dscp->fid.unique;
3099
3100         rxconnp = cm_GetRxConn(connp);
3101         code = RXAFS_CreateFile(connp->rxconnp, &dirAFSFid, fnamep,
3102                                  &inStatus, &newAFSFid, &newFileStatus,
3103                                  &updatedDirStatus, &newFileCallback,
3104                                  &volSync);
3105         rx_PutConnection(rxconnp);
3106
3107     } while (cm_Analyze(connp, userp, reqp,
3108                          &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3109     code = cm_MapRPCError(code, reqp);
3110
3111     if (code)
3112         osi_Log1(afsd_logp, "CALL CreateFile FAILURE, code 0x%x", code);
3113     else
3114         osi_Log0(afsd_logp, "CALL CreateFile SUCCESS");
3115
3116     if (dirop.scp) {
3117         lock_ObtainWrite(&dirop.scp->dirlock);
3118         dirop.lockType = CM_DIRLOCK_WRITE;
3119     }
3120     lock_ObtainWrite(&dscp->rw);
3121     if (code == 0) {
3122         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3123         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3124         if (cm_CheckDirOpForSingleChange(&dirop)) {
3125             lock_ReleaseWrite(&dscp->rw);
3126             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3127 #ifdef USE_BPLUS
3128             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3129 #endif
3130             lock_ObtainWrite(&dscp->rw);
3131         }
3132     } else {
3133         InterlockedDecrement(&dscp->activeRPCs);
3134     }
3135     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3136     lock_ReleaseWrite(&dscp->rw);
3137
3138     /* now try to create the file's entry, too, but be careful to
3139      * make sure that we don't merge in old info.  Since we weren't locking
3140      * out any requests during the file's creation, we may have pretty old
3141      * info.
3142      */
3143     if (code == 0) {
3144         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3145         if (code == 0) {
3146             lock_ObtainWrite(&scp->rw);
3147             scp->creator = userp;               /* remember who created it */
3148             if (!cm_HaveCallback(scp)) {
3149                 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3150                                                       &newFileCallback, &volSync, 0);
3151                 InterlockedIncrement(&scp->activeRPCs);
3152                 if (!lostRace)
3153                     code = cm_MergeStatus( dscp, scp, &newFileStatus, &volSync,
3154                                            userp, reqp, 0);
3155                 didEnd = 1;
3156             }
3157             lock_ReleaseWrite(&scp->rw);
3158         }
3159     }
3160
3161     /* make sure we end things properly */
3162     if (!didEnd)
3163         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3164
3165     cm_EndDirOp(&dirop);
3166
3167     if (fnamep)
3168         free(fnamep);
3169
3170     if (scp) {
3171         if (scpp)
3172             *scpp = scp;
3173         else
3174             cm_ReleaseSCache(scp);
3175     }
3176     return code;
3177 }
3178
3179 /*
3180  * locked if TRUE means write-locked
3181  * else the cm_scache_t rw must not be held
3182  */
3183 long cm_FSync(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
3184 {
3185     long code;
3186
3187     if (locked)
3188         lock_ReleaseWrite(&scp->rw);
3189
3190     osi_Log2(afsd_logp, "cm_FSync scp 0x%p userp 0x%p", scp, userp);
3191
3192     code = buf_CleanVnode(scp, userp, reqp);
3193     if (code == 0) {
3194         lock_ObtainWrite(&scp->rw);
3195
3196         if (scp->mask & (CM_SCACHEMASK_TRUNCPOS
3197                           | CM_SCACHEMASK_CLIENTMODTIME
3198                           | CM_SCACHEMASK_LENGTH))
3199             code = cm_StoreMini(scp, userp, reqp);
3200
3201         if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE)) {
3202             code = (scp->flags & CM_SCACHEFLAG_OVERQUOTA) ? CM_ERROR_QUOTA : CM_ERROR_SPACE;
3203             scp->flags &= ~(CM_SCACHEFLAG_OVERQUOTA | CM_SCACHEFLAG_OUTOFSPACE);
3204         }
3205
3206         if (!locked)
3207             lock_ReleaseWrite(&scp->rw);
3208     } else if (locked) {
3209         lock_ObtainWrite(&scp->rw);
3210     }
3211     return code;
3212 }
3213
3214 long cm_MakeDir(cm_scache_t *dscp, clientchar_t *cnamep, long flags, cm_attr_t *attrp,
3215                 cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3216 {
3217     cm_conn_t *connp;
3218     long code;
3219     AFSFid dirAFSFid;
3220     cm_callbackRequest_t cbReq;
3221     AFSFid newAFSFid;
3222     cm_fid_t newFid;
3223     cm_scache_t *scp = NULL;
3224     int didEnd;
3225     int lostRace;
3226     AFSStoreStatus inStatus;
3227     AFSFetchStatus updatedDirStatus;
3228     AFSFetchStatus newDirStatus;
3229     AFSCallBack newDirCallback;
3230     AFSVolSync volSync;
3231     struct rx_connection * rxconnp;
3232     cm_dirOp_t dirop;
3233     fschar_t * fnamep = NULL;
3234
3235     memset(&volSync, 0, sizeof(volSync));
3236
3237     /* can't create names with @sys in them; must expand it manually first.
3238      * return "invalid request" if they try.
3239      */
3240     if (cm_ExpandSysName(NULL, cnamep, NULL, 0, 0)) {
3241         return CM_ERROR_ATSYS;
3242     }
3243
3244 #ifdef AFS_FREELANCE_CLIENT
3245     /* Freelance root volume does not hold subdirectories */
3246     if (cm_freelanceEnabled &&
3247         dscp->fid.cell==AFS_FAKE_ROOT_CELL_ID &&
3248         dscp->fid.volume==AFS_FAKE_ROOT_VOL_ID )
3249     {
3250         return CM_ERROR_NOACCESS;
3251     }
3252 #endif /* AFS_FREELANCE_CLIENT */
3253
3254     /* Check for RO volume */
3255     if (dscp->flags & CM_SCACHEFLAG_RO)
3256         return CM_ERROR_READONLY;
3257
3258     /* before starting the RPC, mark that we're changing the directory
3259      * data, so that someone who does a chmod on the dir will wait until
3260      * our call completes.
3261      */
3262     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3263                   &dirop);
3264     lock_ObtainWrite(&dscp->rw);
3265     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3266     lock_ReleaseWrite(&dscp->rw);
3267     if (code == 0) {
3268         cm_StartCallbackGrantingCall(NULL, &cbReq);
3269     } else {
3270         cm_EndDirOp(&dirop);
3271     }
3272     if (code) {
3273         return code;
3274     }
3275     didEnd = 0;
3276
3277     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3278     cm_StatusFromAttr(&inStatus, NULL, attrp);
3279
3280     /* try the RPC now */
3281     InterlockedIncrement(&dscp->activeRPCs);
3282     osi_Log1(afsd_logp, "CALL MakeDir scp 0x%p", dscp);
3283     do {
3284         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3285         if (code)
3286             continue;
3287
3288         dirAFSFid.Volume = dscp->fid.volume;
3289         dirAFSFid.Vnode = dscp->fid.vnode;
3290         dirAFSFid.Unique = dscp->fid.unique;
3291
3292         rxconnp = cm_GetRxConn(connp);
3293         code = RXAFS_MakeDir(connp->rxconnp, &dirAFSFid, fnamep,
3294                               &inStatus, &newAFSFid, &newDirStatus,
3295                               &updatedDirStatus, &newDirCallback,
3296                               &volSync);
3297         rx_PutConnection(rxconnp);
3298
3299     } while (cm_Analyze(connp, userp, reqp,
3300                         &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, &cbReq, code));
3301     code = cm_MapRPCError(code, reqp);
3302
3303     if (code)
3304         osi_Log1(afsd_logp, "CALL MakeDir FAILURE, code 0x%x", code);
3305     else
3306         osi_Log0(afsd_logp, "CALL MakeDir SUCCESS");
3307
3308     if (dirop.scp) {
3309         lock_ObtainWrite(&dirop.scp->dirlock);
3310         dirop.lockType = CM_DIRLOCK_WRITE;
3311     }
3312     lock_ObtainWrite(&dscp->rw);
3313     if (code == 0) {
3314         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3315         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3316         if (cm_CheckDirOpForSingleChange(&dirop)) {
3317             lock_ReleaseWrite(&dscp->rw);
3318             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3319 #ifdef USE_BPLUS
3320             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3321 #endif
3322             lock_ObtainWrite(&dscp->rw);
3323         }
3324     } else {
3325         InterlockedDecrement(&dscp->activeRPCs);
3326     }
3327     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3328     lock_ReleaseWrite(&dscp->rw);
3329
3330     /* now try to create the new dir's entry, too, but be careful to
3331      * make sure that we don't merge in old info.  Since we weren't locking
3332      * out any requests during the file's creation, we may have pretty old
3333      * info.
3334      */
3335     if (code == 0) {
3336         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3337         if (code == 0) {
3338             lock_ObtainWrite(&scp->rw);
3339             if (!cm_HaveCallback(scp)) {
3340                 lostRace = cm_EndCallbackGrantingCall(scp, &cbReq,
3341                                                       &newDirCallback, &volSync, 0);
3342                 InterlockedIncrement(&scp->activeRPCs);
3343                 if (!lostRace)
3344                     code = cm_MergeStatus( dscp, scp, &newDirStatus, &volSync,
3345                                            userp, reqp, 0);
3346                 didEnd = 1;
3347             }
3348             lock_ReleaseWrite(&scp->rw);
3349         }
3350     }
3351
3352     /* make sure we end things properly */
3353     if (!didEnd)
3354         cm_EndCallbackGrantingCall(NULL, &cbReq, NULL, NULL, 0);
3355
3356     cm_EndDirOp(&dirop);
3357
3358     free(fnamep);
3359
3360     if (scp) {
3361         if (scpp)
3362             *scpp = scp;
3363         else
3364             cm_ReleaseSCache(scp);
3365     }
3366
3367     /* and return error code */
3368     return code;
3369 }
3370
3371 long cm_Link(cm_scache_t *dscp, clientchar_t *cnamep, cm_scache_t *sscp, long flags,
3372              cm_user_t *userp, cm_req_t *reqp)
3373 {
3374     cm_conn_t *connp;
3375     long code = 0;
3376     AFSFid dirAFSFid;
3377     AFSFid existingAFSFid;
3378     AFSFetchStatus updatedDirStatus;
3379     AFSFetchStatus newLinkStatus;
3380     AFSVolSync volSync;
3381     struct rx_connection * rxconnp;
3382     cm_dirOp_t dirop;
3383     fschar_t * fnamep = NULL;
3384     int invalidate = 0;
3385
3386     memset(&volSync, 0, sizeof(volSync));
3387
3388     if (dscp->fid.cell != sscp->fid.cell ||
3389         dscp->fid.volume != sscp->fid.volume) {
3390         return CM_ERROR_CROSSDEVLINK;
3391     }
3392
3393     /* Check for RO volume */
3394     if (dscp->flags & CM_SCACHEFLAG_RO)
3395         return CM_ERROR_READONLY;
3396
3397     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3398                   &dirop);
3399     lock_ObtainWrite(&dscp->rw);
3400     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3401     lock_ReleaseWrite(&dscp->rw);
3402     if (code != 0)
3403         cm_EndDirOp(&dirop);
3404
3405     if (code)
3406         return code;
3407
3408     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3409
3410     /* try the RPC now */
3411     InterlockedIncrement(&dscp->activeRPCs);
3412     osi_Log1(afsd_logp, "CALL Link scp 0x%p", dscp);
3413     do {
3414         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3415         if (code) continue;
3416
3417         dirAFSFid.Volume = dscp->fid.volume;
3418         dirAFSFid.Vnode = dscp->fid.vnode;
3419         dirAFSFid.Unique = dscp->fid.unique;
3420
3421         existingAFSFid.Volume = sscp->fid.volume;
3422         existingAFSFid.Vnode = sscp->fid.vnode;
3423         existingAFSFid.Unique = sscp->fid.unique;
3424
3425         rxconnp = cm_GetRxConn(connp);
3426         code = RXAFS_Link(rxconnp, &dirAFSFid, fnamep, &existingAFSFid,
3427             &newLinkStatus, &updatedDirStatus, &volSync);
3428         rx_PutConnection(rxconnp);
3429         osi_Log1(afsd_logp,"  RXAFS_Link returns 0x%x", code);
3430
3431     } while (cm_Analyze(connp, userp, reqp, &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3432
3433     code = cm_MapRPCError(code, reqp);
3434
3435     if (code)
3436         osi_Log1(afsd_logp, "CALL Link FAILURE, code 0x%x", code);
3437     else
3438         osi_Log0(afsd_logp, "CALL Link SUCCESS");
3439
3440     if (dirop.scp) {
3441         lock_ObtainWrite(&dirop.scp->dirlock);
3442         dirop.lockType = CM_DIRLOCK_WRITE;
3443     }
3444     lock_ObtainWrite(&dscp->rw);
3445     if (code == 0) {
3446         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3447         invalidate = 1;
3448
3449         if (cm_CheckDirOpForSingleChange(&dirop)) {
3450             lock_ReleaseWrite(&dscp->rw);
3451             cm_DirCreateEntry(&dirop, fnamep, &sscp->fid);
3452 #ifdef USE_BPLUS
3453             cm_BPlusDirCreateEntry(&dirop, cnamep, &sscp->fid);
3454 #endif
3455             lock_ObtainWrite(&dscp->rw);
3456         }
3457     } else {
3458         InterlockedDecrement(&dscp->activeRPCs);
3459     }
3460     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3461     lock_ReleaseWrite(&dscp->rw);
3462
3463     cm_EndDirOp(&dirop);
3464
3465     if (invalidate && RDR_Initialized)
3466         RDR_InvalidateObject(dscp->fid.cell, dscp->fid.volume, dscp->fid.vnode,
3467                              dscp->fid.unique, dscp->fid.hash,
3468                              dscp->fileType, AFS_INVALIDATE_DATA_VERSION);
3469
3470     /* Update the linked object status */
3471     if (code == 0) {
3472         lock_ObtainWrite(&sscp->rw);
3473         InterlockedIncrement(&sscp->activeRPCs);
3474         code = cm_MergeStatus(NULL, sscp, &newLinkStatus, &volSync, userp, reqp, 0);
3475         lock_ReleaseWrite(&sscp->rw);
3476     }
3477
3478     free(fnamep);
3479
3480     return code;
3481 }
3482
3483 long cm_SymLink(cm_scache_t *dscp, clientchar_t *cnamep, fschar_t *contentsp, long flags,
3484                 cm_attr_t *attrp, cm_user_t *userp, cm_req_t *reqp, cm_scache_t **scpp)
3485 {
3486     cm_conn_t *connp;
3487     long code;
3488     AFSFid dirAFSFid;
3489     AFSFid newAFSFid;
3490     cm_fid_t newFid;
3491     cm_scache_t *scp;
3492     AFSStoreStatus inStatus;
3493     AFSFetchStatus updatedDirStatus;
3494     AFSFetchStatus newLinkStatus;
3495     AFSVolSync volSync;
3496     struct rx_connection * rxconnp;
3497     cm_dirOp_t dirop;
3498     fschar_t *fnamep = NULL;
3499
3500     if (scpp)
3501         *scpp = NULL;
3502
3503     /* Check for RO volume */
3504     if (dscp->flags & CM_SCACHEFLAG_RO)
3505         return CM_ERROR_READONLY;
3506
3507     memset(&volSync, 0, sizeof(volSync));
3508
3509     /* before starting the RPC, mark that we're changing the directory data,
3510      * so that someone who does a chmod on the dir will wait until our
3511      * call completes.
3512      */
3513     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3514                   &dirop);
3515     lock_ObtainWrite(&dscp->rw);
3516     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3517     lock_ReleaseWrite(&dscp->rw);
3518     if (code != 0)
3519         cm_EndDirOp(&dirop);
3520     if (code) {
3521         return code;
3522     }
3523
3524     fnamep = cm_ClientStringToFsStringAlloc(cnamep, -1, NULL);
3525
3526     cm_StatusFromAttr(&inStatus, NULL, attrp);
3527
3528     /* try the RPC now */
3529     InterlockedIncrement(&dscp->activeRPCs);
3530     osi_Log1(afsd_logp, "CALL Symlink scp 0x%p", dscp);
3531     do {
3532         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3533         if (code)
3534             continue;
3535
3536         dirAFSFid.Volume = dscp->fid.volume;
3537         dirAFSFid.Vnode = dscp->fid.vnode;
3538         dirAFSFid.Unique = dscp->fid.unique;
3539
3540         rxconnp = cm_GetRxConn(connp);
3541         code = RXAFS_Symlink(rxconnp, &dirAFSFid, fnamep, contentsp,
3542                               &inStatus, &newAFSFid, &newLinkStatus,
3543                               &updatedDirStatus, &volSync);
3544         rx_PutConnection(rxconnp);
3545
3546     } while (cm_Analyze(connp, userp, reqp,
3547                          &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3548     code = cm_MapRPCError(code, reqp);
3549
3550     if (code)
3551         osi_Log1(afsd_logp, "CALL Symlink FAILURE, code 0x%x", code);
3552     else
3553         osi_Log0(afsd_logp, "CALL Symlink SUCCESS");
3554
3555     if (dirop.scp) {
3556         lock_ObtainWrite(&dirop.scp->dirlock);
3557         dirop.lockType = CM_DIRLOCK_WRITE;
3558     }
3559     lock_ObtainWrite(&dscp->rw);
3560     if (code == 0) {
3561         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3562         cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3563         if (cm_CheckDirOpForSingleChange(&dirop)) {
3564             lock_ReleaseWrite(&dscp->rw);
3565             cm_SetFid(&newFid, dscp->fid.cell, dscp->fid.volume, newAFSFid.Vnode, newAFSFid.Unique);
3566
3567             cm_DirCreateEntry(&dirop, fnamep, &newFid);
3568 #ifdef USE_BPLUS
3569             cm_BPlusDirCreateEntry(&dirop, cnamep, &newFid);
3570 #endif
3571             lock_ObtainWrite(&dscp->rw);
3572         }
3573     } else {
3574         InterlockedDecrement(&dscp->activeRPCs);
3575     }
3576     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3577     lock_ReleaseWrite(&dscp->rw);
3578
3579     cm_EndDirOp(&dirop);
3580
3581     /* now try to create the new dir's entry, too, but be careful to
3582      * make sure that we don't merge in old info.  Since we weren't locking
3583      * out any requests during the file's creation, we may have pretty old
3584      * info.
3585      */
3586     if (code == 0) {
3587         code = cm_GetSCache(&newFid, &dscp->fid, &scp, userp, reqp);
3588         if (code == 0) {
3589             lock_ObtainWrite(&scp->rw);
3590             if (!cm_HaveCallback(scp)) {
3591                 InterlockedIncrement(&scp->activeRPCs);
3592                 code = cm_MergeStatus( dscp, scp, &newLinkStatus, &volSync,
3593                                        userp, reqp, 0);
3594             }
3595             lock_ReleaseWrite(&scp->rw);
3596
3597             if (scpp) {
3598                 *scpp = scp;
3599             } else {
3600                 cm_ReleaseSCache(scp);
3601             }
3602         }
3603     }
3604
3605     free(fnamep);
3606
3607     /* and return error code */
3608     return code;
3609 }
3610
3611 /*! \brief Remove a directory
3612
3613   Encapsulates a call to RXAFS_RemoveDir().
3614
3615   \param[in] dscp cm_scache_t for the directory containing the
3616       directory to be removed.
3617
3618   \param[in] fnamep This will be the original name of the directory
3619       as known to the file server.   It will be passed in to RXAFS_RemoveDir().
3620       This parameter is optional.  If it is not provided the value
3621       will be looked up.
3622
3623   \param[in] cnamep Normalized name used to update the local
3624       directory caches.
3625
3626   \param[in] userp cm_user_t for the request.
3627
3628   \param[in] reqp Request tracker.
3629 */
3630 long cm_RemoveDir(cm_scache_t *dscp, fschar_t *fnamep, clientchar_t *cnamep, cm_user_t *userp, cm_req_t *reqp)
3631 {
3632     cm_conn_t *connp;
3633     long code;
3634     AFSFid dirAFSFid;
3635     int didEnd;
3636     AFSFetchStatus updatedDirStatus;
3637     AFSVolSync volSync;
3638     struct rx_connection * rxconnp;
3639     cm_dirOp_t dirop;
3640     cm_scache_t *scp = NULL;
3641     int free_fnamep = FALSE;
3642
3643     memset(&volSync, 0, sizeof(volSync));
3644
3645     if (fnamep == NULL) {
3646         code = -1;
3647 #ifdef USE_BPLUS
3648         code = cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_READ,
3649                              CM_DIROP_FLAG_NONE, &dirop);
3650         if (code == 0) {
3651             code = cm_BPlusDirLookupOriginalName(&dirop, cnamep, &fnamep);
3652             if (code == 0)
3653                 free_fnamep = TRUE;
3654             cm_EndDirOp(&dirop);
3655         }
3656 #endif
3657         if (code)
3658             goto done;
3659     }
3660
3661     code = cm_Lookup(dscp, cnamep, CM_FLAG_NOMOUNTCHASE, userp, reqp, &scp);
3662     if (code)
3663         goto done;
3664
3665     /* Check for RO volume */
3666     if (dscp->flags & CM_SCACHEFLAG_RO) {
3667         code = CM_ERROR_READONLY;
3668         goto done;
3669     }
3670
3671     /* before starting the RPC, mark that we're changing the directory data,
3672      * so that someone who does a chmod on the dir will wait until our
3673      * call completes.
3674      */
3675     cm_BeginDirOp(dscp, userp, reqp, CM_DIRLOCK_NONE, CM_DIROP_FLAG_NONE,
3676                   &dirop);
3677     lock_ObtainWrite(&dscp->rw);
3678     code = cm_SyncOp(dscp, NULL, userp, reqp, 0, CM_SCACHESYNC_STOREDATA);
3679     lock_ReleaseWrite(&dscp->rw);
3680     if (code) {
3681         cm_EndDirOp(&dirop);
3682         goto done;
3683     }
3684     didEnd = 0;
3685
3686     /* try the RPC now */
3687     InterlockedIncrement(&dscp->activeRPCs);
3688     osi_Log1(afsd_logp, "CALL RemoveDir scp 0x%p", dscp);
3689     do {
3690         code = cm_ConnFromFID(&dscp->fid, userp, reqp, &connp);
3691         if (code)
3692             continue;
3693
3694         dirAFSFid.Volume = dscp->fid.volume;
3695         dirAFSFid.Vnode = dscp->fid.vnode;
3696         dirAFSFid.Unique = dscp->fid.unique;
3697
3698         rxconnp = cm_GetRxConn(connp);
3699         code = RXAFS_RemoveDir(rxconnp, &dirAFSFid, fnamep,
3700                                &updatedDirStatus, &volSync);
3701         rx_PutConnection(rxconnp);
3702
3703     } while (cm_Analyze(connp, userp, reqp,
3704                         &dscp->fid, NULL, 1, &updatedDirStatus, &volSync, NULL, NULL, code));
3705     code = cm_MapRPCErrorRmdir(code, reqp);
3706
3707     if (code)
3708         osi_Log1(afsd_logp, "CALL RemoveDir FAILURE, code 0x%x", code);
3709     else
3710         osi_Log0(afsd_logp, "CALL RemoveDir SUCCESS");
3711
3712     if (dirop.scp) {
3713         lock_ObtainWrite(&dirop.scp->dirlock);
3714         dirop.lockType = CM_DIRLOCK_WRITE;
3715     }
3716     lock_ObtainWrite(&dscp->rw);
3717     if (code == 0) {
3718         cm_dnlcRemove(dscp, cnamep);
3719         code = cm_MergeStatus(NULL, dscp, &updatedDirStatus, &volSync, userp, reqp, CM_MERGEFLAG_DIROP);
3720         if (cm_CheckDirOpForSingleChange(&dirop) && cnamep != NULL) {
3721             lock_ReleaseWrite(&dscp->rw);
3722             cm_DirDeleteEntry(&dirop, fnamep);
3723 #ifdef USE_BPLUS
3724             cm_BPlusDirDeleteEntry(&dirop, cnamep);
3725 #endif
3726             lock_ObtainWrite(&dscp->rw);
3727         }
3728     } else {
3729         InterlockedDecrement(&dscp->activeRPCs);
3730     }
3731     cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_STOREDATA);
3732     lock_ReleaseWrite(&dscp->rw);
3733
3734     cm_EndDirOp(&dirop);
3735
3736     if (scp) {
3737         if (code == 0) {
3738             lock_ObtainWrite(&scp->rw);
3739             scp->flags |= CM_SCACHEFLAG_DELETED;
3740             lock_ObtainWrite(&cm_scacheLock);
3741             cm_AdjustScacheLRU(scp);
3742             cm_RemoveSCacheFromHashTable(scp);
3743             lock_ReleaseWrite(&cm_scacheLock);
3744             lock_ReleaseWrite(&scp->rw);
3745             if (RDR_Initialized && !(reqp->flags & CM_REQ_SOURCE_REDIR))
3746                 RDR_InvalidateObject(scp->fid.cell, scp->fid.volume, scp->fid.vnode,
3747                                      scp->fid.unique, scp->fid.hash,
3748                                      scp->fileType, AFS_INVALIDATE_DELETED);
3749         }
3750         cm_ReleaseSCache(scp);
3751     }
3752
3753   done:
3754     if (free_fnamep)
3755         free(fnamep);
3756
3757     /* and return error code */
3758     return code;
3759 }
3760
3761 long cm_Open(cm_scache_t *scp, int type, cm_user_t *userp)
3762 {
3763     /* grab mutex on contents */
3764     lock_ObtainWrite(&scp->rw);
3765
3766     /* reset the prefetch info */
3767     scp->prefetch.base.LowPart = 0;             /* base */
3768     scp->prefetch.base.HighPart = 0;
3769     scp->prefetch.end.LowPart = 0;              /* and end */
3770     scp->prefetch.end.HighPart = 0;
3771
3772     /* release mutex on contents */
3773     lock_ReleaseWrite(&scp->rw);
3774
3775     /* we're done */
3776     return 0;
3777 }
3778
3779 /*! \brief Rename a file or directory
3780
3781   Encapsulates a RXAFS_Rename() call.
3782
3783   \param[in] oldDscp cm_scache_t for the directory containing the old
3784       name.
3785
3786   \param[in] oldNamep The original old name known to the file server.
3787       This is the name that will be passed into the RXAFS_Rename().
3788       If it is not provided, it will be looked up.
3789
3790   \param[in] normalizedOldNamep Normalized old name.  This is used for
3791   updating local directory caches.
3792
3793   \param[in] newDscp cm_scache_t for the directory containing the new
3794   name.
3795
3796   \param[in] newNamep New name. Normalized.
3797
3798   \param[in] userp cm_user_t for the request.
3799
3800   \param[in,out] reqp Request tracker.
3801
3802 */
3803 long cm_Rename(cm_scache_t *oldDscp, fschar_t *oldNamep, clientchar_t *cOldNamep,
3804                cm_scache_t *newDscp, clientchar_t *cNewNamep, cm_user_t *userp,
3805                cm_req_t *reqp)
3806 {
3807     cm_conn_t *connp;
3808     long code = 0;
3809     AFSFid oldDirAFSFid;
3810     AFSFid newDirAFSFid;
3811     AFSFetchStatus updatedOldDirStatus;
3812     AFSFetchStatus updatedNewDirStatus;
3813     AFSVolSync volSync;
3814     int oneDir = 0;
3815     int bTargetExists = 0;
3816     struct rx_connection * rxconnp;
3817     cm_dirOp_t oldDirOp;
3818     cm_fid_t   fileFid;
3819     int        diropCode = -1;
3820     cm_dirOp_t newDirOp;
3821     fschar_t * newNamep = NULL;
3822     int free_oldNamep = FALSE;
3823     cm_scache_t *oldScp = NULL, *oldTargetScp = NULL;
3824     int rpc_skipped = 0;
3825
3826     memset(&volSync, 0, sizeof(volSync));
3827
3828     if (cOldNamep == NULL || cNewNamep == NULL ||
3829         cm_ClientStrLen(cOldNamep) == 0 ||
3830         cm_ClientStrLen(cNewNamep) == 0)
3831         return CM_ERROR_INVAL;
3832
3833     /* check for identical names */
3834     if (oldDscp == newDscp &&
3835         cm_ClientStrCmp(cOldNamep, cNewNamep) == 0) {
3836         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_RENAME_IDENTICAL",
3837                   oldDscp, newDscp);
3838         return CM_ERROR_RENAME_IDENTICAL;
3839     }
3840
3841     /* Check for RO volume */
3842     if ((oldDscp->flags & CM_SCACHEFLAG_RO) || (newDscp->flags & CM_SCACHEFLAG_RO)) {
3843         return CM_ERROR_READONLY;
3844     }
3845
3846     if (oldNamep == NULL) {
3847         code = -1;
3848 #ifdef USE_BPLUS
3849         code = cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_READ,
3850                              CM_DIROP_FLAG_NONE, &oldDirOp);
3851         if (code == 0) {
3852             code = cm_BPlusDirLookupOriginalName(&oldDirOp, cOldNamep, &oldNamep);
3853             if (code == 0)
3854                 free_oldNamep = TRUE;
3855             cm_EndDirOp(&oldDirOp);
3856         }
3857 #endif
3858         if (code) {
3859             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S Original Name lookup failed",
3860                       oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3861             goto done;
3862         }
3863     }
3864
3865     /* before starting the RPC, mark that we're changing the directory data,
3866      * so that someone who does a chmod on the dir will wait until our call
3867      * completes.  We do this in vnode order so that we don't deadlock,
3868      * which makes the code a little verbose.
3869      */
3870     if (oldDscp == newDscp) {
3871         oneDir = 1;
3872         cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3873                       CM_DIROP_FLAG_NONE, &oldDirOp);
3874         lock_ObtainWrite(&oldDscp->rw);
3875         cm_dnlcRemove(oldDscp, cOldNamep);
3876         cm_dnlcRemove(oldDscp, cNewNamep);
3877         code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3878                           CM_SCACHESYNC_STOREDATA);
3879         lock_ReleaseWrite(&oldDscp->rw);
3880         if (code != 0) {
3881             cm_EndDirOp(&oldDirOp);
3882         }
3883     }
3884     else {
3885         /* two distinct dir vnodes */
3886         oneDir = 0;
3887         if (oldDscp->fid.cell != newDscp->fid.cell ||
3888              oldDscp->fid.volume != newDscp->fid.volume) {
3889             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p CM_ERROR_CROSSDEVLINK",
3890                       oldDscp, newDscp);
3891             code = CM_ERROR_CROSSDEVLINK;
3892             goto done;
3893         }
3894
3895         /* shouldn't happen that we have distinct vnodes for two
3896          * different files, but could due to deliberate attack, or
3897          * stale info.  Avoid deadlocks and quit now.
3898          */
3899         if (oldDscp->fid.vnode == newDscp->fid.vnode) {
3900             osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p newDscp 0x%p vnode collision",
3901                       oldDscp, newDscp);
3902             code = CM_ERROR_CROSSDEVLINK;
3903             goto done;
3904         }
3905
3906         if (oldDscp->fid.vnode < newDscp->fid.vnode) {
3907             cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3908                           CM_DIROP_FLAG_NONE, &oldDirOp);
3909             lock_ObtainWrite(&oldDscp->rw);
3910             cm_dnlcRemove(oldDscp, cOldNamep);
3911             code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3912                              CM_SCACHESYNC_STOREDATA);
3913             lock_ReleaseWrite(&oldDscp->rw);
3914             if (code != 0)
3915                 cm_EndDirOp(&oldDirOp);
3916             if (code == 0) {
3917                 cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3918                               CM_DIROP_FLAG_NONE, &newDirOp);
3919                 lock_ObtainWrite(&newDscp->rw);
3920                 cm_dnlcRemove(newDscp, cNewNamep);
3921                 code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3922                                  CM_SCACHESYNC_STOREDATA);
3923                 lock_ReleaseWrite(&newDscp->rw);
3924                 if (code) {
3925                     cm_EndDirOp(&newDirOp);
3926
3927                     /* cleanup first one */
3928                     lock_ObtainWrite(&oldDscp->rw);
3929                     cm_SyncOpDone(oldDscp, NULL,
3930                                    CM_SCACHESYNC_STOREDATA);
3931                     lock_ReleaseWrite(&oldDscp->rw);
3932                     cm_EndDirOp(&oldDirOp);
3933                 }
3934             }
3935         }
3936         else {
3937             /* lock the new vnode entry first */
3938             cm_BeginDirOp(newDscp, userp, reqp, CM_DIRLOCK_NONE,
3939                           CM_DIROP_FLAG_NONE, &newDirOp);
3940             lock_ObtainWrite(&newDscp->rw);
3941             cm_dnlcRemove(newDscp, cNewNamep);
3942             code = cm_SyncOp(newDscp, NULL, userp, reqp, 0,
3943                               CM_SCACHESYNC_STOREDATA);
3944             lock_ReleaseWrite(&newDscp->rw);
3945             if (code != 0)
3946                 cm_EndDirOp(&newDirOp);
3947             if (code == 0) {
3948                 cm_BeginDirOp(oldDscp, userp, reqp, CM_DIRLOCK_NONE,
3949                               CM_DIROP_FLAG_NONE, &oldDirOp);
3950                 lock_ObtainWrite(&oldDscp->rw);
3951                 cm_dnlcRemove(oldDscp, cOldNamep);
3952                 code = cm_SyncOp(oldDscp, NULL, userp, reqp, 0,
3953                                   CM_SCACHESYNC_STOREDATA);
3954                 lock_ReleaseWrite(&oldDscp->rw);
3955                 if (code != 0)
3956                     cm_EndDirOp(&oldDirOp);
3957                 if (code) {
3958                     /* cleanup first one */
3959                     lock_ObtainWrite(&newDscp->rw);
3960                     cm_SyncOpDone(newDscp, NULL,
3961                                    CM_SCACHESYNC_STOREDATA);
3962                     lock_ReleaseWrite(&newDscp->rw);
3963                     cm_EndDirOp(&newDirOp);
3964                 }
3965             }
3966         }
3967     }   /* two distinct vnodes */
3968
3969     if (code)
3970         goto done;
3971
3972     /*
3973      * The source and destination directories are now locked and no other local
3974      * changes can occur.
3975      *
3976      * Before we permit the operation, make sure that we do not already have
3977      * an object in the destination directory that has a case-insensitive match
3978      * for this name UNLESS the matching object is the object we are renaming.
3979      */
3980     code = cm_Lookup(oldDscp, cOldNamep, 0, userp, reqp, &oldScp);
3981     if (code) {
3982         osi_Log2(afsd_logp, "cm_Rename oldDscp 0x%p cOldName %S old name lookup failed",
3983                  oldDscp, osi_LogSaveStringW(afsd_logp, cOldNamep));
3984         rpc_skipped = 1;
3985         goto post_rpc;
3986     }
3987
3988     /* Case sensitive lookup.  If this succeeds we are done. */
3989     code = cm_Lookup(newDscp, cNewNamep, 0, userp, reqp, &oldTargetScp);
3990     if (code) {
3991         /*
3992          * Case insensitive lookup.  If this succeeds, it could have found the
3993          * same file with a name that differs only by case or it could be a
3994          * different file entirely.
3995          */
3996         code = cm_Lookup(newDscp, cNewNamep, CM_FLAG_CASEFOLD, userp, reqp, &oldTargetScp);
3997         if (code == 0) {
3998             /* found a matching object with the new name */
3999             if (cm_FidCmp(&oldScp->fid, &oldTargetScp->fid)) {
4000                 /* and they don't match so return an error */
4001                 osi_Log2(afsd_logp, "cm_Rename newDscp 0x%p cNewName %S new name already exists",
4002                           newDscp, osi_LogSaveStringW(afsd_logp, cNewNamep));
4003                 code = CM_ERROR_EXISTS;
4004             }
4005             cm_ReleaseSCache(oldTargetScp);
4006             oldTargetScp = NULL;
4007         } else if (code == CM_ERROR_AMBIGUOUS_FILENAME) {
4008             code = CM_ERROR_EXISTS;
4009         } else {
4010             /* The target does not exist.  Clear the error and perform the rename. */
4011             code = 0;
4012         }
4013     } else {
4014         bTargetExists = 1;
4015     }
4016
4017     if (code) {
4018         rpc_skipped = 1;
4019         goto post_rpc;
4020     }
4021
4022     newNamep = cm_ClientStringToFsStringAlloc(cNewNamep, -1, NULL);
4023
4024     /* try the RPC now */
4025     InterlockedIncrement(&oldDscp->activeRPCs);
4026     if (!oneDir)
4027         InterlockedIncrement(&newDscp->activeRPCs);
4028     osi_Log2(afsd_logp, "CALL Rename old scp 0x%p new scp 0x%p",
4029               oldDscp, newDscp);
4030     do {
4031         code = cm_ConnFromFID(&oldDscp->fid, userp, reqp, &connp);
4032         if (code)
4033             continue;
4034
4035         oldDirAFSFid.Volume = oldDscp->fid.volume;
4036         oldDirAFSFid.Vnode = oldDscp->fid.vnode;
4037         oldDirAFSFid.Unique = oldDscp->fid.unique;
4038         newDirAFSFid.Volume = newDscp->fid.volume;
4039         newDirAFSFid.Vnode = newDscp->fid.vnode;
4040         newDirAFSFid.Unique = newDscp->fid.unique;
4041
4042         rxconnp = cm_GetRxConn(connp);
4043         code = RXAFS_Rename(rxconnp, &oldDirAFSFid, oldNamep,
4044                             &newDirAFSFid, newNamep,
4045                             &updatedOldDirStatus, &updatedNewDirStatus,
4046                             &volSync);
4047         rx_PutConnection(rxconnp);
4048
4049     } while (cm_Analyze(connp, userp, reqp, &oldDscp->fid, NULL, 1,
4050                         &updatedOldDirStatus, &volSync, NULL, NULL, code));
4051     code = cm_MapRPCError(code, reqp);
4052
4053     if (code)
4054         osi_Log1(afsd_logp, "CALL Rename FAILURE, code 0x%x", code);
4055     else
4056         osi_Log0(afsd_logp, "CALL Rename SUCCESS");
4057
4058   post_rpc:
4059     /* update the individual stat cache entries for the directories */
4060     if (oldDirOp.scp) {
4061         lock_ObtainWrite(&oldDirOp.scp->dirlock);
4062         oldDirOp.lockType = CM_DIRLOCK_WRITE;
4063     }
4064
4065     lock_ObtainWrite(&oldDscp->rw);
4066     if (code == 0) {
4067         code = cm_MergeStatus( NULL, oldDscp, &updatedOldDirStatus, &volSync,
4068                                userp, reqp, CM_MERGEFLAG_DIROP);
4069         if (cm_CheckDirOpForSingleChange(&oldDirOp)) {
4070             lock_ReleaseWrite(&oldDscp->rw);
4071             if (bTargetExists && oneDir) {
4072                 diropCode = cm_DirDeleteEntry(&oldDirOp, newNamep);
4073 #ifdef USE_BPLUS
4074                 cm_BPlusDirDeleteEntry(&oldDirOp, cNewNamep);
4075 #endif
4076             }
4077
4078 #ifdef USE_BPLUS
4079             diropCode = cm_BPlusDirLookup(&oldDirOp, cOldNamep, &fileFid);
4080             if (diropCode == CM_ERROR_INEXACT_MATCH)
4081                 diropCode = 0;
4082             else if (diropCode == EINVAL)
4083 #endif
4084                 diropCode = cm_DirLookup(&oldDirOp, oldNamep, &fileFid);
4085
4086             if (diropCode == 0) {
4087                 if (oneDir) {
4088                     diropCode = cm_DirCreateEntry(&oldDirOp, newNamep, &fileFid);
4089 #ifdef USE_BPLUS
4090                     cm_BPlusDirCreateEntry(&oldDirOp, cNewNamep, &fileFid);
4091 #endif
4092                 }
4093
4094                 if (diropCode == 0) {
4095                     diropCode = cm_DirDeleteEntry(&oldDirOp, oldNamep);
4096 #ifdef USE_BPLUS
4097                     cm_BPlusDirDeleteEntry(&oldDirOp, cOldNamep);
4098 #endif
4099                 }
4100             }
4101             lock_ObtainWrite(&oldDscp->rw);
4102         }
4103     } else {
4104         if (!rpc_skipped)
4105             InterlockedDecrement(&oldDscp->activeRPCs);
4106     }
4107     cm_SyncOpDone(oldDscp, NULL, CM_SCACHESYNC_STOREDATA);
4108     lock_ReleaseWrite(&oldDscp->rw);
4109
4110     cm_EndDirOp(&oldDirOp);
4111
4112     /* and update it for the new one, too, if necessary */
4113     if (!oneDir) {
4114         if (newDirOp.scp) {
4115             lock_ObtainWrite(&newDirOp.scp->dirlock);
4116             newDirOp.lockType = CM_DIRLOCK_WRITE;
4117         }
4118         lock_ObtainWrite(&newDscp->rw);
4119         if (code == 0) {
4120             code = cm_MergeStatus( NULL, newDscp, &updatedNewDirStatus, &volSync,
4121                                    userp, reqp, CM_MERGEFLAG_DIROP);
4122
4123             /*
4124              * we only make the local change if we successfully made
4125              * the change in the old directory AND there was only one
4126              * change in the new directory
4127              */
4128             if (diropCode == 0 && cm_CheckDirOpForSingleChange(&newDirOp)) {
4129                 lock_ReleaseWrite(&newDscp->rw);
4130
4131                 if (bTargetExists && !oneDir) {
4132                     diropCode = cm_DirDeleteEntry(&newDirOp, newNamep);
4133 #ifdef USE_BPLUS
4134                     cm_BPlusDirDeleteEntry(&newDirOp, cNewNamep);
4135 #endif
4136                 }
4137
4138                 cm_DirCreateEntry(&newDirOp, newNamep, &fileFid);
4139 #ifdef USE_BPLUS
4140                 cm_BPlusDirCreateEntry(&newDirOp, cNewNamep, &fileFid);
4141 #endif
4142                 lock_ObtainWrite(&newDscp->rw);
4143             }
4144         } else {
4145             if (!rpc_skipped)
4146                 InterlockedIncrement(&newDscp->activeRPCs);
4147         }
4148         cm_SyncOpDone(newDscp, NULL, CM_SCACHESYNC_STOREDATA);
4149         lock_ReleaseWrite(&newDscp->rw);
4150
4151         cm_EndDirOp(&newDirOp);
4152     }
4153
4154     if (code == 0) {
4155         /*
4156          * After the rename the file server has invalidated the callbacks
4157          * on the file that was moved and destroyed any target file.
4158          */
4159         lock_ObtainWrite(&oldScp->rw);
4160         cm_DiscardSCache(oldScp);
4161         lock_ReleaseWrite(&oldScp->rw);
4162
4163         if (RDR_Initialized)
4164             RDR_InvalidateObject(oldScp->fid.cell, oldScp->fid.volume, oldScp->fid.vnode, oldScp->fid.unique,
4165                                   oldScp->fid.hash, oldScp->fileType, AFS_INVALIDATE_CALLBACK);
4166
4167         if (oldTargetScp) {
4168             lock_ObtainWrite(&oldTargetScp->rw);
4169             cm_DiscardSCache(oldTargetScp);
4170             lock_ReleaseWrite(&oldTargetScp->rw);
4171
4172             if (RDR_Initialized)
4173                 RDR_InvalidateObject(oldTargetScp->fid.cell, oldTargetScp->fid.volume, oldTargetScp->fid.vnode, oldTargetScp->fid.unique,
4174                                      oldTargetScp->fid.hash, oldTargetScp->fileType, AFS_INVALIDATE_CALLBACK);
4175         }
4176     }
4177
4178   done:
4179     if (oldScp)
4180         cm_ReleaseSCache(oldScp);
4181
4182     if (oldTargetScp)
4183         cm_ReleaseSCache(oldTargetScp);
4184
4185     if (free_oldNamep)
4186         free(oldNamep);
4187
4188     free(newNamep);
4189
4190     /* and return error code */
4191     return code;
4192 }
4193
4194 /* Byte range locks:
4195
4196    The OpenAFS Windows client has to fake byte range locks given no
4197    server side support for such locks.  This is implemented as keyed
4198    byte range locks on the cache manager.
4199
4200    Keyed byte range locks:
4201
4202    Each cm_scache_t structure keeps track of a list of keyed locks.
4203    The key for a lock identifies an owner of a set of locks (referred
4204    to as a client).  Each key is represented by a value.  The set of
4205    key values used within a specific cm_scache_t structure form a
4206    namespace that has a scope of just that cm_scache_t structure.  The
4207    same key value can be used with another cm_scache_t structure and
4208    correspond to a completely different client.  However it is
4209    advantageous for the SMB or IFS layer to make sure that there is a
4210    1-1 mapping between client and keys over all cm_scache_t objects.
4211
4212    Assume a client C has key Key(C) (although, since the scope of the
4213    key is a cm_scache_t, the key can be Key(C,S), where S is the
4214    cm_scache_t.  But assume a 1-1 relation between keys and clients).
4215    A byte range (O,+L) denotes byte addresses (O) through (O+L-1)
4216    inclusive (a.k.a. [O,O+L-1]).  The function Key(x) is implemented
4217    through cm_generateKey() function for both SMB and IFS.
4218
4219    The list of locks for a cm_scache_t object S is maintained in
4220    S->fileLocks.  The cache manager will set a lock on the AFS file
4221    server in order to assert the locks in S->fileLocks.  If only
4222    shared locks are in place for S, then the cache manager will obtain
4223    a LockRead lock, while if there are any exclusive locks, it will
4224    obtain a LockWrite lock.  If the exclusive locks are all released
4225    while the shared locks remain, then the cache manager will
4226    downgrade the lock from LockWrite to LockRead.  Similarly, if an
4227    exclusive lock is obtained when only shared locks exist, then the
4228    cache manager will try to upgrade the lock from LockRead to
4229    LockWrite.
4230
4231    Each lock L owned by client C maintains a key L->key such that
4232    L->key == Key(C), the effective range defined by L->LOffset and
4233    L->LLength such that the range of bytes affected by the lock is
4234    (L->LOffset, +L->LLength), a type maintained in L->LockType which
4235    is either exclusive or shared.
4236
4237    Lock states:
4238
4239    A lock exists iff it is in S->fileLocks for some cm_scache_t
4240    S. Existing locks are in one of the following states: ACTIVE,
4241    WAITLOCK, WAITUNLOCK, LOST, DELETED.
4242
4243    The following sections describe each lock and the associated
4244    transitions.
4245
4246    1. ACTIVE: A lock L is ACTIVE iff the cache manager has asserted
4247       the lock with the AFS file server.  This type of lock can be
4248       exercised by a client to read or write to the locked region (as
4249       the lock allows).
4250
4251       1.1 ACTIVE->LOST: When the AFS file server fails to extend a
4252         server lock that was required to assert the lock.  Before
4253         marking the lock as lost, the cache manager checks if the file
4254         has changed on the server.  If the file has not changed, then
4255         the cache manager will attempt to obtain a new server lock
4256         that is sufficient to assert the client side locks for the
4257         file.  If any of these fail, the lock is marked as LOST.
4258         Otherwise, it is left as ACTIVE.
4259
4260       1.2 ACTIVE->DELETED: Lock is released.
4261
4262    2. WAITLOCK: A lock is in a WAITLOCK state if the cache manager
4263       grants the lock but the lock is yet to be asserted with the AFS
4264       file server.  Once the file server grants the lock, the state
4265       will transition to an ACTIVE lock.
4266
4267       2.1 WAITLOCK->ACTIVE: The server granted the lock.
4268
4269       2.2 WAITLOCK->DELETED: Lock is abandoned, or timed out during
4270         waiting.
4271
4272       2.3 WAITLOCK->LOST: One or more locks from this client were
4273         marked as LOST.  No further locks will be granted to this
4274         client until all lost locks are removed.
4275
4276    3. WAITUNLOCK: A lock is in a WAITUNLOCK state if the cache manager
4277       receives a request for a lock that conflicts with an existing
4278       ACTIVE or WAITLOCK lock.  The lock will be placed in the queue
4279       and will be granted at such time the conflicting locks are
4280       removed, at which point the state will transition to either
4281       WAITLOCK or ACTIVE.
4282
4283       3.1 WAITUNLOCK->ACTIVE: The conflicting lock was removed.  The
4284         current serverLock is sufficient to assert this lock, or a
4285         sufficient serverLock is obtained.
4286
4287       3.2 WAITUNLOCK->WAITLOCK: The conflicting lock was removed,
4288         however the required serverLock is yet to be asserted with the
4289         server.
4290
4291       3.3 WAITUNLOCK->DELETED: The lock is abandoned, timed out or
4292         released.
4293
4294       3.5 WAITUNLOCK->LOST: One or more locks from this client were
4295         marked as LOST.  No further locks will be granted to this
4296         client until all lost locks are removed.
4297
4298    4. LOST: A lock L is LOST if the server lock that was required to
4299       assert the lock could not be obtained or if it could not be
4300       extended, or if other locks by the same client were LOST.
4301       Essentially, once a lock is LOST, the contract between the cache
4302       manager and that specific client is no longer valid.
4303
4304       The cache manager rechecks the server lock once every minute and
4305       extends it as appropriate.  If this is not done for 5 minutes,
4306       the AFS file server will release the lock (the 5 minute timeout
4307       is based on current file server code and is fairly arbitrary).
4308       Once released, the lock cannot be re-obtained without verifying
4309       that the contents of the file hasn't been modified since the
4310       time the lock was released.  Re-obtaining the lock without
4311       verifying this may lead to data corruption.  If the lock can not
4312       be obtained safely, then all active locks for the cm_scache_t
4313       are marked as LOST.
4314
4315       4.1 LOST->DELETED: The lock is released.
4316
4317    5. DELETED: The lock is no longer relevant.  Eventually, it will
4318       get removed from the cm_scache_t. In the meantime, it will be
4319       treated as if it does not exist.
4320
4321       5.1 DELETED->not exist: The lock is removed from the
4322         cm_scache_t.
4323
4324    The following are classifications of locks based on their state.
4325
4326    6* A lock L is ACCEPTED if it is ACTIVE or WAITLOCK.  These locks
4327       have been accepted by the cache manager, but may or may not have
4328       been granted back to the client.
4329
4330    7* A lock L is QUEUED if it is ACTIVE, WAITLOCK or WAITUNLOCK.
4331
4332    8* A lock L is WAITING if it is WAITLOCK or WAITUNLOCK.
4333
4334    Lock operation:
4335
4336    A client C can READ range (Offset,+Length) of a file represented by
4337    cm_scache_t S iff (1):
4338
4339    1. for all _a_ in (Offset,+Length), all of the following is true:
4340
4341        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4342          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4343          shared.
4344
4345        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4346          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4347          Key(C)
4348
4349        (When locks are lost on an cm_scache_t, all locks are lost.  By
4350        4.2 (below), if there is an exclusive LOST lock, then there
4351        can't be any overlapping ACTIVE locks.)
4352
4353    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4354
4355    2. for all _a_ in (Offset,+Length), one of the following is true:
4356
4357        2.1 Byte _a_ of S is unowned (as specified in 1.1) AND there
4358          does not exist a LOST lock L such that _a_ in
4359          (L->LOffset,+L->LLength).
4360
4361        2.2 Byte _a_ of S is owned by C under lock L (as specified in
4362          1.2) AND L->LockType is exclusive.
4363
4364    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4365
4366    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4367       true:
4368
4369        3.1 If L->LockType is exclusive then there does NOT exist a
4370          ACCEPTED lock M in S->fileLocks such that _a_ in
4371          (M->LOffset,+M->LLength).
4372
4373          (If we count all QUEUED locks then we hit cases such as
4374          cascading waiting locks where the locks later on in the queue
4375          can be granted without compromising file integrity.  On the
4376          other hand if only ACCEPTED locks are considered, then locks
4377          that were received earlier may end up waiting for locks that
4378          were received later to be unlocked. The choice of ACCEPTED
4379          locks was made to mimic the Windows byte range lock
4380          semantics.)
4381
4382        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4383          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4384          M->LockType is shared.
4385
4386    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4387
4388        4.1 M->key != Key(C)
4389
4390        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4391          and (M->LOffset,+M->LLength) do not intersect.
4392
4393          (Note: If a client loses a lock, it loses all locks.
4394          Subsequently, it will not be allowed to obtain any more locks
4395          until all existing LOST locks that belong to the client are
4396          released.  Once all locks are released by a single client,
4397          there exists no further contract between the client and AFS
4398          about the contents of the file, hence the client can then
4399          proceed to obtain new locks and establish a new contract.
4400
4401          This doesn't quite work as you think it should, because most
4402          applications aren't built to deal with losing locks they
4403          thought they once had.  For now, we don't have a good
4404          solution to lost locks.
4405
4406          Also, for consistency reasons, we have to hold off on
4407          granting locks that overlap exclusive LOST locks.)
4408
4409    A client C can only unlock locks L in S->fileLocks which have
4410    L->key == Key(C).
4411
4412    The representation and invariants are as follows:
4413
4414    - Each cm_scache_t structure keeps:
4415
4416        - A queue of byte-range locks (cm_scache_t::fileLocks) which
4417          are of type cm_file_lock_t.
4418
4419        - A record of the highest server-side lock that has been
4420          obtained for this object (cm_scache_t::serverLock), which is
4421          one of (-1), LockRead, LockWrite.
4422
4423        - A count of ACCEPTED exclusive and shared locks that are in the
4424          queue (cm_scache_t::sharedLocks and
4425          cm_scache_t::exclusiveLocks)
4426
4427    - Each cm_file_lock_t structure keeps:
4428
4429        - The type of lock (cm_file_lock_t::LockType)
4430
4431        - The key associated with the lock (cm_file_lock_t::key)
4432
4433        - The offset and length of the lock (cm_file_lock_t::LOffset
4434          and cm_file_lock_t::LLength)
4435
4436        - The state of the lock.
4437
4438        - Time of issuance or last successful extension
4439
4440    Semantic invariants:
4441
4442        I1. The number of ACCEPTED locks in S->fileLocks are
4443            (S->sharedLocks + S->exclusiveLocks)
4444
4445    External invariants:
4446
4447        I3. S->serverLock is the lock that we have asserted with the
4448            AFS file server for this cm_scache_t.
4449
4450        I4. S->serverLock == LockRead iff there is at least one ACTIVE
4451            shared lock, but no ACTIVE exclusive locks.
4452
4453        I5. S->serverLock == LockWrite iff there is at least one ACTIVE
4454            exclusive lock.
4455
4456        I6. If L is a LOST lock, then for each lock M in S->fileLocks,
4457            M->key == L->key IMPLIES M is LOST or DELETED.
4458
4459    --asanka
4460  */
4461
4462 #define IS_LOCK_ACTIVE(lockp)     (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_WAITLOCK|CM_FILELOCK_FLAG_WAITUNLOCK|CM_FILELOCK_FLAG_LOST)) == 0)
4463
4464 #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)
4465
4466 #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)
4467
4468 #define IS_LOCK_LOST(lockp)       (((lockp)->flags & (CM_FILELOCK_FLAG_DELETED|CM_FILELOCK_FLAG_LOST)) == CM_FILELOCK_FLAG_LOST)
4469
4470 #define IS_LOCK_DELETED(lockp)    (((lockp)->flags & CM_FILELOCK_FLAG_DELETED) == CM_FILELOCK_FLAG_DELETED)
4471
4472 /* unsafe */
4473 #define IS_LOCK_ACCEPTED(lockp)   (IS_LOCK_ACTIVE(lockp) || IS_LOCK_WAITLOCK(lockp))
4474
4475 /* unsafe */
4476 #define IS_LOCK_CLIENTONLY(lockp) ((((lockp)->scp->flags & CM_SCACHEFLAG_RO) == CM_SCACHEFLAG_RO) || (((lockp)->flags & CM_FILELOCK_FLAG_CLIENTONLY) == CM_FILELOCK_FLAG_CLIENTONLY))
4477
4478 /* unsafe */
4479 #define INTERSECT_RANGE(r1,r2) (((r2).offset+(r2).length) > (r1).offset && ((r1).offset +(r1).length) > (r2).offset)
4480
4481 /* unsafe */
4482 #define CONTAINS_RANGE(r1,r2) (((r2).offset+(r2).length) <= ((r1).offset+(r1).length) && (r1).offset <= (r2).offset)
4483
4484 #if defined(VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS) && !defined(LOCK_TESTING)
4485 #define SCP_SUPPORTS_BRLOCKS(scp) ((scp)->cbServerp && ((scp)->cbServerp->capabilities & VICED_CAPABILITY_USE_BYTE_RANGE_LOCKS))
4486 #else
4487 #define SCP_SUPPORTS_BRLOCKS(scp) (1)
4488 #endif
4489
4490 #define SERVERLOCKS_ENABLED(scp) (!((scp)->flags & CM_SCACHEFLAG_RO) && cm_enableServerLocks && SCP_SUPPORTS_BRLOCKS(scp))
4491
4492 #if defined(VICED_CAPABILITY_WRITELOCKACL)
4493 #define SCP_SUPPORTS_WRITELOCKACL(scp) ((scp)->cbServerp && ((scp->cbServerp->capabilities & VICED_CAPABILITY_WRITELOCKACL)))
4494 #else
4495 #define SCP_SUPPORTS_WRITELOCKACL(scp) (0)
4496
4497 /* This should really be defined in any build that this code is being
4498    compiled. */
4499 #error  VICED_CAPABILITY_WRITELOCKACL not defined.
4500 #endif
4501
4502 static void cm_LockRangeSubtract(cm_range_t * pos, const cm_range_t * neg)
4503 {
4504     afs_int64 int_begin;
4505     afs_int64 int_end;
4506
4507     int_begin = max(pos->offset, neg->offset);
4508     int_end = min(pos->offset+pos->length, neg->offset+neg->length);
4509
4510     if (int_begin < int_end) {
4511         if (int_begin == pos->offset) {
4512             pos->length = pos->offset + pos->length - int_end;
4513             pos->offset = int_end;
4514         } else if (int_end == pos->offset + pos->length) {
4515             pos->length = int_begin - pos->offset;
4516         }
4517
4518         /* We only subtract ranges if the resulting range is
4519            contiguous.  If we try to support non-contigous ranges, we
4520            aren't actually improving performance. */
4521     }
4522 }
4523
4524 /* Called with scp->rw held.  Returns 0 if all is clear to read the
4525    specified range by the client identified by key.
4526  */
4527 long cm_LockCheckRead(cm_scache_t *scp,
4528                       LARGE_INTEGER LOffset,
4529                       LARGE_INTEGER LLength,
4530                       cm_key_t key)
4531 {
4532 #ifndef ADVISORY_LOCKS
4533
4534     cm_file_lock_t *fileLock;
4535     osi_queue_t *q;
4536     long code = 0;
4537     cm_range_t range;
4538     int substract_ranges = FALSE;
4539
4540     range.offset = LOffset.QuadPart;
4541     range.length = LLength.QuadPart;
4542
4543     /*
4544
4545      1. for all _a_ in (Offset,+Length), all of the following is true:
4546
4547        1.1 For each ACTIVE lock L in S->fileLocks such that _a_ in
4548          (L->LOffset,+L->LLength); L->key == Key(C) OR L->LockType is
4549          shared.
4550
4551        1.2 For each LOST lock L in S->fileLocks such that _a_ in
4552          (L->LOffset,+L->LLength); L->LockType is shared AND L->key !=
4553          Key(C)
4554
4555     */
4556
4557     lock_ObtainRead(&cm_scacheLock);
4558
4559     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4560         fileLock =
4561             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4562
4563         if (INTERSECT_RANGE(range, fileLock->range)) {
4564             if (IS_LOCK_ACTIVE(fileLock)) {
4565                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4566
4567                     /* If there is an active lock for this client, it
4568                        is safe to substract ranges.*/
4569                     cm_LockRangeSubtract(&range, &fileLock->range);
4570                     substract_ranges = TRUE;
4571                 } else {
4572                     if (fileLock->lockType != LockRead) {
4573                         code = CM_ERROR_LOCK_CONFLICT;
4574                         break;
4575                     }
4576
4577                     /* even if the entire range is locked for reading,
4578                        we still can't grant the lock at this point
4579                        because the client may have lost locks. That
4580                        is, unless we have already seen an active lock
4581                        belonging to the client, in which case there
4582                        can't be any lost locks for this client. */
4583                     if (substract_ranges)
4584                         cm_LockRangeSubtract(&range, &fileLock->range);
4585                 }
4586             } else if (IS_LOCK_LOST(fileLock) &&
4587                        (cm_KeyEquals(&fileLock->key, &key, 0) || fileLock->lockType == LockWrite)) {
4588                 code = CM_ERROR_BADFD;
4589                 break;
4590             }
4591         }
4592     }
4593
4594     lock_ReleaseRead(&cm_scacheLock);
4595
4596     osi_Log4(afsd_logp, "cm_LockCheckRead scp 0x%x offset %d length %d code 0x%x",
4597               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4598
4599     return code;
4600
4601 #else
4602
4603     return 0;
4604
4605 #endif
4606 }
4607
4608 /* Called with scp->rw held.  Returns 0 if all is clear to write the
4609    specified range by the client identified by key.
4610  */
4611 long cm_LockCheckWrite(cm_scache_t *scp,
4612                        LARGE_INTEGER LOffset,
4613                        LARGE_INTEGER LLength,
4614                        cm_key_t key)
4615 {
4616 #ifndef ADVISORY_LOCKS
4617
4618     cm_file_lock_t *fileLock;
4619     osi_queue_t *q;
4620     long code = 0;
4621     cm_range_t range;
4622
4623     range.offset = LOffset.QuadPart;
4624     range.length = LLength.QuadPart;
4625
4626     /*
4627    A client C can WRITE range (Offset,+Length) of cm_scache_t S iff (2):
4628
4629    2. for all _a_ in (Offset,+Length), one of the following is true:
4630
4631        2.1 Byte _a_ of S is unowned AND there does not exist a LOST
4632          lock L such that _a_ in (L->LOffset,+L->LLength).
4633
4634        2.2 Byte _a_ of S is owned by C under lock L AND L->LockType is
4635          exclusive.
4636     */
4637
4638     lock_ObtainRead(&cm_scacheLock);
4639
4640     for (q = scp->fileLocksH; q && range.length > 0; q = osi_QNext(q)) {
4641         fileLock =
4642             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4643
4644         if (INTERSECT_RANGE(range, fileLock->range)) {
4645             if (IS_LOCK_ACTIVE(fileLock)) {
4646                 if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4647                     if (fileLock->lockType == LockWrite) {
4648
4649                         /* if there is an active lock for this client, it
4650                            is safe to substract ranges */
4651                         cm_LockRangeSubtract(&range, &fileLock->range);
4652                     } else {
4653                         code = CM_ERROR_LOCK_CONFLICT;
4654                         break;
4655                     }
4656                 } else {
4657                     code = CM_ERROR_LOCK_CONFLICT;
4658                     break;
4659                 }
4660             } else if (IS_LOCK_LOST(fileLock)) {
4661                 code = CM_ERROR_BADFD;
4662                 break;
4663             }
4664         }
4665     }
4666
4667     lock_ReleaseRead(&cm_scacheLock);
4668
4669     osi_Log4(afsd_logp, "cm_LockCheckWrite scp 0x%x offset %d length %d code 0x%x",
4670               scp, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart, code);
4671
4672     return code;
4673
4674 #else
4675
4676     return 0;
4677
4678 #endif
4679 }
4680
4681 /* Called with cm_scacheLock write locked */
4682 static cm_file_lock_t * cm_GetFileLock(void) {
4683     cm_file_lock_t * l;
4684
4685     l = (cm_file_lock_t *) cm_freeFileLocks;
4686     if (l) {
4687         osi_QRemove(&cm_freeFileLocks, &l->q);
4688     } else {
4689         l = malloc(sizeof(cm_file_lock_t));
4690         osi_assertx(l, "null cm_file_lock_t");
4691     }
4692
4693     memset(l, 0, sizeof(cm_file_lock_t));
4694
4695     return l;
4696 }
4697
4698 /* Called with cm_scacheLock write locked */
4699 static void cm_PutFileLock(cm_file_lock_t *l) {
4700     osi_QAdd(&cm_freeFileLocks, &l->q);
4701 }
4702
4703 /* called with scp->rw held.  May release it during processing, but
4704    leaves it held on exit. */
4705 long cm_IntSetLock(cm_scache_t * scp, cm_user_t * userp, int lockType,
4706                    cm_req_t * reqp) {
4707     long code = 0;
4708     AFSFid tfid;
4709     cm_fid_t cfid;
4710     cm_conn_t * connp;
4711     struct rx_connection * rxconnp;
4712     AFSVolSync volSync;
4713     afs_uint32 reqflags = reqp->flags;
4714
4715     osi_Log2(afsd_logp, "CALL SetLock scp 0x%p for lock %d", scp, lockType);
4716
4717 #if 0
4718     /*
4719      * The file server prior to 1.6.2 does not report an accurate value
4720      * and callbacks are not issued if the lock is dropped due to expiration.
4721      */
4722     if ((lockType != LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount != 0) ||
4723          (lockType == LOCKING_ANDX_SHARED_LOCK && scp->fsLockCount < 0))
4724     {
4725         code = CM_ERROR_LOCK_NOT_GRANTED;
4726         osi_Log2(afsd_logp, "CALL SetLock FAILURE, fsLockCount %d code 0x%x", scp->fsLockCount, code);
4727         return code;
4728     }
4729 #endif
4730
4731     memset(&volSync, 0, sizeof(volSync));
4732
4733     tfid.Volume = scp->fid.volume;
4734     tfid.Vnode = scp->fid.vnode;
4735     tfid.Unique = scp->fid.unique;
4736     cfid = scp->fid;
4737
4738     reqp->flags |= CM_REQ_NORETRY;
4739     lock_ReleaseWrite(&scp->rw);
4740
4741     do {
4742         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4743         if (code)
4744             break;
4745
4746         rxconnp = cm_GetRxConn(connp);
4747         code = RXAFS_SetLock(rxconnp, &tfid, lockType,
4748                              &volSync);
4749         rx_PutConnection(rxconnp);
4750
4751     } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, NULL, &volSync,
4752                         NULL, NULL, code));
4753
4754     code = cm_MapRPCError(code, reqp);
4755     if (code) {
4756         osi_Log1(afsd_logp, "CALL SetLock FAILURE, code 0x%x", code);
4757     } else {
4758         osi_Log0(afsd_logp, "CALL SetLock SUCCESS");
4759     }
4760
4761     reqp->flags = reqflags;
4762
4763     lock_ObtainWrite(&scp->rw);
4764     if (code == 0) {
4765         /*
4766          * The file server does not return a status structure so we must
4767          * locally track the file server lock count to the best of our
4768          * ability.
4769          */
4770         if (lockType == LockWrite)
4771             scp->fsLockCount = -1;
4772         else
4773             scp->fsLockCount++;
4774     }
4775     return code;
4776 }
4777
4778 /* called with scp->rw held.  Releases it during processing */
4779 long cm_IntReleaseLock(cm_scache_t * scp, cm_user_t * userp,
4780                        cm_req_t * reqp) {
4781     long code = 0;
4782     AFSFid tfid;
4783     cm_fid_t cfid;
4784     cm_conn_t * connp;
4785     struct rx_connection * rxconnp;
4786     AFSVolSync volSync;
4787
4788     if (scp->flags & CM_SCACHEFLAG_DELETED) {
4789         osi_Log1(afsd_logp, "CALL ReleaseLock on Deleted Vnode scp 0x%p", scp);
4790         return 0;
4791     }
4792
4793     memset(&volSync, 0, sizeof(volSync));
4794
4795     tfid.Volume = scp->fid.volume;
4796     tfid.Vnode = scp->fid.vnode;
4797     tfid.Unique = scp->fid.unique;
4798     cfid = scp->fid;
4799
4800     lock_ReleaseWrite(&scp->rw);
4801
4802     osi_Log1(afsd_logp, "CALL ReleaseLock scp 0x%p", scp);
4803
4804     do {
4805         code = cm_ConnFromFID(&cfid, userp, reqp, &connp);
4806         if (code)
4807             break;
4808
4809         rxconnp = cm_GetRxConn(connp);
4810         code = RXAFS_ReleaseLock(rxconnp, &tfid, &volSync);
4811         rx_PutConnection(rxconnp);
4812
4813     } while (cm_Analyze(connp, userp, reqp, &cfid, NULL, 1, NULL, &volSync,
4814                         NULL, NULL, code));
4815     code = cm_MapRPCError(code, reqp);
4816     if (code)
4817         osi_Log1(afsd_logp,
4818                  "CALL ReleaseLock FAILURE, code 0x%x", code);
4819     else
4820         osi_Log0(afsd_logp,
4821                  "CALL ReleaseLock SUCCESS");
4822
4823     lock_ObtainWrite(&scp->rw);
4824     if (code == 0) {
4825         /*
4826          * The file server does not return a status structure so we must
4827          * locally track the file server lock count to the best of our
4828          * ability.
4829          */
4830         scp->fsLockCount--;
4831         if (scp->fsLockCount < 0)
4832             scp->fsLockCount = 0;
4833     }
4834
4835     return (code != CM_ERROR_BADFD ? code : 0);
4836 }
4837
4838 /* called with scp->rw held.  May release it during processing, but
4839    will exit with lock held.
4840
4841    This will return:
4842
4843    - 0 if the user has permission to get the specified lock for the scp
4844
4845    - CM_ERROR_NOACCESS if not
4846
4847    Any other error from cm_SyncOp will be sent down untranslated.
4848
4849    If CM_ERROR_NOACCESS is returned and lock_type is LockRead, then
4850    phas_insert (if non-NULL) will receive a boolean value indicating
4851    whether the user has INSERT permission or not.
4852 */
4853 long cm_LockCheckPerms(cm_scache_t * scp,
4854                        int lock_type,
4855                        cm_user_t * userp,
4856                        cm_req_t * reqp,
4857                        int * phas_insert)
4858 {
4859     long rights = 0;
4860     long code = 0, code2 = 0;
4861
4862     /* lock permissions are slightly tricky because of the 'i' bit.
4863        If the user has PRSFS_LOCK, she can read-lock the file.  If the
4864        user has PRSFS_WRITE, she can write-lock the file.  However, if
4865        the user has PRSFS_INSERT, then she can write-lock new files,
4866        but not old ones.  Since we don't have information about
4867        whether a file is new or not, we assume that if the user owns
4868        the scp, then she has the permissions that are granted by
4869        PRSFS_INSERT. */
4870
4871     osi_Log3(afsd_logp, "cm_LockCheckPerms for scp[0x%p] type[%d] user[0x%p]",
4872              scp, lock_type, userp);
4873
4874     if (lock_type == LockRead)
4875         rights |= PRSFS_LOCK;
4876     else if (lock_type == LockWrite)
4877         rights |= PRSFS_WRITE | PRSFS_LOCK;
4878     else {
4879         /* hmmkay */
4880         osi_assertx(FALSE, "invalid lock type");
4881         return 0;
4882     }
4883
4884     if (phas_insert)
4885         *phas_insert = FALSE;
4886
4887     code = cm_SyncOp(scp, NULL, userp, reqp, rights,
4888                      CM_SCACHESYNC_GETSTATUS |
4889                      CM_SCACHESYNC_NEEDCALLBACK);
4890
4891     if (phas_insert && scp->creator == userp) {
4892
4893         /* If this file was created by the user, then we check for
4894            PRSFS_INSERT.  If the file server is recent enough, then
4895            this should be sufficient for her to get a write-lock (but
4896            not necessarily a read-lock). VICED_CAPABILITY_WRITELOCKACL
4897            indicates whether a file server supports getting write
4898            locks when the user only has PRSFS_INSERT.
4899
4900            If the file was not created by the user we skip the check
4901            because the INSERT bit will not apply to this user even
4902            if it is set.
4903          */
4904
4905         code2 = cm_SyncOp(scp, NULL, userp, reqp, PRSFS_INSERT,
4906                          CM_SCACHESYNC_GETSTATUS |
4907                          CM_SCACHESYNC_NEEDCALLBACK);
4908
4909         if (code2 == CM_ERROR_NOACCESS) {
4910             osi_Log0(afsd_logp, "cm_LockCheckPerms user has no INSERT bits");
4911         } else {
4912             *phas_insert = TRUE;
4913             osi_Log0(afsd_logp, "cm_LockCheckPerms user has INSERT bits");
4914         }
4915     }
4916
4917     cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4918
4919     osi_Log1(afsd_logp, "cm_LockCheckPerms returning code %d", code);
4920
4921     return code;
4922 }
4923
4924 /* called with scp->rw held */
4925 long cm_Lock(cm_scache_t *scp, unsigned char sLockType,
4926              LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
4927              cm_key_t key,
4928              int allowWait, cm_user_t *userp, cm_req_t *reqp,
4929              cm_file_lock_t **lockpp)
4930 {
4931     long code = 0;
4932     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
4933     cm_file_lock_t *fileLock;
4934     osi_queue_t *q;
4935     cm_range_t range;
4936     int wait_unlock = FALSE;
4937     int force_client_lock = FALSE;
4938
4939     osi_Log4(afsd_logp, "cm_Lock scp 0x%x type 0x%x offset %d length %d",
4940              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
4941     osi_Log4(afsd_logp, "... allowWait %d key <0x%x, 0x%x, 0x%x>", allowWait,
4942              key.process_id, key.session_id, key.file_id);
4943
4944     /*
4945    A client C can OBTAIN a lock L on cm_scache_t S iff (both 3 and 4):
4946
4947    3. for all _a_ in (L->LOffset,+L->LLength), ALL of the following is
4948       true:
4949
4950        3.1 If L->LockType is exclusive then there does NOT exist a
4951          ACCEPTED lock M in S->fileLocks such that _a_ in
4952          (M->LOffset,+M->LLength).
4953
4954        3.2 If L->LockType is shared then for each ACCEPTED lock M in
4955          S->fileLocks, if _a_ in (M->LOffset,+M->LLength) then
4956          M->LockType is shared.
4957
4958    4. For all LOST locks M in S->fileLocks, ALL of the following are true:
4959
4960        4.1 M->key != Key(C)
4961
4962        4.2 If M->LockType is exclusive, then (L->LOffset,+L->LLength)
4963          and (M->LOffset,+M->LLength) do not intersect.
4964     */
4965
4966     range.offset = LOffset.QuadPart;
4967     range.length = LLength.QuadPart;
4968
4969     lock_ObtainRead(&cm_scacheLock);
4970
4971     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
4972         fileLock =
4973             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
4974
4975         if (IS_LOCK_LOST(fileLock)) {
4976             if (cm_KeyEquals(&fileLock->key, &key, 0)) {
4977                 code = CM_ERROR_BADFD;
4978                 break;
4979             } else if (fileLock->lockType == LockWrite && INTERSECT_RANGE(range, fileLock->range)) {
4980                 code = CM_ERROR_WOULDBLOCK;
4981                 wait_unlock = TRUE;
4982                 break;
4983             }
4984         }
4985
4986         /* we don't need to check for deleted locks here since deleted
4987            locks are dequeued from scp->fileLocks */
4988         if (IS_LOCK_ACCEPTED(fileLock) &&
4989            INTERSECT_RANGE(range, fileLock->range)) {
4990
4991             if ((sLockType & LOCKING_ANDX_SHARED_LOCK) == 0 ||
4992                 fileLock->lockType != LockRead) {
4993                 wait_unlock = TRUE;
4994                 code = CM_ERROR_WOULDBLOCK;
4995                 break;
4996             }
4997         }
4998     }
4999
5000     lock_ReleaseRead(&cm_scacheLock);
5001
5002     if (code == 0 && SERVERLOCKS_ENABLED(scp)) {
5003         if (Which == scp->serverLock ||
5004            (Which == LockRead && scp->serverLock == LockWrite)) {
5005
5006             int has_insert = 0;
5007
5008             /* we already have the lock we need */
5009             osi_Log3(afsd_logp, "   we already have the correct lock. exclusives[%d], shared[%d], serverLock[%d]",
5010                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5011
5012             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
5013
5014             /* special case: if we don't have permission to read-lock
5015                the file, then we force a clientside lock.  This is to
5016                compensate for applications that obtain a read-lock for
5017                reading files off of directories that don't grant
5018                read-locks to the user. */
5019             if (code == CM_ERROR_NOACCESS && Which == LockRead) {
5020
5021                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5022                     osi_Log0(afsd_logp, "   User has no read-lock perms, but has INSERT perms.");
5023                     code = 0;
5024                 } else {
5025                     osi_Log0(afsd_logp, "   User has no read-lock perms. Forcing client-side lock");
5026                     force_client_lock = TRUE;
5027                 }
5028             }
5029
5030         } else if ((scp->exclusiveLocks > 0) ||
5031                    (scp->sharedLocks > 0 && scp->serverLock != LockRead)) {
5032             int has_insert = 0;
5033
5034             /* We are already waiting for some other lock.  We should
5035                wait for the daemon to catch up instead of generating a
5036                flood of SetLock calls. */
5037             osi_Log3(afsd_logp, "   already waiting for other lock. exclusives[%d], shared[%d], serverLock[%d]",
5038                      scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5039
5040             /* see if we have permission to create the lock in the
5041                first place. */
5042             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
5043             if (code == 0)
5044                 code = CM_ERROR_WOULDBLOCK;
5045             else if (code == CM_ERROR_NOACCESS && Which == LockRead) {
5046
5047                 if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5048                     osi_Log0(afsd_logp,
5049                              "   User has no read-lock perms, but has INSERT perms.");
5050                     code = CM_ERROR_WOULDBLOCK;
5051                 } else {
5052                     osi_Log0(afsd_logp,
5053                              "   User has no read-lock perms. Forcing client-side lock");
5054                     force_client_lock = TRUE;
5055                 }
5056             }
5057
5058             /* leave any other codes as-is */
5059
5060         } else {
5061             int newLock;
5062             int check_data_version = FALSE;
5063             int has_insert = 0;
5064
5065             /* first check if we have permission to elevate or obtain
5066                the lock. */
5067             code = cm_LockCheckPerms(scp, Which, userp, reqp, &has_insert);
5068             if (code) {
5069                 if (code == CM_ERROR_NOACCESS && Which == LockRead &&
5070                     (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp))) {
5071                     osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
5072                     force_client_lock = TRUE;
5073                 }
5074                 goto check_code;
5075             }
5076
5077             /* has_insert => (Which == LockRead, code == CM_ERROR_NOACCESS) */
5078
5079             if (scp->serverLock == LockRead && Which == LockWrite) {
5080
5081                 /* We want to escalate the lock to a LockWrite.
5082                  * Unfortunately that's not really possible without
5083                  * letting go of the current lock.  But for now we do
5084                  * it anyway. */
5085
5086                 osi_Log0(afsd_logp,
5087                          "   attempting to UPGRADE from LockRead to LockWrite.");
5088                 osi_Log1(afsd_logp,
5089                          "   dataVersion on scp: %I64d", scp->dataVersion);
5090
5091                 /* we assume at this point (because scp->serverLock
5092                    was valid) that we had a valid server lock. */
5093                 scp->lockDataVersion = scp->dataVersion;
5094                 check_data_version = TRUE;
5095
5096                 code = cm_IntReleaseLock(scp, userp, reqp);
5097
5098                 if (code) {
5099                     /* We couldn't release the lock */
5100                     goto check_code;
5101                 } else {
5102                     scp->serverLock = -1;
5103                 }
5104             }
5105
5106             /* We need to obtain a server lock of type Which in order
5107              * to assert this file lock */
5108 #ifndef AGGRESSIVE_LOCKS
5109             newLock = Which;
5110 #else
5111             newLock = LockWrite;
5112 #endif
5113
5114             code = cm_IntSetLock(scp, userp, newLock, reqp);
5115
5116 #ifdef AGGRESSIVE_LOCKS
5117             if ((code == CM_ERROR_WOULDBLOCK ||
5118                  code == CM_ERROR_NOACCESS) && newLock != Which) {
5119                 /* we wanted LockRead.  We tried LockWrite. Now try
5120                  * LockRead again */
5121                 newLock = Which;
5122
5123                 /* am I sane? */
5124                 osi_assertx(newLock == LockRead, "lock type not read");
5125
5126                 code = cm_IntSetLock(scp, userp, newLock, reqp);
5127             }
5128 #endif
5129
5130             if (code == CM_ERROR_NOACCESS) {
5131                 if (Which == LockRead) {
5132                     if (has_insert && SCP_SUPPORTS_WRITELOCKACL(scp)) {
5133                         long tcode;
5134                         /* We requested a read-lock, but we have permission to
5135                          * get a write-lock. Try that */
5136
5137                         tcode = cm_LockCheckPerms(scp, LockWrite, userp, reqp, NULL);
5138
5139                         if (tcode == 0) {
5140                             newLock = LockWrite;
5141
5142                             osi_Log0(afsd_logp, "   User has 'i' perms and the request was for a LockRead.  Trying to get a LockWrite instead");
5143
5144                             code = cm_IntSetLock(scp, userp, newLock, reqp);
5145                         }
5146                     } else {
5147                         osi_Log0(afsd_logp, "   User has no read-lock perms.  Forcing client-side lock");
5148                         force_client_lock = TRUE;
5149                     }
5150                 } else if (Which == LockWrite &&
5151                            scp->creator == userp && !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5152                     long tcode;
5153
5154                     /* Special case: if the lock request was for a
5155                      * LockWrite and the user owns the file and we weren't
5156                      * allowed to obtain the serverlock, we either lost a
5157                      * race (the permissions changed from under us), or we
5158                      * have 'i' bits, but we aren't allowed to lock the
5159                      * file. */
5160
5161                     /* check if we lost a race... */
5162                     tcode = cm_LockCheckPerms(scp, Which, userp, reqp, NULL);
5163
5164                     if (tcode == 0) {
5165                         osi_Log0(afsd_logp, "   User has 'i' perms but can't obtain write locks. Using client-side locks.");
5166                         force_client_lock = TRUE;
5167                     }
5168                 }
5169             }
5170
5171             if (code == 0 && check_data_version &&
5172                scp->dataVersion != scp->lockDataVersion) {
5173                 /* We lost a race.  Although we successfully obtained
5174                  * a lock, someone modified the file in between.  The
5175                  * locks have all been technically lost. */
5176
5177                 osi_Log0(afsd_logp,
5178                          "  Data version mismatch while upgrading lock.");
5179                 osi_Log2(afsd_logp,
5180                          "  Data versions before=%I64d, after=%I64d",
5181                          scp->lockDataVersion,
5182                          scp->dataVersion);
5183                 osi_Log1(afsd_logp,
5184                          "  Releasing stale lock for scp 0x%x", scp);
5185
5186                 code = cm_IntReleaseLock(scp, userp, reqp);
5187
5188                 scp->serverLock = -1;
5189
5190                 code = CM_ERROR_INVAL;
5191             } else if (code == 0) {
5192                 scp->serverLock = newLock;
5193                 scp->lockDataVersion = scp->dataVersion;
5194             }
5195
5196             if (code != 0 &&
5197                 (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5198                 scp->serverLock == -1) {
5199                 /* Oops. We lost the lock. */
5200                 cm_LockMarkSCacheLost(scp);
5201             }
5202         }
5203     } else if (code == 0) {     /* server locks not enabled */
5204         osi_Log0(afsd_logp,
5205                  "  Skipping server lock for scp");
5206     }
5207
5208  check_code:
5209
5210     if (code != 0 && !force_client_lock) {
5211         /* Special case error translations
5212
5213            Applications don't expect certain errors from a
5214            LockFile/UnlockFile call.  We need to translate some error
5215            code to codes that apps expect and handle. */
5216
5217         /* We shouldn't actually need to handle this case since we
5218            simulate locks for RO scps anyway. */
5219         if (code == CM_ERROR_READONLY) {
5220             osi_Log0(afsd_logp, "   Reinterpreting CM_ERROR_READONLY as CM_ERROR_NOACCESS");
5221             code = CM_ERROR_NOACCESS;
5222         }
5223     }
5224
5225     if (code == 0 || (code == CM_ERROR_WOULDBLOCK && allowWait) ||
5226         force_client_lock) {
5227
5228         /* clear the error if we are forcing a client lock, so we
5229            don't get confused later. */
5230         if (force_client_lock && code != CM_ERROR_WOULDBLOCK)
5231             code = 0;
5232
5233         cm_HoldUser(userp);
5234
5235         lock_ObtainWrite(&cm_scacheLock);
5236         fileLock = cm_GetFileLock();
5237 #ifdef DEBUG
5238         fileLock->fid = scp->fid;
5239 #endif
5240         fileLock->key = key;
5241         fileLock->lockType = Which;
5242         fileLock->userp = userp;
5243         fileLock->range = range;
5244         fileLock->flags = (code == 0 ? 0 :
5245                            ((wait_unlock)?
5246                             CM_FILELOCK_FLAG_WAITUNLOCK :
5247                             CM_FILELOCK_FLAG_WAITLOCK));
5248
5249         if (force_client_lock || !SERVERLOCKS_ENABLED(scp))
5250             fileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
5251
5252         fileLock->lastUpdate = (code == 0 && !force_client_lock) ? time(NULL) : 0;
5253
5254         osi_QAddT(&scp->fileLocksH, &scp->fileLocksT, &fileLock->fileq);
5255         cm_HoldSCacheNoLock(scp);
5256         fileLock->scp = scp;
5257         osi_QAdd(&cm_allFileLocks, &fileLock->q);
5258         lock_ReleaseWrite(&cm_scacheLock);
5259
5260         if (code != 0) {
5261             *lockpp = fileLock;
5262         }
5263
5264         if (IS_LOCK_CLIENTONLY(fileLock)) {
5265             scp->clientLocks++;
5266         } else if (IS_LOCK_ACCEPTED(fileLock)) {
5267             if (Which == LockRead)
5268                 scp->sharedLocks++;
5269             else
5270                 scp->exclusiveLocks++;
5271         }
5272
5273         osi_Log3(afsd_logp,
5274                  "cm_Lock Lock added 0x%p flags 0x%x to scp [0x%p]",
5275                  fileLock, fileLock->flags, scp);
5276         osi_Log4(afsd_logp,
5277                  "   exclusives[%d] shared[%d] client[%d] serverLock[%d]",
5278                  scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5279                  (int)(signed char) scp->serverLock);
5280     } else {
5281         osi_Log1(afsd_logp,
5282                  "cm_Lock Rejecting lock (code = 0x%x)", code);
5283     }
5284
5285     /* Convert from would block to lock not granted */
5286     if (code == CM_ERROR_WOULDBLOCK)
5287         code = CM_ERROR_LOCK_NOT_GRANTED;
5288
5289     return code;
5290 }
5291
5292 static long
5293 cm_IntUnlock(cm_scache_t * scp,
5294              cm_user_t * userp,
5295              cm_req_t *  reqp)
5296 {
5297     long code = 0;
5298
5299     osi_assertx(scp->sharedLocks >= 0, "scp->sharedLocks < 0");
5300     osi_assertx(scp->exclusiveLocks >= 0, "scp->exclusiveLocks < 0");
5301     osi_assertx(scp->clientLocks >= 0, "scp->clientLocks < 0");
5302
5303     if (!SERVERLOCKS_ENABLED(scp)) {
5304         osi_Log0(afsd_logp, "  Skipping server lock for scp");
5305         goto done;
5306     }
5307
5308     /* Ideally we would go through the rest of the locks to determine
5309      * if one or more locks that were formerly in WAITUNLOCK can now
5310      * be put to ACTIVE or WAITLOCK and update scp->exclusiveLocks and
5311      * scp->sharedLocks accordingly.  However, the retrying of locks
5312      * in that manner is done cm_RetryLock() manually.
5313      */
5314
5315     if (scp->serverLock == LockWrite &&
5316         scp->exclusiveLocks == 0 &&
5317         scp->sharedLocks > 0) {
5318         /* The serverLock should be downgraded to LockRead */
5319         osi_Log0(afsd_logp, "  DOWNGRADE lock from LockWrite to LockRead");
5320
5321         /* Make sure there are no dirty buffers left. */
5322         code = cm_FSync(scp, userp, reqp, TRUE);
5323
5324         /* since scp->serverLock looked sane, we are going to assume
5325            that we have a valid server lock. */
5326         scp->lockDataVersion = scp->dataVersion;
5327         osi_Log1(afsd_logp, "  dataVersion on scp = %I64d", scp->dataVersion);
5328
5329         /* before we downgrade, make sure that we have enough
5330            permissions to get the read lock. */
5331         code = cm_LockCheckPerms(scp, LockRead, userp, reqp, NULL);
5332         if (code != 0) {
5333
5334             osi_Log0(afsd_logp, "  SKIPPING downgrade because user doesn't have perms to get downgraded lock");
5335
5336             code = 0;
5337             goto done;
5338         }
5339
5340         code = cm_IntReleaseLock(scp, userp, reqp);
5341
5342         if (code) {
5343             /* so we couldn't release it.  Just let the lock be for now */
5344             code = 0;
5345             goto done;
5346         } else {
5347             scp->serverLock = -1;
5348         }
5349
5350         code = cm_IntSetLock(scp, userp, LockRead, reqp);
5351
5352         if (code == 0 && scp->lockDataVersion == scp->dataVersion) {
5353             scp->serverLock = LockRead;
5354         } else if (code == 0 && scp->lockDataVersion != scp->dataVersion) {
5355             /* We lost a race condition.  Although we have a valid
5356                lock on the file, the data has changed and essentially
5357                we have lost the lock we had during the transition. */
5358
5359             osi_Log0(afsd_logp, "Data version mismatch during lock downgrade");
5360             osi_Log2(afsd_logp, "  Data versions before=%I64d, after=%I64d",
5361                      scp->lockDataVersion,
5362                      scp->dataVersion);
5363
5364             code = cm_IntReleaseLock(scp, userp, reqp);
5365
5366             code = CM_ERROR_INVAL;
5367             scp->serverLock = -1;
5368         }
5369
5370         if (code != 0 &&
5371             (scp->sharedLocks > 0 || scp->exclusiveLocks > 0) &&
5372                 (scp->serverLock == -1)) {
5373                 /* Oopsie */
5374                 cm_LockMarkSCacheLost(scp);
5375             }
5376
5377         /* failure here has no bearing on the return value of cm_Unlock() */
5378         code = 0;
5379
5380     } else if (scp->serverLock != (-1) &&
5381               scp->exclusiveLocks == 0 &&
5382               scp->sharedLocks == 0) {
5383         /* The serverLock should be released entirely */
5384
5385         if (scp->serverLock == LockWrite) {
5386             osi_Log0(afsd_logp, "  RELEASE LockWrite -> LockNone");
5387
5388             /* Make sure there are no dirty buffers left. */
5389             code = cm_FSync(scp, userp, reqp, TRUE);
5390         } else {
5391             osi_Log0(afsd_logp, "  RELEASE LockRead -> LockNone");
5392         }
5393
5394         code = cm_IntReleaseLock(scp, userp, reqp);
5395
5396         if (code == 0)
5397             scp->serverLock = (-1);
5398     }
5399
5400   done:
5401     return code;
5402 }
5403 /* Called with scp->rw held */
5404 long cm_UnlockByKey(cm_scache_t * scp,
5405                     cm_key_t key,
5406                     afs_uint32 flags,
5407                     cm_user_t * userp,
5408                     cm_req_t * reqp)
5409 {
5410     long code = 0;
5411     cm_file_lock_t *fileLock;
5412     osi_queue_t *q, *qn;
5413     int n_unlocks = 0;
5414
5415     osi_Log4(afsd_logp, "cm_UnlockByKey scp 0x%p key <0x%x,0x%x,0x%x",
5416              scp, key.process_id, key.session_id, key.file_id);
5417     osi_Log1(afsd_logp, "    flags=0x%x", flags);
5418
5419     lock_ObtainWrite(&cm_scacheLock);
5420
5421     for (q = scp->fileLocksH; q; q = qn) {
5422         qn = osi_QNext(q);
5423
5424         fileLock = (cm_file_lock_t *)
5425             ((char *) q - offsetof(cm_file_lock_t, fileq));
5426
5427 #ifdef DEBUG
5428         osi_Log4(afsd_logp, "   Checking lock[0x%x] range[%d,+%d] type[%d]",
5429                  fileLock,
5430                  (unsigned long) fileLock->range.offset,
5431                  (unsigned long) fileLock->range.length,
5432                 fileLock->lockType);
5433         osi_Log4(afsd_logp, "     key<0x%x, 0x%x, 0x%x> flags[0x%x]",
5434                  fileLock->key.process_id, fileLock->key.session_id, fileLock->key.file_id,
5435                  fileLock->flags);
5436
5437         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5438             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5439             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5440                      fileLock->fid.cell,
5441                      fileLock->fid.volume,
5442                      fileLock->fid.vnode,
5443                      fileLock->fid.unique);
5444             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5445                      fileLock->scp->fid.cell,
5446                      fileLock->scp->fid.volume,
5447                      fileLock->scp->fid.vnode,
5448                      fileLock->scp->fid.unique);
5449             osi_assertx(FALSE, "invalid fid value");
5450         }
5451 #endif
5452
5453         if (!IS_LOCK_DELETED(fileLock) &&
5454             cm_KeyEquals(&fileLock->key, &key, flags)) {
5455             osi_Log3(afsd_logp, "...Unlock range [%d,+%d] type %d",
5456                     fileLock->range.offset,
5457                     fileLock->range.length,
5458                     fileLock->lockType);
5459
5460             osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5461
5462             if (IS_LOCK_CLIENTONLY(fileLock)) {
5463                 scp->clientLocks--;
5464             } else if (IS_LOCK_ACCEPTED(fileLock)) {
5465                 if (fileLock->lockType == LockRead)
5466                     scp->sharedLocks--;
5467                 else
5468                     scp->exclusiveLocks--;
5469             }
5470
5471             fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5472
5473             cm_ReleaseUser(fileLock->userp);
5474             cm_ReleaseSCacheNoLock(scp);
5475
5476             fileLock->userp = NULL;
5477             fileLock->scp = NULL;
5478
5479             n_unlocks++;
5480         }
5481     }
5482
5483     lock_ReleaseWrite(&cm_scacheLock);
5484
5485     if (n_unlocks == 0) {
5486         osi_Log0(afsd_logp, "cm_UnlockByKey no locks found");
5487         osi_Log3(afsd_logp, "   Leaving scp with exclusives[%d], shared[%d], serverLock[%d]",
5488                  scp->exclusiveLocks, scp->sharedLocks, (int)(signed char) scp->serverLock);
5489
5490         return 0;
5491     }
5492
5493     code = cm_IntUnlock(scp, userp, reqp);
5494     osi_Log1(afsd_logp, "cm_UnlockByKey code 0x%x", code);
5495
5496     osi_Log4(afsd_logp, "   Leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5497              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5498              (int)(signed char) scp->serverLock);
5499
5500     return code;
5501 }
5502
5503 /* Called with scp->rw held */
5504 long cm_Unlock(cm_scache_t *scp,
5505                unsigned char sLockType,
5506                LARGE_INTEGER LOffset, LARGE_INTEGER LLength,
5507                cm_key_t key,
5508                afs_uint32 flags,
5509                cm_user_t *userp,
5510                cm_req_t *reqp)
5511 {
5512     long code = 0;
5513     int Which = ((sLockType & LOCKING_ANDX_SHARED_LOCK) ? LockRead : LockWrite);
5514     cm_file_lock_t *fileLock;
5515     osi_queue_t *q;
5516     int release_userp = FALSE;
5517     int exact_match = !(flags & CM_UNLOCK_FLAG_MATCH_RANGE);
5518     int lock_found  = 0;
5519     LARGE_INTEGER RangeEnd;
5520
5521     osi_Log4(afsd_logp, "cm_Unlock scp 0x%p type 0x%x offset 0x%x length 0x%x",
5522              scp, sLockType, (unsigned long)LOffset.QuadPart, (unsigned long)LLength.QuadPart);
5523     osi_Log4(afsd_logp, "... key <0x%x,0x%x,0x%x> flags 0x%x",
5524              key.process_id, key.session_id, key.file_id, flags);
5525
5526     if (!exact_match)
5527         RangeEnd.QuadPart = LOffset.QuadPart + LLength.QuadPart;
5528
5529   try_again:
5530     lock_ObtainRead(&cm_scacheLock);
5531
5532     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5533         fileLock = (cm_file_lock_t *)
5534             ((char *) q - offsetof(cm_file_lock_t, fileq));
5535
5536 #ifdef DEBUG
5537         if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5538             osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5539             osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5540                      fileLock->fid.cell,
5541                      fileLock->fid.volume,
5542                      fileLock->fid.vnode,
5543                      fileLock->fid.unique);
5544             osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5545                      fileLock->scp->fid.cell,
5546                      fileLock->scp->fid.volume,
5547                      fileLock->scp->fid.vnode,
5548                      fileLock->scp->fid.unique);
5549             osi_assertx(FALSE, "invalid fid value");
5550         }
5551 #endif
5552         if (exact_match) {
5553             if (!IS_LOCK_DELETED(fileLock) &&
5554                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5555                  fileLock->range.offset == LOffset.QuadPart &&
5556                  fileLock->range.length == LLength.QuadPart) {
5557                 lock_found = 1;
5558                 break;
5559             }
5560         } else {
5561
5562             if (!IS_LOCK_DELETED(fileLock) &&
5563                  cm_KeyEquals(&fileLock->key, &key, 0) &&
5564                  fileLock->range.offset >= LOffset.QuadPart &&
5565                  fileLock->range.offset < RangeEnd.QuadPart &&
5566                  (fileLock->range.offset + fileLock->range.length) <= RangeEnd.QuadPart) {
5567                 lock_found = 1;
5568                 break;
5569             }
5570         }
5571     }
5572
5573     if (!q) {
5574         lock_ReleaseRead(&cm_scacheLock);
5575
5576         if (lock_found && !exact_match) {
5577             code = 0;
5578             goto done;
5579         } else {
5580             osi_Log0(afsd_logp, "cm_Unlock lock not found; failure");
5581
5582             /* The lock didn't exist anyway. *shrug* */
5583             return CM_ERROR_RANGE_NOT_LOCKED;
5584         }
5585     }
5586
5587     /* discard lock record */
5588     lock_ConvertRToW(&cm_scacheLock);
5589     osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, q);
5590
5591     /*
5592      * Don't delete it here; let the daemon delete it, to simplify
5593      * the daemon's traversal of the list.
5594      */
5595
5596     if (IS_LOCK_CLIENTONLY(fileLock)) {
5597         scp->clientLocks--;
5598     } else if (IS_LOCK_ACCEPTED(fileLock)) {
5599         if (fileLock->lockType == LockRead)
5600             scp->sharedLocks--;
5601         else
5602             scp->exclusiveLocks--;
5603     }
5604
5605     fileLock->flags |= CM_FILELOCK_FLAG_DELETED;
5606
5607     if (userp != NULL) {
5608         cm_ReleaseUser(fileLock->userp);
5609     } else {
5610         userp = fileLock->userp;
5611         release_userp = TRUE;
5612     }
5613     cm_ReleaseSCacheNoLock(scp);
5614     fileLock->userp = NULL;
5615     fileLock->scp = NULL;
5616     lock_ReleaseWrite(&cm_scacheLock);
5617
5618     code = cm_IntUnlock(scp, userp, reqp);
5619
5620     if (release_userp) {
5621         cm_ReleaseUser(userp);
5622         release_userp = FALSE;
5623     }
5624
5625     if (!exact_match) {
5626         osi_Log1(afsd_logp, "cm_Unlock not exact match, searching for next lock, code 0x%x", code);
5627         goto try_again;         /* might be more than one lock in the range */
5628     }
5629
5630  done:
5631
5632     osi_Log1(afsd_logp, "cm_Unlock code 0x%x", code);
5633     osi_Log4(afsd_logp, "  leaving scp with excl[%d], shared[%d], client[%d], serverLock[%d]",
5634              scp->exclusiveLocks, scp->sharedLocks, scp->clientLocks,
5635              (int)(signed char) scp->serverLock);
5636
5637     return code;
5638 }
5639
5640 /* called with scp->rw held */
5641 void cm_LockMarkSCacheLost(cm_scache_t * scp)
5642 {
5643     cm_file_lock_t *fileLock;
5644     osi_queue_t *q;
5645
5646     osi_Log1(afsd_logp, "cm_LockMarkSCacheLost scp 0x%x", scp);
5647
5648     /* cm_scacheLock needed because we are modifying fileLock->flags */
5649     lock_ObtainWrite(&cm_scacheLock);
5650
5651     for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5652         fileLock =
5653             (cm_file_lock_t *)((char *) q - offsetof(cm_file_lock_t, fileq));
5654
5655         if (IS_LOCK_ACTIVE(fileLock) &&
5656             !IS_LOCK_CLIENTONLY(fileLock)) {
5657             if (fileLock->lockType == LockRead)
5658                 scp->sharedLocks--;
5659             else
5660                 scp->exclusiveLocks--;
5661
5662             fileLock->flags |= CM_FILELOCK_FLAG_LOST;
5663         }
5664     }
5665
5666     scp->serverLock = -1;
5667     scp->lockDataVersion = CM_SCACHE_VERSION_BAD;
5668     lock_ReleaseWrite(&cm_scacheLock);
5669 }
5670
5671 /* Called with no relevant locks held */
5672 void cm_CheckLocks()
5673 {
5674     osi_queue_t *q, *nq;
5675     cm_file_lock_t *fileLock;
5676     cm_req_t req;
5677     AFSFid tfid;
5678     AFSVolSync volSync;
5679     cm_conn_t *connp;
5680     long code;
5681     struct rx_connection * rxconnp;
5682     cm_scache_t * scp;
5683
5684     memset(&volSync, 0, sizeof(volSync));
5685
5686     cm_InitReq(&req);
5687
5688     lock_ObtainWrite(&cm_scacheLock);
5689
5690     cm_lockRefreshCycle++;
5691
5692     osi_Log1(afsd_logp, "cm_CheckLocks starting lock check cycle %d", cm_lockRefreshCycle);
5693
5694     for (q = cm_allFileLocks; q; q = nq) {
5695         fileLock = (cm_file_lock_t *) q;
5696         nq = osi_QNext(q);
5697         code = -1;
5698
5699         if (IS_LOCK_DELETED(fileLock)) {
5700             cm_user_t *userp = fileLock->userp;
5701             cm_scache_t *scp = fileLock->scp;
5702             fileLock->userp = NULL;
5703             fileLock->scp = NULL;
5704
5705             if (scp && userp) {
5706                 lock_ReleaseWrite(&cm_scacheLock);
5707                 lock_ObtainWrite(&scp->rw);
5708                 code = cm_IntUnlock(scp, userp, &req);
5709                 lock_ReleaseWrite(&scp->rw);
5710
5711                 cm_ReleaseUser(userp);
5712                 lock_ObtainWrite(&cm_scacheLock);
5713                 cm_ReleaseSCacheNoLock(scp);
5714             }
5715             osi_QRemove(&cm_allFileLocks, q);
5716             cm_PutFileLock(fileLock);
5717
5718         } else if (IS_LOCK_ACTIVE(fileLock) && !IS_LOCK_CLIENTONLY(fileLock)) {
5719
5720             /* Server locks must have been enabled for us to have
5721                received an active non-client-only lock. */
5722             osi_assertx(cm_enableServerLocks, "!cm_enableServerLocks");
5723
5724             scp = fileLock->scp;
5725             osi_assertx(scp != NULL, "null cm_scache_t");
5726
5727             cm_HoldSCacheNoLock(scp);
5728
5729 #ifdef DEBUG
5730             if (cm_FidCmp(&fileLock->fid, &fileLock->scp->fid)) {
5731                 osi_Log0(afsd_logp, "!!fileLock->fid != scp->fid");
5732                 osi_Log4(afsd_logp, "  fileLock->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5733                          fileLock->fid.cell,
5734                          fileLock->fid.volume,
5735                          fileLock->fid.vnode,
5736                          fileLock->fid.unique);
5737                 osi_Log4(afsd_logp, "  scp->fid(cell=[%d], volume=[%d], vnode=[%d], unique=[%d]",
5738                          fileLock->scp->fid.cell,
5739                          fileLock->scp->fid.volume,
5740                          fileLock->scp->fid.vnode,
5741                          fileLock->scp->fid.unique);
5742                 osi_assertx(FALSE, "invalid fid");
5743             }
5744 #endif
5745             /* Server locks are extended once per scp per refresh
5746                cycle. */
5747             if (scp->lastRefreshCycle != cm_lockRefreshCycle) {
5748
5749                 int scp_done = FALSE;
5750
5751                 osi_Log1(afsd_logp, "cm_CheckLocks Updating scp 0x%x", scp);
5752
5753                 lock_ReleaseWrite(&cm_scacheLock);
5754                 lock_ObtainWrite(&scp->rw);
5755
5756                 /* did the lock change while we weren't holding the lock? */
5757                 if (!IS_LOCK_ACTIVE(fileLock))
5758                     goto post_syncopdone;
5759
5760                 code = cm_SyncOp(scp, NULL, fileLock->userp, &req, 0,
5761                                  CM_SCACHESYNC_NEEDCALLBACK
5762                                  | CM_SCACHESYNC_GETSTATUS
5763                                  | CM_SCACHESYNC_LOCK);
5764
5765                 if (code) {
5766                     osi_Log1(afsd_logp,
5767                              "cm_CheckLocks SyncOp failure code 0x%x", code);
5768                     goto post_syncopdone;
5769                 }
5770
5771                 /* cm_SyncOp releases scp->rw during which the lock
5772                    may get released. */
5773                 if (!IS_LOCK_ACTIVE(fileLock))
5774                     goto pre_syncopdone;
5775
5776                 if (scp->serverLock != -1 && !(scp->flags & CM_SCACHEFLAG_DELETED)) {
5777                     cm_fid_t cfid;
5778                     cm_user_t * userp;
5779
5780                     tfid.Volume = scp->fid.volume;
5781                     tfid.Vnode = scp->fid.vnode;
5782                     tfid.Unique = scp->fid.unique;
5783                     cfid = scp->fid;
5784                     userp = fileLock->userp;
5785
5786                     osi_Log3(afsd_logp, "CALL ExtendLock lock 0x%p for scp=0x%p with lock %d",
5787                              fileLock,
5788                              scp,
5789                              (int) scp->serverLock);
5790
5791                     lock_ReleaseWrite(&scp->rw);
5792
5793                     do {
5794                         code = cm_ConnFromFID(&cfid, userp,
5795                                        &req, &connp);
5796                         if (code)
5797                             break;
5798
5799                         rxconnp = cm_GetRxConn(connp);
5800                         code = RXAFS_ExtendLock(rxconnp, &tfid,
5801                                                 &volSync);
5802                         rx_PutConnection(rxconnp);
5803
5804                         osi_Log1(afsd_logp, "   ExtendLock returns %d", code);
5805
5806                     } while (cm_Analyze(connp, userp, &req,
5807                                         &cfid, NULL, 1, NULL, &volSync, NULL, NULL,
5808                                         code));
5809
5810                     code = cm_MapRPCError(code, &req);
5811
5812                     lock_ObtainWrite(&scp->rw);
5813
5814                     if (code) {
5815                         osi_Log1(afsd_logp, "CALL ExtendLock FAILURE, code 0x%x", code);
5816                         scp->fsLockCount = 0;
5817                     } else {
5818                         osi_Log0(afsd_logp, "CALL ExtendLock SUCCESS");
5819                         scp->lockDataVersion = scp->dataVersion;
5820                     }
5821
5822                     if ((code == EINVAL || code == CM_ERROR_INVAL) &&
5823                         scp->lockDataVersion == scp->dataVersion) {
5824                         int lockType;
5825
5826                         lockType =
5827                             (scp->exclusiveLocks > 0) ? LockWrite: LockRead;
5828
5829                         /* we might still have a chance to obtain a
5830                            new lock */
5831
5832                         code = cm_IntSetLock(scp, userp, lockType, &req);
5833
5834                         if (code) {
5835                             code = CM_ERROR_INVAL;
5836                         } else if (scp->lockDataVersion != scp->dataVersion) {
5837
5838                             /* now check if we still have the file at
5839                                the right data version. */
5840                             osi_Log1(afsd_logp,
5841                                      "Data version mismatch on scp 0x%p",
5842                                      scp);
5843                             osi_Log2(afsd_logp,
5844                                      "   Data versions: before=%I64d, after=%I64d",
5845                                      scp->lockDataVersion,
5846                                      scp->dataVersion);
5847
5848                             code = cm_IntReleaseLock(scp, userp, &req);
5849
5850                             code = CM_ERROR_INVAL;
5851                         }
5852                     }
5853
5854                     if (code == EINVAL || code == CM_ERROR_INVAL ||
5855                         code == CM_ERROR_BADFD) {
5856                         cm_LockMarkSCacheLost(scp);
5857                     }
5858
5859                 } else {
5860                     /* interestingly, we have found an active lock
5861                        belonging to an scache that has no
5862                        serverLock */
5863                     cm_LockMarkSCacheLost(scp);
5864                 }
5865
5866                 scp_done = TRUE;
5867
5868             pre_syncopdone:
5869
5870                 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
5871
5872             post_syncopdone:
5873                 lock_ReleaseWrite(&scp->rw);
5874
5875                 lock_ObtainWrite(&cm_scacheLock);
5876
5877                 if (code == 0) {
5878                     fileLock->lastUpdate = time(NULL);
5879                 }
5880
5881                 if (scp_done)
5882                     scp->lastRefreshCycle = cm_lockRefreshCycle;
5883
5884             } else {
5885                 /* we have already refreshed the locks on this scp */
5886                 fileLock->lastUpdate = time(NULL);
5887             }
5888
5889             cm_ReleaseSCacheNoLock(scp);
5890
5891         } else if (IS_LOCK_ACTIVE(fileLock) && IS_LOCK_CLIENTONLY(fileLock)) {
5892             /* TODO: Check callbacks */
5893         }
5894     }
5895
5896     lock_ReleaseWrite(&cm_scacheLock);
5897     osi_Log1(afsd_logp, "cm_CheckLocks completes lock check cycle %d", cm_lockRefreshCycle);
5898 }
5899
5900 /* NOT called with scp->rw held. */
5901 long cm_RetryLock(cm_file_lock_t *oldFileLock, int client_is_dead)
5902 {
5903     long code = 0;
5904     cm_scache_t *scp = NULL;
5905     cm_file_lock_t *fileLock;
5906     osi_queue_t *q;
5907     cm_req_t req;
5908     int newLock = -1;
5909     int force_client_lock = FALSE;
5910     int has_insert = FALSE;
5911     int check_data_version = FALSE;
5912
5913     cm_InitReq(&req);
5914
5915     if (client_is_dead) {
5916         code = CM_ERROR_TIMEDOUT;
5917         goto updateLock;
5918     }
5919
5920     lock_ObtainRead(&cm_scacheLock);
5921
5922     osi_Log2(afsd_logp, "cm_RetryLock checking lock %p (scp=%p)", oldFileLock, oldFileLock->scp);
5923     osi_Log4(afsd_logp, "    offset(%x:%x) length(%x:%x)",
5924              (unsigned)(oldFileLock->range.offset >> 32),
5925              (unsigned)(oldFileLock->range.offset & 0xffffffff),
5926              (unsigned)(oldFileLock->range.length >> 32),
5927              (unsigned)(oldFileLock->range.length & 0xffffffff));
5928     osi_Log4(afsd_logp, "    key<0x%x,0x%x,0x%x> flags=%x",
5929              oldFileLock->key.process_id, oldFileLock->key.session_id, oldFileLock->key.file_id,
5930              (unsigned)(oldFileLock->flags));
5931
5932     /* if the lock has already been granted, then we have nothing to do */
5933     if (IS_LOCK_ACTIVE(oldFileLock)) {
5934         lock_ReleaseRead(&cm_scacheLock);
5935         osi_Log0(afsd_logp, "cm_RetryLock lock already granted");
5936         return 0;
5937     }
5938
5939     /* we can't do anything with lost or deleted locks at the moment. */
5940     if (IS_LOCK_LOST(oldFileLock) || IS_LOCK_DELETED(oldFileLock)) {
5941         code = CM_ERROR_BADFD;
5942         osi_Log0(afsd_logp, "cm_RetryLock lock is lost or deleted");
5943         lock_ReleaseRead(&cm_scacheLock);
5944         goto updateLock;
5945     }
5946
5947     scp = oldFileLock->scp;
5948
5949     osi_assertx(scp != NULL, "null cm_scache_t");
5950
5951     lock_ReleaseRead(&cm_scacheLock);
5952     lock_ObtainWrite(&scp->rw);
5953
5954     code = cm_LockCheckPerms(scp, oldFileLock->lockType,
5955                              oldFileLock->userp,
5956                              &req, &has_insert);
5957
5958     if (code == CM_ERROR_NOACCESS && oldFileLock->lockType == LockRead) {
5959         if (!has_insert || !SCP_SUPPORTS_WRITELOCKACL(scp)) {
5960         force_client_lock = TRUE;
5961         }
5962         code = 0;
5963     } else if (code) {
5964         lock_ReleaseWrite(&scp->rw);
5965         return code;
5966     }
5967
5968     lock_ObtainWrite(&cm_scacheLock);
5969
5970     /* Check if we already have a sufficient server lock to allow this
5971        lock to go through. */
5972     if (IS_LOCK_WAITLOCK(oldFileLock) &&
5973         (!SERVERLOCKS_ENABLED(scp) ||
5974          scp->serverLock == oldFileLock->lockType ||
5975          scp->serverLock == LockWrite)) {
5976
5977         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
5978
5979         if (SERVERLOCKS_ENABLED(scp)) {
5980             osi_Log1(afsd_logp, "cm_RetryLock Server lock (%d) is sufficient for lock.  Granting",
5981                      (int) scp->serverLock);
5982         } else {
5983             osi_Log0(afsd_logp, "cm_RetryLock skipping server lock for scp");
5984         }
5985
5986         lock_ReleaseWrite(&cm_scacheLock);
5987         lock_ReleaseWrite(&scp->rw);
5988
5989         return 0;
5990     }
5991
5992     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
5993
5994         /* check if the conflicting locks have dissappeared already */
5995         for (q = scp->fileLocksH; q; q = osi_QNext(q)) {
5996
5997             fileLock = (cm_file_lock_t *)
5998                 ((char *) q - offsetof(cm_file_lock_t, fileq));
5999
6000             if (IS_LOCK_LOST(fileLock)) {
6001                 if (cm_KeyEquals(&fileLock->key, &oldFileLock->key, 0)) {
6002                     code = CM_ERROR_BADFD;
6003                     oldFileLock->flags |= CM_FILELOCK_FLAG_LOST;
6004                     osi_Log1(afsd_logp, "    found lost lock %p for same key.  Marking lock as lost",
6005                              fileLock);
6006                     break;
6007                 } else if (fileLock->lockType == LockWrite &&
6008                            INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
6009                     osi_Log1(afsd_logp, "    found conflicting LOST lock %p", fileLock);
6010                     code = CM_ERROR_WOULDBLOCK;
6011                     break;
6012                 }
6013             }
6014
6015             if (IS_LOCK_ACCEPTED(fileLock) &&
6016                 INTERSECT_RANGE(oldFileLock->range, fileLock->range)) {
6017
6018                 if (oldFileLock->lockType != LockRead ||
6019                    fileLock->lockType != LockRead) {
6020
6021                     osi_Log1(afsd_logp, "    found conflicting lock %p", fileLock);
6022                     code = CM_ERROR_WOULDBLOCK;
6023                     break;
6024                 }
6025             }
6026         }
6027     }
6028
6029     if (code != 0) {
6030         lock_ReleaseWrite(&cm_scacheLock);
6031         lock_ReleaseWrite(&scp->rw);
6032
6033         goto handleCode;
6034     }
6035
6036     /* when we get here, the lock is either a WAITUNLOCK or WAITLOCK.
6037        If it is WAITUNLOCK, then we didn't find any conflicting lock
6038        but we haven't verfied whether the serverLock is sufficient to
6039        assert it.  If it is WAITLOCK, then the serverLock is
6040        insufficient to assert it. Eitherway, we are ready to accept
6041        the lock as either ACTIVE or WAITLOCK depending on the
6042        serverLock. */
6043
6044     /* First, promote the WAITUNLOCK to a WAITLOCK */
6045     if (IS_LOCK_WAITUNLOCK(oldFileLock)) {
6046         if (oldFileLock->lockType == LockRead)
6047             scp->sharedLocks++;
6048         else
6049             scp->exclusiveLocks++;
6050
6051         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITUNLOCK;
6052         oldFileLock->flags |= CM_FILELOCK_FLAG_WAITLOCK;
6053     }
6054
6055     osi_assertx(IS_LOCK_WAITLOCK(oldFileLock), "!IS_LOCK_WAITLOCK");
6056
6057     if (force_client_lock ||
6058         !SERVERLOCKS_ENABLED(scp) ||
6059         scp->serverLock == oldFileLock->lockType ||
6060         (oldFileLock->lockType == LockRead &&
6061          scp->serverLock == LockWrite)) {
6062
6063         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6064
6065         if ((force_client_lock ||
6066              !SERVERLOCKS_ENABLED(scp)) &&
6067             !IS_LOCK_CLIENTONLY(oldFileLock)) {
6068
6069             oldFileLock->flags |= CM_FILELOCK_FLAG_CLIENTONLY;
6070
6071             if (oldFileLock->lockType == LockRead)
6072                 scp->sharedLocks--;
6073             else
6074                 scp->exclusiveLocks--;
6075
6076             scp->clientLocks++;
6077         }
6078
6079         lock_ReleaseWrite(&cm_scacheLock);
6080         lock_ReleaseWrite(&scp->rw);
6081
6082         return 0;
6083
6084     } else {
6085         cm_user_t * userp;
6086
6087         code = cm_SyncOp(scp, NULL, oldFileLock->userp, &req, 0,
6088                          CM_SCACHESYNC_NEEDCALLBACK
6089                          | CM_SCACHESYNC_GETSTATUS
6090                          | CM_SCACHESYNC_LOCK);
6091         if (code) {
6092             osi_Log1(afsd_logp, "cm_RetryLock SyncOp failure code 0x%x", code);
6093             lock_ReleaseWrite(&cm_scacheLock);
6094             goto post_syncopdone;
6095         }
6096
6097         if (!IS_LOCK_WAITLOCK(oldFileLock))
6098             goto pre_syncopdone;
6099
6100         userp = oldFileLock->userp;
6101
6102 #ifndef AGGRESSIVE_LOCKS
6103         newLock = oldFileLock->lockType;
6104 #else
6105         newLock = LockWrite;
6106 #endif
6107
6108         if (has_insert) {
6109             /* if has_insert is non-zero, then:
6110                - the lock a LockRead
6111                - we don't have permission to get a LockRead
6112                - we do have permission to get a LockWrite
6113                - the server supports VICED_CAPABILITY_WRITELOCKACL
6114             */
6115
6116             newLock = LockWrite;
6117         }
6118
6119         lock_ReleaseWrite(&cm_scacheLock);
6120
6121         /* when we get here, either we have a read-lock and want a
6122            write-lock or we don't have any locks and we want some
6123            lock. */
6124
6125         if (scp->serverLock == LockRead) {
6126
6127             osi_assertx(newLock == LockWrite, "!LockWrite");
6128
6129             osi_Log0(afsd_logp, "  Attempting to UPGRADE from LockRead to LockWrite");
6130
6131             scp->lockDataVersion = scp->dataVersion;
6132             check_data_version = TRUE;
6133
6134             code = cm_IntReleaseLock(scp, userp, &req);
6135
6136             if (code)
6137                 goto pre_syncopdone;
6138             else
6139                 scp->serverLock = -1;
6140         }
6141
6142         code = cm_IntSetLock(scp, userp, newLock, &req);
6143
6144         if (code == 0) {
6145             if (scp->dataVersion != scp->lockDataVersion) {
6146                 /* we lost a race.  too bad */
6147
6148                 osi_Log0(afsd_logp,
6149                          "  Data version mismatch while upgrading lock.");
6150                 osi_Log2(afsd_logp,
6151                          "  Data versions before=%I64d, after=%I64d",
6152                          scp->lockDataVersion,
6153                          scp->dataVersion);
6154                 osi_Log1(afsd_logp,
6155                          "  Releasing stale lock for scp 0x%x", scp);
6156
6157                 code = cm_IntReleaseLock(scp, userp, &req);
6158
6159                 scp->serverLock = -1;
6160
6161                 code = CM_ERROR_INVAL;
6162
6163                 cm_LockMarkSCacheLost(scp);
6164             } else {
6165                 scp->serverLock = newLock;
6166             }
6167         }
6168
6169     pre_syncopdone:
6170         cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6171     post_syncopdone:
6172         ;
6173     }
6174
6175   handleCode:
6176     if (code != 0 && code != CM_ERROR_WOULDBLOCK) {
6177         lock_ObtainWrite(&cm_scacheLock);
6178         osi_QRemoveHT(&scp->fileLocksH, &scp->fileLocksT, &oldFileLock->fileq);
6179         lock_ReleaseWrite(&cm_scacheLock);
6180     }
6181     lock_ReleaseWrite(&scp->rw);
6182
6183   updateLock:
6184     lock_ObtainWrite(&cm_scacheLock);
6185     if (code == 0) {
6186         oldFileLock->flags &= ~CM_FILELOCK_FLAG_WAITLOCK;
6187     } else if (code != CM_ERROR_WOULDBLOCK) {
6188         oldFileLock->flags |= CM_FILELOCK_FLAG_DELETED;
6189         cm_ReleaseUser(oldFileLock->userp);
6190         oldFileLock->userp = NULL;
6191         if (oldFileLock->scp) {
6192             cm_ReleaseSCacheNoLock(oldFileLock->scp);
6193             oldFileLock->scp = NULL;
6194         }
6195     }
6196     lock_ReleaseWrite(&cm_scacheLock);
6197
6198     return code;
6199 }
6200
6201 cm_key_t cm_GenerateKey(afs_uint16 session_id, afs_offs_t process_id, afs_uint64 file_id)
6202 {
6203     cm_key_t key;
6204
6205     key.process_id = process_id;
6206     key.session_id = session_id;
6207     key.file_id = file_id;
6208
6209     return key;
6210 }
6211
6212 int cm_KeyEquals(cm_key_t *k1, cm_key_t *k2, int flags)
6213 {
6214     return (k1->session_id == k2->session_id) && (k1->file_id == k2->file_id) &&
6215         ((flags & CM_UNLOCK_FLAG_BY_FID) || (k1->process_id == k2->process_id));
6216 }
6217
6218 void cm_ReleaseAllLocks(void)
6219 {
6220     cm_scache_t *scp;
6221     cm_req_t req;
6222     cm_user_t *userp;
6223     cm_key_t   key;
6224     cm_file_lock_t *fileLock;
6225     unsigned int i;
6226
6227     for (i = 0; i < cm_data.scacheHashTableSize; i++)
6228     {
6229         for ( scp = cm_data.scacheHashTablep[i]; scp; scp = scp->nextp ) {
6230             while (scp->fileLocksH != NULL) {
6231                 lock_ObtainWrite(&scp->rw);
6232                 lock_ObtainWrite(&cm_scacheLock);
6233                 if (!scp->fileLocksH) {
6234                     lock_ReleaseWrite(&cm_scacheLock);
6235                     lock_ReleaseWrite(&scp->rw);
6236                     break;
6237                 }
6238                 fileLock = (cm_file_lock_t *)((char *) scp->fileLocksH - offsetof(cm_file_lock_t, fileq));
6239                 userp = fileLock->userp;
6240                 cm_HoldUser(userp);
6241                 key = fileLock->key;
6242                 cm_HoldSCacheNoLock(scp);
6243                 lock_ReleaseWrite(&cm_scacheLock);
6244                 cm_UnlockByKey(scp, key, 0, userp, &req);
6245                 cm_ReleaseSCache(scp);
6246                 cm_ReleaseUser(userp);
6247                 lock_ReleaseWrite(&scp->rw);
6248             }
6249         }
6250     }
6251 }