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