a28c22aa251bcef39697f752c6e0c06e998b8efd
[openafs.git] / src / vol / vutil.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 /*
11         System:         VICE-TWO
12         Module:         vutil.c
13         Institution:    The Information Technology Center, Carnegie-Mellon University
14
15  */
16
17 #include <afsconfig.h>
18 #include <afs/param.h>
19
20
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <errno.h>
24 #ifdef AFS_NT40_ENV
25 #include <time.h>
26 #include <fcntl.h>
27 #else
28 #include <sys/time.h>
29 #include <sys/file.h>
30 #include <unistd.h>
31 #endif
32 #include <sys/stat.h>
33 #ifdef AFS_PTHREAD_ENV
34 #include <assert.h>
35 #else /* AFS_PTHREAD_ENV */
36 #include <afs/assert.h>
37 #endif /* AFS_PTHREAD_ENV */
38
39 #include <rx/xdr.h>
40 #include <afs/afsint.h>
41 #include "nfs.h"
42 #include <afs/errors.h>
43 #include "lock.h"
44 #include "lwp.h"
45 #include <afs/afssyscalls.h>
46 #include "ihandle.h"
47 #include <afs/afsutil.h>
48 #ifdef AFS_NT40_ENV
49 #include "ntops.h"
50 #include <io.h>
51 #endif
52 #include "vnode.h"
53 #include "volume.h"
54 #include "partition.h"
55 #include "viceinode.h"
56
57 #include "volinodes.h"
58 #include "vol_prototypes.h"
59
60 #ifdef  AFS_AIX_ENV
61 #include <sys/lockf.h>
62 #endif
63 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
64 #include <string.h>
65 #else
66 #include <strings.h>
67 #endif
68
69 #ifdef O_LARGEFILE
70 #define afs_open        open64
71 #else /* !O_LARGEFILE */
72 #define afs_open        open
73 #endif /* !O_LARGEFILE */
74
75 /*@printflike@*/ extern void Log(const char *format, ...);
76
77 #define nFILES  (sizeof (stuff)/sizeof(struct stuff))
78
79 /* Note:  the volume creation functions herein leave the destroyMe flag in the
80    volume header ON:  this means that the volumes will not be attached by the
81    file server and WILL BE DESTROYED the next time a system salvage is performed */
82
83 #ifdef FSSYNC_BUILD_CLIENT
84 static void
85 RemoveInodes(Device dev, VolumeId vid)
86 {
87     register int i;
88     IHandle_t *handle;
89
90     /* This relies on the fact that IDEC only needs the device and NT only
91      * needs the dev and vid to decrement volume special files.
92      */
93     IH_INIT(handle, dev, vid, -1);
94     for (i = 0; i < nFILES; i++) {
95         Inode inode = *stuff[i].inode;
96         if (VALID_INO(inode))
97             IH_DEC(handle, inode, vid);
98     }
99     IH_RELEASE(handle);
100 }
101
102 Volume *
103 VCreateVolume(Error * ec, char *partname, VolId volumeId, VolId parentId)
104 {                               /* Should be the same as volumeId if there is
105                                  * no parent */
106     Volume *retVal;
107     VOL_LOCK;
108     retVal = VCreateVolume_r(ec, partname, volumeId, parentId);
109     VOL_UNLOCK;
110     return retVal;
111 }
112
113 Volume *
114 VCreateVolume_r(Error * ec, char *partname, VolId volumeId, VolId parentId)
115 {                               /* Should be the same as volumeId if there is
116                                  * no parent */
117     VolumeDiskData vol;
118     int i, rc;
119     char headerName[VMAXPATHLEN], volumePath[VMAXPATHLEN];
120     Device device;
121     struct DiskPartition64 *partition;
122     struct VolumeDiskHeader diskHeader;
123     IHandle_t *handle;
124     FdHandle_t *fdP;
125     Inode nearInode = 0;
126     char *part, *name;
127     struct stat st;
128
129     *ec = 0;
130     memset(&vol, 0, sizeof(vol));
131     vol.id = volumeId;
132     vol.parentId = parentId;
133     vol.copyDate = time(0);     /* The only date which really means when this
134                                  * @i(instance) of this volume was created.
135                                  * Creation date does not mean this */
136
137     /* Initialize handle for error case below. */
138     handle = NULL;
139
140     /* Verify that the parition is valid before writing to it. */
141     if (!(partition = VGetPartition_r(partname, 0))) {
142         Log("VCreateVolume: partition %s is not in service.\n", partname);
143         *ec = VNOVOL;
144         return NULL;
145     }
146 #if     defined(NEARINODE_HINT)
147     nearInodeHash(volumeId, nearInode);
148     nearInode %= partition->f_files;
149 #endif
150     VGetVolumePath(ec, vol.id, &part, &name);
151     if (*ec == VNOVOL || !strcmp(partition->name, part)) {
152         /* this case is ok */
153     } else {
154         /* return EXDEV if it's a clone to an alternate partition
155          * otherwise assume it's a move */
156         if (vol.parentId != vol.id) {
157             *ec = EXDEV;
158             return NULL;
159         }
160     }
161     *ec = 0;
162     VLockPartition_r(partname);
163     memset(&tempHeader, 0, sizeof(tempHeader));
164     tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
165     tempHeader.stamp.version = VOLUMEHEADERVERSION;
166     tempHeader.id = vol.id;
167     tempHeader.parent = vol.parentId;
168     vol.stamp.magic = VOLUMEINFOMAGIC;
169     vol.stamp.version = VOLUMEINFOVERSION;
170     vol.destroyMe = DESTROY_ME;
171     (void)afs_snprintf(headerName, sizeof headerName, VFORMAT, afs_printable_uint32_lu(vol.id));
172     (void)afs_snprintf(volumePath, sizeof volumePath, "%s/%s",
173                        VPartitionPath(partition), headerName);
174     rc = stat(volumePath, &st);
175     if (rc == 0 || errno != ENOENT) {
176         if (rc == 0) {
177             Log("VCreateVolume: Header file %s already exists!\n",
178                 volumePath);
179             *ec = VVOLEXISTS;
180         } else {
181             Log("VCreateVolume: Error %d trying to stat header file %s\n",
182                 errno, volumePath);
183             *ec = VNOVOL;
184         }
185         return NULL;
186     }
187     device = partition->device;
188
189     for (i = 0; i < nFILES; i++) {
190         register struct stuff *p = &stuff[i];
191         if (p->obsolete)
192             continue;
193 #ifdef AFS_NAMEI_ENV
194         *(p->inode) =
195             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
196                       (p->inodeType == VI_LINKTABLE) ? vol.parentId : vol.id,
197                       INODESPECIAL, p->inodeType, vol.parentId);
198         if (!(VALID_INO(*(p->inode)))) {
199             if (errno == EEXIST) {
200                 /* Increment the reference count instead. */
201                 IHandle_t *lh;
202                 int code;
203
204 #ifdef AFS_NT40_ENV
205                 *(p->inode) = nt_MakeSpecIno(VI_LINKTABLE);
206 #else
207                 *(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
208 #endif
209                 IH_INIT(lh, device, parentId, *(p->inode));
210                 fdP = IH_OPEN(lh);
211                 if (fdP == NULL) {
212                     IH_RELEASE(lh);
213                     goto bad;
214                 }
215                 code = IH_INC(lh, *(p->inode), parentId);
216                 FDH_REALLYCLOSE(fdP);
217                 IH_RELEASE(lh);
218                 if (code < 0)
219                     goto bad;
220                 continue;
221             }
222         }
223 #else
224         *(p->inode) =
225             IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
226                       vol.id, INODESPECIAL, p->inodeType, vol.parentId);
227 #endif
228
229         if (!VALID_INO(*(p->inode))) {
230             Log("VCreateVolume:  Problem creating %s file associated with volume header %s\n", p->description, volumePath);
231           bad:
232             if (handle)
233                 IH_RELEASE(handle);
234             RemoveInodes(device, vol.id);
235             if (!*ec) {
236                 *ec = VNOVOL;
237             }
238             VDestroyVolumeDiskHeader(partition, volumeId, parentId);
239             return NULL;
240         }
241         IH_INIT(handle, device, vol.parentId, *(p->inode));
242         fdP = IH_OPEN(handle);
243         if (fdP == NULL) {
244             Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
245                 PrintInode(NULL, *(p->inode)), errno);
246             goto bad;
247         }
248         if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
249             Log("VCreateVolume:  Problem lseek inode %s (err=%d)\n",
250                 PrintInode(NULL, *(p->inode)), errno);
251             FDH_REALLYCLOSE(fdP);
252             goto bad;
253         }
254         if (FDH_WRITE(fdP, (char *)&p->stamp, sizeof(p->stamp)) !=
255             sizeof(p->stamp)) {
256             Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
257                 PrintInode(NULL, *(p->inode)), errno);
258             FDH_REALLYCLOSE(fdP);
259             goto bad;
260         }
261         FDH_REALLYCLOSE(fdP);
262         IH_RELEASE(handle);
263         nearInode = *(p->inode);
264     }
265
266     IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
267     fdP = IH_OPEN(handle);
268     if (fdP == NULL) {
269         Log("VCreateVolume:  Problem iopen inode %s (err=%d)\n",
270             PrintInode(NULL, tempHeader.volumeInfo), errno);
271         goto bad;
272     }
273     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
274         Log("VCreateVolume:  Problem lseek inode %s (err=%d)\n",
275             PrintInode(NULL, tempHeader.volumeInfo), errno);
276         FDH_REALLYCLOSE(fdP);
277         goto bad;
278     }
279     if (FDH_WRITE(fdP, (char *)&vol, sizeof(vol)) != sizeof(vol)) {
280         Log("VCreateVolume:  Problem writing to  inode %s (err=%d)\n",
281             PrintInode(NULL, tempHeader.volumeInfo), errno);
282         FDH_REALLYCLOSE(fdP);
283         goto bad;
284     }
285     FDH_CLOSE(fdP);
286     IH_RELEASE(handle);
287
288     VolumeHeaderToDisk(&diskHeader, &tempHeader);
289     rc = VCreateVolumeDiskHeader(&diskHeader, partition);
290     if (rc) {
291         Log("VCreateVolume: Error %d trying to write volume header for "
292             "volume %u on partition %s; volume not created\n", rc,
293             vol.id, VPartitionPath(partition));
294         if (rc == EEXIST) {
295             *ec = VVOLEXISTS;
296         }
297         goto bad;
298     }
299
300     return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
301 }
302 #endif /* FSSYNC_BUILD_CLIENT */
303
304
305 void
306 AssignVolumeName(register VolumeDiskData * vol, char *name, char *ext)
307 {
308     VOL_LOCK;
309     AssignVolumeName_r(vol, name, ext);
310     VOL_UNLOCK;
311 }
312
313 void
314 AssignVolumeName_r(register VolumeDiskData * vol, char *name, char *ext)
315 {
316     register char *dot;
317     strncpy(vol->name, name, VNAMESIZE - 1);
318     vol->name[VNAMESIZE - 1] = '\0';
319     dot = strrchr(vol->name, '.');
320     if (dot && (strcmp(dot, ".backup") == 0 || strcmp(dot, ".readonly") == 0))
321         *dot = 0;
322     if (ext)
323         strncat(vol->name, ext, VNAMESIZE - 1 - strlen(vol->name));
324 }
325
326 afs_int32
327 CopyVolumeHeader_r(VolumeDiskData * from, VolumeDiskData * to)
328 {
329     /* The id and parentId fields are not copied; these are inviolate--the to volume
330      * is assumed to have already been created.  The id's cannot be changed once
331      * creation has taken place, since they are embedded in the various inodes associated
332      * with the volume.  The copydate is also inviolate--it always reflects the time
333      * this volume was created (compare with the creation date--the creation date of
334      * a backup volume is the creation date of the original parent, because the backup
335      * is used to backup the parent volume). */
336     Date copydate;
337     VolumeId id, parent;
338     id = to->id;
339     parent = to->parentId;
340     copydate = to->copyDate;
341     memcpy(to, from, sizeof(*from));
342     to->id = id;
343     to->parentId = parent;
344     to->copyDate = copydate;
345     to->destroyMe = DESTROY_ME; /* Caller must always clear this!!! */
346     to->stamp.magic = VOLUMEINFOMAGIC;
347     to->stamp.version = VOLUMEINFOVERSION;
348     return 0;
349 }
350
351 afs_int32
352 CopyVolumeHeader(VolumeDiskData * from, VolumeDiskData * to)
353 {
354     afs_int32 code;
355
356     VOL_LOCK;
357     code = CopyVolumeHeader_r(from, to);
358     VOL_UNLOCK;
359     return (code);
360 }
361
362 void
363 ClearVolumeStats(register VolumeDiskData * vol)
364 {
365     VOL_LOCK;
366     ClearVolumeStats_r(vol);
367     VOL_UNLOCK;
368 }
369
370 void
371 ClearVolumeStats_r(register VolumeDiskData * vol)
372 {
373     memset(vol->weekUse, 0, sizeof(vol->weekUse));
374     vol->dayUse = 0;
375     vol->dayUseDate = 0;
376 }
377
378 /**
379  * read an existing volume disk header.
380  *
381  * @param[in]  volid  volume id
382  * @param[in]  dp     disk partition object
383  * @param[out] hdr    volume disk header
384  *
385  * @return operation status
386  *    @retval 0 success
387  *    @retval -1 volume header doesn't exist
388  *    @retval EIO failed to read volume header
389  *
390  * @internal
391  */
392 afs_int32
393 VReadVolumeDiskHeader(VolumeId volid,
394                       struct DiskPartition64 * dp,
395                       VolumeDiskHeader_t * hdr)
396 {
397     afs_int32 code = 0;
398     int fd;
399     char path[MAXPATHLEN];
400
401     (void)afs_snprintf(path, sizeof(path),
402                        "%s/" VFORMAT,
403                        VPartitionPath(dp), afs_printable_uint32_lu(volid));
404     fd = open(path, O_RDONLY);
405     if (fd < 0) {
406         Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu.\n",
407             afs_printable_uint32_lu(volid));
408         code = -1;
409     } else if (read(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
410         Log("VReadVolumeDiskHeader: Couldn't read header for volume %lu.\n",
411             afs_printable_uint32_lu(volid));
412         code = EIO;
413     }
414
415     if (fd >= 0) {
416         close(fd);
417     }
418     return code;
419 }
420
421 #ifdef FSSYNC_BUILD_CLIENT
422 /**
423  * write an existing volume disk header.
424  *
425  * @param[in] hdr   volume disk header
426  * @param[in] dp    disk partition object
427  * @param[in] cr    assert if O_CREAT | O_EXCL should be passed to open()
428  *
429  * @return operation status
430  *    @retval 0 success
431  *    @retval -1 volume header doesn't exist
432  *    @retval EIO failed to write volume header
433  *
434  * @internal
435  */
436 static afs_int32
437 _VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
438                         struct DiskPartition64 * dp,
439                         int flags)
440 {
441     afs_int32 code = 0;
442     int fd;
443     char path[MAXPATHLEN];
444
445 #ifdef AFS_DEMAND_ATTACH_FS
446     /* prevent racing with VGC scanners reading the vol header while we are
447      * writing it */
448     code = VPartHeaderLock(dp, READ_LOCK);
449     if (code) {
450         return EIO;
451     }
452 #endif /* AFS_DEMAND_ATTACH_FS */
453
454     flags |= O_RDWR;
455
456     (void)afs_snprintf(path, sizeof(path),
457                        "%s/" VFORMAT,
458                        VPartitionPath(dp), afs_printable_uint32_lu(hdr->id));
459     fd = open(path, flags, 0644);
460     if (fd < 0) {
461         code = errno;
462         Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
463             "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
464     } else if (write(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
465         Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
466             "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
467         code = EIO;
468     }
469
470     if (fd >= 0) {
471         if (close(fd) != 0) {
472             Log("_VWriteVolumeDiskHeader: Error closing header for volume "
473                 "%lu, errno %d\n", afs_printable_uint32_lu(hdr->id), errno);
474         }
475     }
476
477 #ifdef AFS_DEMAND_ATTACH_FS
478     VPartHeaderUnlock(dp, READ_LOCK);
479 #endif /* AFS_DEMAND_ATTACH_FS */
480
481     return code;
482 }
483
484 /**
485  * write an existing volume disk header.
486  *
487  * @param[in] hdr   volume disk header
488  * @param[in] dp    disk partition object
489  *
490  * @return operation status
491  *    @retval 0 success
492  *    @retval ENOENT volume header doesn't exist
493  *    @retval EIO failed to write volume header
494  */
495 afs_int32
496 VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
497                        struct DiskPartition64 * dp)
498 {
499     afs_int32 code;
500
501 #ifdef AFS_DEMAND_ATTACH_FS
502     VolumeDiskHeader_t oldhdr;
503     int delvgc = 0, addvgc = 0;
504     SYNC_response res;
505
506     /* first, see if anything with the volume IDs have changed; if so, we
507      * need to update the VGC */
508
509     code = VReadVolumeDiskHeader(hdr->id, dp, &oldhdr);
510     if (code == 0 && (oldhdr.id != hdr->id || oldhdr.parent != hdr->parent)) {
511         /* the vol id or parent vol id changed; need to delete the VGC entry
512          * for the old vol id/parent, and add the new one */
513         delvgc = 1;
514         addvgc = 1;
515
516     } else if (code) {
517         /* couldn't get the old header info; add the new header info to the
518          * VGC in case it hasn't been added yet */
519         addvgc = 1;
520     }
521
522 #endif /* AFS_DEMAND_ATTACH_FS */
523
524     code = _VWriteVolumeDiskHeader(hdr, dp, 0);
525     if (code) {
526         goto done;
527     }
528
529 #ifdef AFS_DEMAND_ATTACH_FS
530     if (delvgc) {
531         memset(&res, 0, sizeof(res));
532         code = FSYNC_VGCDel(dp->name, oldhdr.parent, oldhdr.id, FSYNC_WHATEVER, &res);
533
534         /* unknown vol id is okay; it just further suggests the old header
535          * data was bogus, which is fine since we're trying to fix it */
536         if (code && res.hdr.reason != FSYNC_UNKNOWN_VOLID) {
537             Log("VWriteVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) "
538                 "failed with code %ld reason %ld\n", dp->name,
539                 afs_printable_uint32_lu(oldhdr.parent),
540                 afs_printable_uint32_lu(oldhdr.id),
541                 afs_printable_int32_ld(code),
542                 afs_printable_int32_ld(res.hdr.reason));
543         }
544
545     }
546     if (addvgc) {
547         memset(&res, 0, sizeof(res));
548         code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
549         if (code) {
550             Log("VWriteVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) "
551                 "failed with code %ld reason %ld\n", dp->name,
552                 afs_printable_uint32_lu(hdr->parent),
553                 afs_printable_uint32_lu(hdr->id),
554                 afs_printable_int32_ld(code),
555                 afs_printable_int32_ld(res.hdr.reason));
556         }
557     }
558
559 #endif /* AFS_DEMAND_ATTACH_FS */
560
561  done:
562     return code;
563 }
564
565 /**
566  * create and write a volume disk header to disk.
567  *
568  * @param[in] hdr   volume disk header
569  * @param[in] dp    disk partition object
570  *
571  * @return operation status
572  *    @retval 0 success
573  *    @retval EEXIST volume header already exists
574  *    @retval EIO failed to write volume header
575  *
576  * @internal
577  */
578 afs_int32
579 VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr,
580                         struct DiskPartition64 * dp)
581 {
582     afs_int32 code = 0;
583 #ifdef AFS_DEMAND_ATTACH_FS
584     SYNC_response res;
585 #endif /* AFS_DEMAND_ATTACH_FS */
586
587     code = _VWriteVolumeDiskHeader(hdr, dp, O_CREAT | O_EXCL);
588     if (code) {
589         goto done;
590     }
591
592 #ifdef AFS_DEMAND_ATTACH_FS
593     memset(&res, 0, sizeof(res));
594     code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
595     if (code) {
596         Log("VCreateVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) failed "
597             "with code %ld reason %ld\n", dp->name,
598             afs_printable_uint32_lu(hdr->parent),
599             afs_printable_uint32_lu(hdr->id),
600             afs_printable_int32_ld(code),
601             afs_printable_int32_ld(res.hdr.reason));
602     }
603 #endif /* AFS_DEMAND_ATTACH_FS */
604
605  done:
606     return code;
607 }
608
609
610 /**
611  * destroy a volume disk header.
612  *
613  * @param[in] dp      disk partition object
614  * @param[in] volid   volume id
615  * @param[in] parent  parent's volume id, 0 if unknown
616  *
617  * @return operation status
618  *    @retval 0 success
619  *
620  * @note if parent is 0, the parent volume ID will be looked up from the
621  * fileserver
622  *
623  * @note for non-DAFS, parent is currently ignored
624  */
625 afs_int32
626 VDestroyVolumeDiskHeader(struct DiskPartition64 * dp,
627                          VolumeId volid,
628                          VolumeId parent)
629 {
630     afs_int32 code = 0;
631     char path[MAXPATHLEN];
632 #ifdef AFS_DEMAND_ATTACH_FS
633     SYNC_response res;
634 #endif /* AFS_DEMAND_ATTACH_FS */
635
636     (void)afs_snprintf(path, sizeof(path),
637                        "%s/" VFORMAT,
638                        VPartitionPath(dp), afs_printable_uint32_lu(volid));
639     code = unlink(path);
640     if (code) {
641         Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno);
642         goto done;
643     }
644
645 #ifdef AFS_DEMAND_ATTACH_FS
646     memset(&res, 0, sizeof(res));
647     if (!parent) {
648         FSSYNC_VGQry_response_t q_res;
649
650         code = FSYNC_VGCQuery(dp->name, volid, &q_res, &res);
651         if (code) {
652             Log("VDestroyVolumeDiskHeader: FSYNC_VGCQuery(%s, %lu) failed "
653                 "with code %ld, reason %ld\n", dp->name,
654                 afs_printable_uint32_lu(volid), afs_printable_int32_ld(code),
655                 afs_printable_int32_ld(res.hdr.reason));
656             goto done;
657         }
658
659         parent = q_res.rw;
660
661     }
662     code = FSYNC_VGCDel(dp->name, parent, volid, FSYNC_WHATEVER, &res);
663     if (code) {
664         Log("VDestroyVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) failed "
665             "with code %ld reason %ld\n", dp->name,
666             afs_printable_uint32_lu(parent),
667             afs_printable_uint32_lu(volid),
668             afs_printable_int32_ld(code),
669             afs_printable_int32_ld(res.hdr.reason));
670     }
671 #endif /* AFS_DEMAND_ATTACH_FS */
672
673  done:
674     return code;
675 }
676 #endif /* FSSYNC_BUILD_CLIENT */
677
678 #ifdef AFS_DEMAND_ATTACH_FS
679
680 /**
681  * initialize a struct VLockFile.
682  *
683  * @param[in] lf   struct VLockFile to initialize
684  * @param[in] path Full path to the file to use for locks. The string contents
685  *                 are copied.
686  */
687 void
688 VLockFileInit(struct VLockFile *lf, const char *path)
689 {
690     memset(lf, 0, sizeof(*lf));
691     assert(pthread_mutex_init(&lf->mutex, NULL) == 0);
692     lf->path = strdup(path);
693 }
694
695 # ifdef AFS_NT40_ENV
696 static_inline FD_t
697 _VOpenPath(const char *path)
698 {
699     HANDLE handle;
700
701     handle = CreateFile(path,
702                         GENERIC_READ | GENERIC_WRITE,
703                         FILE_SHARE_READ | FILE_SHARE_WRITE,
704                         NULL,
705                         OPEN_ALWAYS,
706                         FILE_ATTRIBUTE_HIDDEN,
707                         NULL);
708     if (handle == INVALID_HANDLE_VALUE) {
709         return INVALID_FD;
710     }
711
712     return handle;
713 }
714
715 static_inline int
716 _VLockFd(FD_t handle, afs_uint32 offset, int locktype, int nonblock)
717 {
718     DWORD flags = 0;
719     OVERLAPPED lap;
720
721     if (locktype == WRITE_LOCK) {
722         flags |= LOCKFILE_EXCLUSIVE_LOCK;
723     }
724     if (nonblock) {
725         flags |= LOCKFILE_FAIL_IMMEDIATELY;
726     }
727
728     memset(&lap, 0, sizeof(lap));
729     lap.Offset = offset;
730
731     if (!LockFileEx(handle, flags, 0, 1, 0, &lap)) {
732         if (GetLastError() == ERROR_LOCK_VIOLATION) {
733             CloseHandle(handle);
734             return EBUSY;
735         }
736         CloseHandle(handle);
737         return EIO;
738     }
739
740     return 0;
741 }
742
743 static_inline void
744 _VUnlockFd(struct VLockFile *lf, afs_uint32 offset)
745 {
746     OVERLAPPED lap;
747
748     memset(&lap, 0, sizeof(lap));
749     lap.Offset = offset;
750
751     UnlockFileEx(lf->fd, 0, 1, 0, &lap);
752 }
753
754 static_inline void
755 _VCloseFd(struct VLockFile *lf)
756 {
757     CloseHandle(lf->fd);
758 }
759
760 # else /* !AFS_NT40_ENV */
761
762 /**
763  * open a file on the local filesystem suitable for locking
764  *
765  * @param[in] path  abs path of the file to open
766  *
767  * @return file descriptor
768  *  @retval INVALID_FD failure opening file
769  */
770 static_inline int
771 _VOpenPath(const char *path)
772 {
773     int fd;
774
775     fd = open(path, O_RDWR | O_CREAT, 0660);
776     if (fd < 0) {
777         return INVALID_FD;
778     }
779     return fd;
780 }
781
782 /**
783  * lock an offset in a file descriptor.
784  *
785  * @param[in] fd       file descriptor to lock
786  * @param[in] offset   offset in file to lock
787  * @param[in] locktype READ_LOCK or WRITE_LOCK
788  * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
789  *
790  * @return operation status
791  *  @retval 0 success
792  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
793  *                specified
794  *  @retval EIO   error acquiring file lock
795  */
796 static_inline int
797 _VLockFd(int fd, afs_uint32 offset, int locktype, int nonblock)
798 {
799     int l_type = F_WRLCK;
800     int cmd = F_SETLKW;
801     struct flock sf;
802
803     if (locktype == READ_LOCK) {
804         l_type = F_RDLCK;
805     }
806     if (nonblock) {
807         cmd = F_SETLK;
808     }
809
810     sf.l_start = offset;
811     sf.l_len = 1;
812     sf.l_type = l_type;
813     sf.l_whence = SEEK_SET;
814
815     if (fcntl(fd, cmd, &sf)) {
816         if (nonblock && (errno == EACCES || errno == EAGAIN)) {
817             /* We asked for a nonblocking lock, and it was already locked */
818             close(fd);
819             return EBUSY;
820         }
821         Log("_VLockFd: fcntl failed with error %d when trying to lock "
822             "fd %d (locktype=%d)\n", errno, fd, locktype);
823         close(fd);
824         return EIO;
825     }
826
827     return 0;
828 }
829
830 /**
831  * close a file descriptor used for file locking.
832  *
833  * @param[in] fd file descriptor to close
834  */
835 static_inline void
836 _VCloseFd(int fd)
837 {
838     if (close(fd)) {
839         Log("_VCloseFd: error %d closing fd %d\n",
840             errno, fd);
841     }
842 }
843
844 /**
845  * unlock a file offset in a file descriptor.
846  *
847  * @param[in] fd file descriptor to unlock
848  * @param[in] offset offset to unlock
849  */
850 static_inline void
851 _VUnlockFd(int fd, afs_uint32 offset)
852 {
853     struct flock sf;
854
855     sf.l_start = offset;
856     sf.l_len = 1;
857     sf.l_type = F_UNLCK;
858     sf.l_whence = SEEK_SET;
859
860     if (fcntl(fd, F_SETLK, &sf)) {
861         Log("_VUnlockFd: fcntl failed with error %d when trying to unlock "
862             "fd %d\n", errno, fd);
863     }
864 }
865 # endif /* !AFS_NT40_ENV */
866
867 /**
868  * lock a file on disk for the process.
869  *
870  * @param[in] lf       the struct VLockFile representing the file to lock
871  * @param[in] offset   the offset in the file to lock
872  * @param[in] locktype READ_LOCK or WRITE_LOCK
873  * @param[in] nonblock 0 to wait for conflicting locks to clear before
874  *                     obtaining the lock; 1 to fail immediately if a
875  *                     conflicting lock is held by someone else
876  *
877  * @return operation status
878  *  @retval 0 success
879  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
880  *                specified
881  *  @retval EIO   error acquiring file lock
882  *
883  * @note DAFS only
884  *
885  * @note do not try to lock/unlock the same offset in the same file from
886  * different threads; use VGetDiskLock to protect threads from each other in
887  * addition to other processes
888  */
889 int
890 VLockFileLock(struct VLockFile *lf, afs_uint32 offset, int locktype, int nonblock)
891 {
892     int code;
893
894     assert(pthread_mutex_lock(&lf->mutex) == 0);
895
896     if (lf->fd == INVALID_FD) {
897         lf->fd = _VOpenPath(lf->path);
898         if (lf->fd == INVALID_FD) {
899             assert(pthread_mutex_unlock(&lf->mutex) == 0);
900             return EIO;
901         }
902     }
903
904     lf->refcount++;
905
906     assert(pthread_mutex_unlock(&lf->mutex) == 0);
907
908     code = _VLockFd(lf->fd, offset, locktype, nonblock);
909
910     if (code) {
911         assert(pthread_mutex_lock(&lf->mutex) == 0);
912         if (--lf->refcount < 1) {
913             _VCloseFd(lf->fd);
914             lf->fd = INVALID_FD;
915         }
916         assert(pthread_mutex_unlock(&lf->mutex) == 0);
917     }
918
919     return code;
920 }
921
922 void
923 VLockFileUnlock(struct VLockFile *lf, afs_uint32 offset)
924 {
925     assert(pthread_mutex_lock(&lf->mutex) == 0);
926
927     if (--lf->refcount < 1) {
928         _VCloseFd(lf->fd);
929         lf->fd = INVALID_FD;
930     } else {
931         _VUnlockFd(lf->fd, offset);
932     }
933
934     assert(pthread_mutex_unlock(&lf->mutex) == 0);
935 }
936
937 /**
938  * initialize a struct VDiskLock.
939  *
940  * @param[in] dl struct VDiskLock to initialize
941  * @param[in] lf the struct VLockFile to associate with this disk lock
942  */
943 void
944 VDiskLockInit(struct VDiskLock *dl, struct VLockFile *lf, afs_uint32 offset)
945 {
946     assert(lf);
947     memset(dl, 0, sizeof(*dl));
948     Lock_Init(&dl->rwlock);
949     assert(pthread_mutex_init(&dl->mutex, NULL) == 0);
950     assert(pthread_cond_init(&dl->cv, NULL) == 0);
951     dl->lockfile = lf;
952     dl->offset = offset;
953 }
954
955 /**
956  * acquire a lock on a file on local disk.
957  *
958  * @param[in] dl       the VDiskLock structure corresponding to the file on disk
959  * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if
960  *                     you want a write lock
961  * @param[in] nonblock 0 to wait for conflicting locks to clear before
962  *                     obtaining the lock; 1 to fail immediately if a
963  *                     conflicting lock is held by someone else
964  *
965  * @return operation status
966  *  @retval 0 success
967  *  @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
968  *                specified
969  *  @retval EIO   error acquiring file lock
970  *
971  * @note DAFS only
972  *
973  * @note while normal fcntl-y locks on Unix systems generally only work per-
974  * process, this interface also deals with locks between threads in the
975  * process in addition to different processes acquiring the lock
976  */
977 int
978 VGetDiskLock(struct VDiskLock *dl, int locktype, int nonblock)
979 {
980     int code = 0;
981     assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
982
983     if (nonblock) {
984         if (locktype == READ_LOCK) {
985             ObtainReadLockNoBlock(&dl->rwlock, code);
986         } else {
987             ObtainWriteLockNoBlock(&dl->rwlock, code);
988         }
989
990         if (code) {
991             return EBUSY;
992         }
993
994     } else if (locktype == READ_LOCK) {
995         ObtainReadLock(&dl->rwlock);
996     } else {
997         ObtainWriteLock(&dl->rwlock);
998     }
999
1000     assert(pthread_mutex_lock(&dl->mutex) == 0);
1001
1002     if ((dl->flags & VDISKLOCK_ACQUIRING)) {
1003         /* Some other thread is waiting to acquire an fs lock. If nonblock=1,
1004          * we can return immediately, since we know we'll need to wait to
1005          * acquire. Otherwise, wait for the other thread to finish acquiring
1006          * the fs lock */
1007         if (nonblock) {
1008             code = EBUSY;
1009         } else {
1010             while ((dl->flags & VDISKLOCK_ACQUIRING)) {
1011                 assert(pthread_cond_wait(&dl->cv, &dl->mutex) == 0);
1012             }
1013         }
1014     }
1015
1016     if (code == 0 && !(dl->flags & VDISKLOCK_ACQUIRED)) {
1017         /* no other thread holds the lock on the actual file; so grab one */
1018
1019         /* first try, don't block on the lock to see if we can get it without
1020          * waiting */
1021         code = VLockFileLock(dl->lockfile, dl->offset, locktype, 1);
1022
1023         if (code == EBUSY && !nonblock) {
1024
1025             /* mark that we are waiting on the fs lock */
1026             dl->flags |= VDISKLOCK_ACQUIRING;
1027
1028             assert(pthread_mutex_unlock(&dl->mutex) == 0);
1029             code = VLockFileLock(dl->lockfile, dl->offset, locktype, nonblock);
1030             assert(pthread_mutex_lock(&dl->mutex) == 0);
1031
1032             dl->flags &= ~VDISKLOCK_ACQUIRING;
1033
1034             if (code == 0) {
1035                 dl->flags |= VDISKLOCK_ACQUIRED;
1036             }
1037
1038             assert(pthread_cond_broadcast(&dl->cv) == 0);
1039         }
1040     }
1041
1042     if (code) {
1043         if (locktype == READ_LOCK) {
1044             ReleaseReadLock(&dl->rwlock);
1045         } else {
1046             ReleaseWriteLock(&dl->rwlock);
1047         }
1048     } else {
1049         /* successfully got the lock, so inc the number of unlocks we need
1050          * to do before we can unlock the actual file */
1051         ++dl->lockers;
1052     }
1053
1054     assert(pthread_mutex_unlock(&dl->mutex) == 0);
1055
1056     return code;
1057 }
1058
1059 /**
1060  * release a lock on a file on local disk.
1061  *
1062  * @param[in] dl the struct VDiskLock to release
1063  * @param[in] locktype READ_LOCK if you are unlocking a read lock, or
1064  *                     WRITE_LOCK if you are unlocking a write lock
1065  *
1066  * @return operation status
1067  *  @retval 0 success
1068  */
1069 void
1070 VReleaseDiskLock(struct VDiskLock *dl, int locktype)
1071 {
1072     assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1073
1074     assert(pthread_mutex_lock(&dl->mutex) == 0);
1075     assert(dl->lockers > 0);
1076
1077     if (--dl->lockers < 1) {
1078         /* no threads are holding this lock anymore, so we can release the
1079          * actual disk lock */
1080         VLockFileUnlock(dl->lockfile, dl->offset);
1081         dl->flags &= ~VDISKLOCK_ACQUIRED;
1082     }
1083
1084     assert(pthread_mutex_unlock(&dl->mutex) == 0);
1085
1086     if (locktype == READ_LOCK) {
1087         ReleaseReadLock(&dl->rwlock);
1088     } else {
1089         ReleaseWriteLock(&dl->rwlock);
1090     }
1091 }
1092
1093 #endif /* AFS_DEMAND_ATTACH_FS */