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