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