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