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