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