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