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