2 * Copyright 2000, International Business Machines Corporation and others.
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
13 Institution: The Information Technology Center, Carnegie-Mellon University
17 #include <afsconfig.h>
18 #include <afs/param.h>
23 #ifdef HAVE_SYS_FILE_H
27 #ifdef HAVE_SYS_LOCKF_H
28 #include <sys/lockf.h>
31 #ifdef AFS_PTHREAD_ENV
32 # include <opr/lock.h>
34 # include <opr/lockstub.h>
37 #include <rx/rx_queue.h>
39 #include <afs/afsint.h>
41 #include <afs/errors.h>
44 #include <afs/afssyscalls.h>
46 #include <afs/afsutil.h>
52 #include "volume_inline.h"
53 #include "partition.h"
54 #include "viceinode.h"
56 #include "volinodes.h"
57 #include "vol_prototypes.h"
62 # define AFS_SETLKW F_SETLKW64
63 # define AFS_SETLK F_SETLK64
64 # define afs_st_flock flock64
66 # define AFS_SETLKW F_SETLKW
67 # define AFS_SETLK F_SETLK
68 # define afs_st_flock flock
72 /* Note: the volume creation functions herein leave the destroyMe flag in the
73 volume header ON: this means that the volumes will not be attached by the
74 file server and WILL BE DESTROYED the next time a system salvage is performed */
76 #ifdef FSSYNC_BUILD_CLIENT
78 RemoveInodes(struct afs_inode_info
*stuff
, Device dev
, VolumeId parent
,
84 /* This relies on the fact that IDEC only needs the device and NT only
85 * needs the dev and vid to decrement volume special files.
87 IH_INIT(handle
, dev
, parent
, -1);
88 for (i
= 0; i
< MAXINODETYPE
; i
++) {
89 Inode inode
= *stuff
[i
].inode
;
90 if (VALID_INO(inode
)) {
91 if (stuff
[i
].inodeType
== VI_LINKTABLE
) {
92 IH_DEC(handle
, inode
, parent
);
94 IH_DEC(handle
, inode
, vid
);
102 VCreateVolume(Error
* ec
, char *partname
, VolumeId volumeId
, VolumeId parentId
)
103 { /* Should be the same as volumeId if there is
107 retVal
= VCreateVolume_r(ec
, partname
, volumeId
, parentId
);
113 VCreateVolume_r(Error
* ec
, char *partname
, VolumeId volumeId
, VolumeId parentId
)
114 { /* Should be the same as volumeId if there is
118 char headerName
[VMAXPATHLEN
], volumePath
[VMAXPATHLEN
];
120 struct DiskPartition64
*partition
;
121 struct VolumeDiskHeader diskHeader
;
124 Inode nearInode AFS_UNUSED
= 0;
127 struct VolumeHeader tempHeader
;
128 struct afs_inode_info stuff
[MAXINODETYPE
];
130 # ifdef AFS_DEMAND_ATTACH_FS
132 # endif /* AFS_DEMAND_ATTACH_FS */
134 init_inode_info(&tempHeader
, stuff
);
137 memset(&vol
, 0, sizeof(vol
));
139 vol
.parentId
= parentId
;
140 vol
.copyDate
= time(0); /* The only date which really means when this
141 * @i(instance) of this volume was created.
142 * Creation date does not mean this */
144 /* Initialize handle for error case below. */
147 /* Verify that the parition is valid before writing to it. */
148 if (!(partition
= VGetPartition_r(partname
, 0))) {
149 Log("VCreateVolume: partition %s is not in service.\n", partname
);
153 #if defined(NEARINODE_HINT)
154 nearInodeHash(volumeId
, nearInode
);
155 nearInode
%= partition
->f_files
;
157 VGetVolumePath(ec
, vol
.id
, &part
, &name
);
158 if (*ec
== VNOVOL
|| !strcmp(partition
->name
, part
)) {
159 /* this case is ok */
161 /* return EXDEV if it's a clone or read-only to an alternate partition
162 * otherwise assume it's a move */
163 if (vol
.parentId
!= vol
.id
) {
164 Log("VCreateVolume: volume %" AFS_VOLID_FMT
" for parent %" AFS_VOLID_FMT
165 " found on %s; unable to create volume on %s.\n",
166 afs_printable_VolumeId_lu(vol
.id
),
167 afs_printable_VolumeId_lu(vol
.parentId
), part
, partition
->name
);
174 # ifdef AFS_DEMAND_ATTACH_FS
175 /* volume doesn't exist yet, but we must lock it to try to prevent something
176 * else from reading it when we're e.g. half way through creating it (or
177 * something tries to create the same volume at the same time) */
178 locktype
= VVolLockType(V_VOLUPD
, 1);
179 rc
= VLockVolumeByIdNB(volumeId
, partition
, locktype
);
181 Log("VCreateVolume: vol %lu already locked by someone else\n",
182 afs_printable_uint32_lu(volumeId
));
186 # else /* AFS_DEMAND_ATTACH_FS */
187 VLockPartition_r(partname
);
188 # endif /* !AFS_DEMAND_ATTACH_FS */
190 memset(&tempHeader
, 0, sizeof(tempHeader
));
191 tempHeader
.stamp
.magic
= VOLUMEHEADERMAGIC
;
192 tempHeader
.stamp
.version
= VOLUMEHEADERVERSION
;
193 tempHeader
.id
= vol
.id
;
194 tempHeader
.parent
= vol
.parentId
;
195 vol
.stamp
.magic
= VOLUMEINFOMAGIC
;
196 vol
.stamp
.version
= VOLUMEINFOVERSION
;
197 vol
.destroyMe
= DESTROY_ME
;
198 snprintf(headerName
, sizeof headerName
, VFORMAT
,
199 afs_printable_VolumeId_lu(vol
.id
));
200 snprintf(volumePath
, sizeof volumePath
, "%s" OS_DIRSEP
"%s",
201 VPartitionPath(partition
), headerName
);
202 rc
= stat(volumePath
, &st
);
203 if (rc
== 0 || errno
!= ENOENT
) {
205 Log("VCreateVolume: Header file %s already exists!\n",
209 Log("VCreateVolume: Error %d trying to stat header file %s\n",
215 device
= partition
->device
;
217 for (i
= 0; i
< MAXINODETYPE
; i
++) {
218 struct afs_inode_info
*p
= &stuff
[i
];
223 IH_CREATE(NULL
, device
, VPartitionPath(partition
), nearInode
,
224 (p
->inodeType
== VI_LINKTABLE
) ? vol
.parentId
: vol
.id
,
225 INODESPECIAL
, p
->inodeType
, vol
.parentId
);
226 if (!(VALID_INO(*(p
->inode
)))) {
227 if (errno
== EEXIST
&& (p
->inodeType
== VI_LINKTABLE
)) {
228 /* Increment the reference count instead. */
232 *(p
->inode
) = namei_MakeSpecIno(vol
.parentId
, VI_LINKTABLE
);
233 IH_INIT(lh
, device
, parentId
, *(p
->inode
));
239 code
= IH_INC(lh
, *(p
->inode
), parentId
);
240 FDH_REALLYCLOSE(fdP
);
249 IH_CREATE(NULL
, device
, VPartitionPath(partition
), nearInode
,
250 vol
.id
, INODESPECIAL
, p
->inodeType
, vol
.parentId
);
253 if (!VALID_INO(*(p
->inode
))) {
254 Log("VCreateVolume: Problem creating %s file associated with volume header %s\n", p
->description
, volumePath
);
258 RemoveInodes(stuff
, device
, vol
.parentId
, vol
.id
);
262 VDestroyVolumeDiskHeader(partition
, volumeId
, parentId
);
264 # ifdef AFS_DEMAND_ATTACH_FS
266 VUnlockVolumeById(volumeId
, partition
);
268 # endif /* AFS_DEMAND_ATTACH_FS */
271 IH_INIT(handle
, device
, vol
.parentId
, *(p
->inode
));
272 fdP
= IH_OPEN(handle
);
274 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
275 PrintInode(stmp
, *(p
->inode
)), errno
);
278 if (FDH_PWRITE(fdP
, (char *)&p
->stamp
, sizeof(p
->stamp
), 0) !=
280 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
281 PrintInode(stmp
, *(p
->inode
)), errno
);
282 FDH_REALLYCLOSE(fdP
);
285 FDH_REALLYCLOSE(fdP
);
287 nearInode
= *(p
->inode
);
290 IH_INIT(handle
, device
, vol
.parentId
, tempHeader
.volumeInfo
);
291 fdP
= IH_OPEN(handle
);
293 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
294 PrintInode(stmp
, tempHeader
.volumeInfo
), errno
);
297 if (FDH_PWRITE(fdP
, (char *)&vol
, sizeof(vol
), 0) != sizeof(vol
)) {
298 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
299 PrintInode(stmp
, tempHeader
.volumeInfo
), errno
);
300 FDH_REALLYCLOSE(fdP
);
306 VolumeHeaderToDisk(&diskHeader
, &tempHeader
);
307 rc
= VCreateVolumeDiskHeader(&diskHeader
, partition
);
309 Log("VCreateVolume: Error %d trying to write volume header for "
310 "volume %" AFS_VOLID_FMT
" on partition %s; volume not created\n", rc
,
311 afs_printable_VolumeId_lu(vol
.id
), VPartitionPath(partition
));
318 # ifdef AFS_DEMAND_ATTACH_FS
320 VUnlockVolumeById(volumeId
, partition
);
322 # endif /* AFS_DEMAND_ATTACH_FS */
323 return (VAttachVolumeByName_r(ec
, partname
, headerName
, V_SECRETLY
));
325 #endif /* FSSYNC_BUILD_CLIENT */
329 AssignVolumeName(VolumeDiskData
* vol
, char *name
, char *ext
)
332 AssignVolumeName_r(vol
, name
, ext
);
337 AssignVolumeName_r(VolumeDiskData
* vol
, char *name
, char *ext
)
340 strncpy(vol
->name
, name
, VNAMESIZE
- 1);
341 vol
->name
[VNAMESIZE
- 1] = '\0';
342 dot
= strrchr(vol
->name
, '.');
343 if (dot
&& (strcmp(dot
, ".backup") == 0 || strcmp(dot
, ".readonly") == 0))
346 strncat(vol
->name
, ext
, VNAMESIZE
- 1 - strlen(vol
->name
));
350 CopyVolumeHeader_r(VolumeDiskData
* from
, VolumeDiskData
* to
)
352 /* The id and parentId fields are not copied; these are inviolate--the to volume
353 * is assumed to have already been created. The id's cannot be changed once
354 * creation has taken place, since they are embedded in the various inodes associated
355 * with the volume. The copydate is also inviolate--it always reflects the time
356 * this volume was created (compare with the creation date--the creation date of
357 * a backup volume is the creation date of the original parent, because the backup
358 * is used to backup the parent volume). */
362 parent
= to
->parentId
;
363 copydate
= to
->copyDate
;
364 memcpy(to
, from
, sizeof(*from
));
366 to
->parentId
= parent
;
367 to
->copyDate
= copydate
;
368 to
->destroyMe
= DESTROY_ME
; /* Caller must always clear this!!! */
369 to
->stamp
.magic
= VOLUMEINFOMAGIC
;
370 to
->stamp
.version
= VOLUMEINFOVERSION
;
375 CopyVolumeHeader(VolumeDiskData
* from
, VolumeDiskData
* to
)
380 code
= CopyVolumeHeader_r(from
, to
);
386 ClearVolumeStats(VolumeDiskData
* vol
)
389 ClearVolumeStats_r(vol
);
394 ClearVolumeStats_r(VolumeDiskData
* vol
)
396 memset(vol
->weekUse
, 0, sizeof(vol
->weekUse
));
402 CopyVolumeStats_r(VolumeDiskData
* from
, VolumeDiskData
* to
)
404 memcpy(to
->weekUse
, from
->weekUse
, sizeof(to
->weekUse
));
405 to
->dayUse
= from
->dayUse
;
406 to
->dayUseDate
= from
->dayUseDate
;
407 if (from
->stat_initialized
) {
408 memcpy(to
->stat_reads
, from
->stat_reads
, sizeof(to
->stat_reads
));
409 memcpy(to
->stat_writes
, from
->stat_writes
, sizeof(to
->stat_writes
));
410 memcpy(to
->stat_fileSameAuthor
, from
->stat_fileSameAuthor
,
411 sizeof(to
->stat_fileSameAuthor
));
412 memcpy(to
->stat_fileDiffAuthor
, from
->stat_fileDiffAuthor
,
413 sizeof(to
->stat_fileDiffAuthor
));
414 memcpy(to
->stat_dirSameAuthor
, from
->stat_dirSameAuthor
,
415 sizeof(to
->stat_dirSameAuthor
));
416 memcpy(to
->stat_dirDiffAuthor
, from
->stat_dirDiffAuthor
,
417 sizeof(to
->stat_dirDiffAuthor
));
422 CopyVolumeStats(VolumeDiskData
* from
, VolumeDiskData
* to
)
425 CopyVolumeStats_r(from
, to
);
430 * read an existing volume disk header.
432 * @param[in] volid volume id
433 * @param[in] dp disk partition object
434 * @param[out] hdr volume disk header or NULL
436 * @note if hdr is NULL, this is essentially an existence test for the vol
439 * @return operation status
441 * @retval -1 volume header doesn't exist
442 * @retval EIO failed to read volume header
447 VReadVolumeDiskHeader(VolumeId volid
,
448 struct DiskPartition64
* dp
,
449 VolumeDiskHeader_t
* hdr
)
453 char path
[MAXPATHLEN
];
455 snprintf(path
, sizeof(path
), "%s" OS_DIRSEP VFORMAT
,
456 VPartitionPath(dp
), afs_printable_VolumeId_lu(volid
));
457 fd
= open(path
, O_RDONLY
);
459 Log("VReadVolumeDiskHeader: Couldn't open header for volume %" AFS_VOLID_FMT
" (errno %d).\n",
460 afs_printable_VolumeId_lu(volid
), errno
);
463 } else if (hdr
&& read(fd
, hdr
, sizeof(*hdr
)) != sizeof(*hdr
)) {
464 Log("VReadVolumeDiskHeader: Couldn't read header for volume %" AFS_VOLID_FMT
".\n",
465 afs_printable_VolumeId_lu(volid
));
475 #ifdef FSSYNC_BUILD_CLIENT
477 * write an existing volume disk header.
479 * @param[in] hdr volume disk header
480 * @param[in] dp disk partition object
481 * @param[in] cr assert if O_CREAT | O_EXCL should be passed to open()
483 * @return operation status
485 * @retval -1 volume header doesn't exist
486 * @retval EIO failed to write volume header
491 _VWriteVolumeDiskHeader(VolumeDiskHeader_t
* hdr
,
492 struct DiskPartition64
* dp
,
497 char path
[MAXPATHLEN
];
499 #ifdef AFS_DEMAND_ATTACH_FS
500 /* prevent racing with VGC scanners reading the vol header while we are
502 code
= VPartHeaderLock(dp
, READ_LOCK
);
506 #endif /* AFS_DEMAND_ATTACH_FS */
510 snprintf(path
, sizeof(path
), "%s" OS_DIRSEP VFORMAT
,
511 VPartitionPath(dp
), afs_printable_VolumeId_lu(hdr
->id
));
512 fd
= open(path
, flags
, 0644);
515 Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
516 "error = %d\n", afs_printable_uint32_lu(hdr
->id
), errno
);
517 } else if (write(fd
, hdr
, sizeof(*hdr
)) != sizeof(*hdr
)) {
518 Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
519 "error = %d\n", afs_printable_uint32_lu(hdr
->id
), errno
);
524 if (close(fd
) != 0) {
525 Log("_VWriteVolumeDiskHeader: Error closing header for volume "
526 "%lu, errno %d\n", afs_printable_uint32_lu(hdr
->id
), errno
);
530 #ifdef AFS_DEMAND_ATTACH_FS
531 VPartHeaderUnlock(dp
, READ_LOCK
);
532 #endif /* AFS_DEMAND_ATTACH_FS */
538 * write an existing volume disk header.
540 * @param[in] hdr volume disk header
541 * @param[in] dp disk partition object
543 * @return operation status
545 * @retval ENOENT volume header doesn't exist
546 * @retval EIO failed to write volume header
549 VWriteVolumeDiskHeader(VolumeDiskHeader_t
* hdr
,
550 struct DiskPartition64
* dp
)
554 #ifdef AFS_DEMAND_ATTACH_FS
555 VolumeDiskHeader_t oldhdr
;
556 int delvgc
= 0, addvgc
= 0;
559 /* first, see if anything with the volume IDs have changed; if so, we
560 * need to update the VGC */
562 code
= VReadVolumeDiskHeader(hdr
->id
, dp
, &oldhdr
);
563 if (code
== 0 && (oldhdr
.id
!= hdr
->id
|| oldhdr
.parent
!= hdr
->parent
)) {
564 /* the vol id or parent vol id changed; need to delete the VGC entry
565 * for the old vol id/parent, and add the new one */
570 /* couldn't get the old header info; add the new header info to the
571 * VGC in case it hasn't been added yet */
575 #endif /* AFS_DEMAND_ATTACH_FS */
577 code
= _VWriteVolumeDiskHeader(hdr
, dp
, 0);
582 #ifdef AFS_DEMAND_ATTACH_FS
584 memset(&res
, 0, sizeof(res
));
585 code
= FSYNC_VGCDel(dp
->name
, oldhdr
.parent
, oldhdr
.id
, FSYNC_WHATEVER
, &res
);
587 /* unknown vol id is okay; it just further suggests the old header
588 * data was bogus, which is fine since we're trying to fix it */
589 if (code
&& res
.hdr
.reason
!= FSYNC_UNKNOWN_VOLID
) {
590 Log("VWriteVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) "
591 "failed with code %ld reason %ld\n", dp
->name
,
592 afs_printable_uint32_lu(oldhdr
.parent
),
593 afs_printable_uint32_lu(oldhdr
.id
),
594 afs_printable_int32_ld(code
),
595 afs_printable_int32_ld(res
.hdr
.reason
));
600 memset(&res
, 0, sizeof(res
));
601 code
= FSYNC_VGCAdd(dp
->name
, hdr
->parent
, hdr
->id
, FSYNC_WHATEVER
, &res
);
603 Log("VWriteVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) "
604 "failed with code %ld reason %ld\n", dp
->name
,
605 afs_printable_uint32_lu(hdr
->parent
),
606 afs_printable_uint32_lu(hdr
->id
),
607 afs_printable_int32_ld(code
),
608 afs_printable_int32_ld(res
.hdr
.reason
));
612 #endif /* AFS_DEMAND_ATTACH_FS */
619 * create and write a volume disk header to disk.
621 * @param[in] hdr volume disk header
622 * @param[in] dp disk partition object
624 * @return operation status
626 * @retval EEXIST volume header already exists
627 * @retval EIO failed to write volume header
632 VCreateVolumeDiskHeader(VolumeDiskHeader_t
* hdr
,
633 struct DiskPartition64
* dp
)
636 #ifdef AFS_DEMAND_ATTACH_FS
638 #endif /* AFS_DEMAND_ATTACH_FS */
640 code
= _VWriteVolumeDiskHeader(hdr
, dp
, O_CREAT
| O_EXCL
);
645 #ifdef AFS_DEMAND_ATTACH_FS
646 memset(&res
, 0, sizeof(res
));
647 code
= FSYNC_VGCAdd(dp
->name
, hdr
->parent
, hdr
->id
, FSYNC_WHATEVER
, &res
);
649 Log("VCreateVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) failed "
650 "with code %ld reason %ld\n", dp
->name
,
651 afs_printable_uint32_lu(hdr
->parent
),
652 afs_printable_uint32_lu(hdr
->id
),
653 afs_printable_int32_ld(code
),
654 afs_printable_int32_ld(res
.hdr
.reason
));
656 #endif /* AFS_DEMAND_ATTACH_FS */
664 * destroy a volume disk header.
666 * @param[in] dp disk partition object
667 * @param[in] volid volume id
668 * @param[in] parent parent's volume id, 0 if unknown
670 * @return operation status
673 * @note if parent is 0, the parent volume ID will be looked up from the
676 * @note for non-DAFS, parent is currently ignored
679 VDestroyVolumeDiskHeader(struct DiskPartition64
* dp
,
684 char path
[MAXPATHLEN
];
685 #ifdef AFS_DEMAND_ATTACH_FS
687 #endif /* AFS_DEMAND_ATTACH_FS */
689 snprintf(path
, sizeof(path
), "%s" OS_DIRSEP VFORMAT
,
690 VPartitionPath(dp
), afs_printable_VolumeId_lu(volid
));
693 Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno
);
697 #ifdef AFS_DEMAND_ATTACH_FS
698 memset(&res
, 0, sizeof(res
));
700 FSSYNC_VGQry_response_t q_res
;
702 code
= FSYNC_VGCQuery(dp
->name
, volid
, &q_res
, &res
);
704 Log("VDestroyVolumeDiskHeader: FSYNC_VGCQuery(%s, %lu) failed "
705 "with code %ld, reason %ld\n", dp
->name
,
706 afs_printable_uint32_lu(volid
), afs_printable_int32_ld(code
),
707 afs_printable_int32_ld(res
.hdr
.reason
));
714 code
= FSYNC_VGCDel(dp
->name
, parent
, volid
, FSYNC_WHATEVER
, &res
);
716 Log("VDestroyVolumeDiskHeader: FSYNC_VGCDel(%s, %" AFS_VOLID_FMT
", %" AFS_VOLID_FMT
") failed "
717 "with code %ld reason %ld\n", dp
->name
,
718 afs_printable_VolumeId_lu(parent
),
719 afs_printable_VolumeId_lu(volid
),
720 afs_printable_int32_ld(code
),
721 afs_printable_int32_ld(res
.hdr
.reason
));
723 #endif /* AFS_DEMAND_ATTACH_FS */
728 #endif /* FSSYNC_BUILD_CLIENT */
731 * handle a single vol header as part of VWalkVolumeHeaders.
733 * @param[in] dp disk partition
734 * @param[in] volfunc function to call when a vol header is successfully read
735 * @param[in] name full path name to the .vol header
736 * @param[out] hdr header data read in from the .vol header
737 * @param[in] locked 1 if the partition headers are locked, 0 otherwise
738 * @param[in] rock the rock to pass to volfunc
740 * @return operation status
742 * @retval -1 fatal error, stop scanning
743 * @retval 1 failed to read header
744 * @retval 2 volfunc callback indicated error after header read
747 _VHandleVolumeHeader(struct DiskPartition64
*dp
, VWalkVolFunc volfunc
,
748 const char *name
, struct VolumeDiskHeader
*hdr
,
749 int locked
, void *rock
)
754 if ((fd
= OS_OPEN(name
, O_RDONLY
, 0)) == INVALID_FD
755 || OS_READ(fd
, hdr
, sizeof(*hdr
))
757 || hdr
->stamp
.magic
!= VOLUMEHEADERMAGIC
) {
761 if (fd
!= INVALID_FD
) {
765 #ifdef AFSFS_DEMAND_ATTACH_FS
767 VPartHeaderUnlock(dp
);
769 #endif /* AFS_DEMAND_ATTACH_FS */
771 if (!error
&& volfunc
) {
772 /* the volume header seems fine; call the caller-supplied
773 * 'we-found-a-volume-header' function */
776 #ifdef AFS_DEMAND_ATTACH_FS
780 #endif /* AFS_DEMAND_ATTACH_FS */
782 error
= (*volfunc
) (dp
, name
, hdr
, last
, rock
);
791 #ifdef AFS_DEMAND_ATTACH_FS
792 if (error
&& !locked
) {
794 /* retry reading the volume header under the partition
795 * header lock, just to be safe and ensure we're not
796 * racing something rewriting the vol header */
797 code
= VPartHeaderLock(dp
, WRITE_LOCK
);
799 Log("Error acquiring partition write lock when "
800 "looking at header %s\n", name
);
804 return _VHandleVolumeHeader(dp
, volfunc
, name
, hdr
, 1, rock
);
806 #endif /* AFS_DEMAND_ATTACH_FS */
812 * walk through the list of volume headers on a partition.
814 * This function looks through all of the .vol headers on a partition, reads in
815 * each header, and calls the supplied volfunc function on each one. If the
816 * header cannot be read (or volfunc returns a positive error code), DAFS will
817 * VPartHeaderExLock() and retry. If that fails, or if we are non-DAFS, errfunc
818 * will be called (which typically will unlink the problem volume header).
820 * If volfunc returns a negative error code, walking the partition will stop
821 * and we will return an error immediately.
823 * @param[in] dp partition to walk
824 * @param[in] partpath the path opendir()
825 * @param[in] volfunc the function to call when a header is encountered, or
826 * NULL to just skip over valid headers
827 * @param[in] errfunc the function to call when a problematic header is
828 * encountered, or NULL to just skip over bad headers
829 * @param[in] rock rock for volfunc and errfunc
834 * @return operation status
836 * @retval negative fatal error, walk did not finish
839 VWalkVolumeHeaders(struct DiskPartition64
*dp
, const char *partpath
,
840 VWalkVolFunc volfunc
, VWalkErrFunc errfunc
, void *rock
)
843 struct dirent
*dentry
;
845 struct VolumeDiskHeader diskHeader
;
847 dirp
= opendir(partpath
);
849 Log("VWalkVolumeHeaders: cannot open directory %s\n", partpath
);
854 while ((dentry
= readdir(dirp
)) != NULL
) {
856 p
= strrchr(dentry
->d_name
, '.');
857 if (p
!= NULL
&& strcmp(p
, VHDREXT
) == 0) {
858 char name
[VMAXPATHLEN
];
860 sprintf(name
, "%s" OS_DIRSEP
"%s", partpath
, dentry
->d_name
);
862 code
= _VHandleVolumeHeader(dp
, volfunc
, name
, &diskHeader
, -1, rock
);
864 /* fatal error, stop walking */
867 if (code
&& errfunc
) {
868 /* error with header; call the caller-supplied vol error
871 struct VolumeDiskHeader
*hdr
= &diskHeader
;
873 /* we failed to read the header at all, so don't pass in
877 (*errfunc
) (dp
, name
, hdr
, rock
);
892 * initialize a struct VLockFile.
894 * @param[in] lf struct VLockFile to initialize
895 * @param[in] path Full path to the file to use for locks. The string contents
899 VLockFileInit(struct VLockFile
*lf
, const char *path
)
901 memset(lf
, 0, sizeof(*lf
));
902 lf
->path
= strdup(path
);
904 opr_mutex_init(&lf
->mutex
);
909 _VOpenPath(const char *path
)
913 handle
= CreateFile(path
,
914 GENERIC_READ
| GENERIC_WRITE
,
915 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
918 FILE_ATTRIBUTE_HIDDEN
,
920 if (handle
== INVALID_HANDLE_VALUE
) {
928 _VLockFd(FD_t handle
, afs_uint32 offset
, int locktype
, int nonblock
)
933 if (locktype
== WRITE_LOCK
) {
934 flags
|= LOCKFILE_EXCLUSIVE_LOCK
;
937 flags
|= LOCKFILE_FAIL_IMMEDIATELY
;
940 memset(&lap
, 0, sizeof(lap
));
943 if (!LockFileEx(handle
, flags
, 0, 1, 0, &lap
)) {
944 if (GetLastError() == ERROR_LOCK_VIOLATION
) {
954 _VUnlockFd(FD_t handle
, afs_uint32 offset
)
958 memset(&lap
, 0, sizeof(lap
));
961 UnlockFileEx(handle
, 0, 1, 0, &lap
);
965 _VCloseFd(FD_t handle
)
970 #else /* !AFS_NT40_ENV */
973 * open a file on the local filesystem suitable for locking
975 * @param[in] path abs path of the file to open
977 * @return file descriptor
978 * @retval INVALID_FD failure opening file
981 _VOpenPath(const char *path
)
985 fd
= open(path
, O_RDWR
| O_CREAT
, 0660);
993 * lock an offset in a file descriptor.
995 * @param[in] fd file descriptor to lock
996 * @param[in] offset offset in file to lock
997 * @param[in] locktype READ_LOCK or WRITE_LOCK
998 * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
1000 * @return operation status
1002 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1004 * @retval EIO error acquiring file lock
1007 _VLockFd(FD_t fd
, afs_uint32 offset
, int locktype
, int nonblock
)
1009 int l_type
= F_WRLCK
;
1010 int cmd
= AFS_SETLKW
;
1011 struct afs_st_flock sf
;
1013 opr_Assert(fd
>= 0);
1015 if (locktype
== READ_LOCK
) {
1022 sf
.l_start
= offset
;
1025 sf
.l_whence
= SEEK_SET
;
1027 if (fcntl(fd
, cmd
, &sf
)) {
1028 if (nonblock
&& (errno
== EACCES
|| errno
== EAGAIN
)) {
1029 /* We asked for a nonblocking lock, and it was already locked */
1031 if (fcntl(fd
, F_GETLK
, &sf
) != 0 || sf
.l_pid
== 0) {
1032 Log("_VLockFd: fcntl failed with error %d when trying to "
1033 "query the conflicting lock for fd %d (locktype=%d, "
1034 "offset=%lu)\n", errno
, fd
, locktype
,
1035 afs_printable_uint32_lu(offset
));
1037 Log("_VLockFd: conflicting lock held on fd %d, offset %lu by "
1038 "pid %ld (locktype=%d)\n", fd
,
1039 afs_printable_uint32_lu(offset
), (long int)sf
.l_pid
,
1044 Log("_VLockFd: fcntl failed with error %d when trying to lock "
1045 "fd %d (locktype=%d, offset=%lu)\n", errno
, fd
, locktype
,
1046 afs_printable_uint32_lu(offset
));
1054 * close a file descriptor used for file locking.
1056 * @param[in] fd file descriptor to close
1062 Log("_VCloseFd: error %d closing fd %d\n",
1068 * unlock a file offset in a file descriptor.
1070 * @param[in] fd file descriptor to unlock
1071 * @param[in] offset offset to unlock
1074 _VUnlockFd(FD_t fd
, afs_uint32 offset
)
1076 struct afs_st_flock sf
;
1078 sf
.l_start
= offset
;
1080 sf
.l_type
= F_UNLCK
;
1081 sf
.l_whence
= SEEK_SET
;
1083 if (fcntl(fd
, AFS_SETLK
, &sf
)) {
1084 Log("_VUnlockFd: fcntl failed with error %d when trying to unlock "
1085 "fd %d\n", errno
, fd
);
1088 #endif /* !AFS_NT40_ENV */
1091 * reinitialize a struct VLockFile.
1093 * Use this to close the lock file (unlocking any locks in it), and effectively
1094 * restore lf to the state it was in when it was initialized. This is the same
1095 * as unlocking all of the locks on the file, without having to remember what
1096 * all of the locks were. Do not unlock previously held locks after calling
1099 * @param[in] lf struct VLockFile to reinit
1101 * @pre nobody is waiting for a lock on this lockfile or otherwise using
1102 * this lockfile at all
1105 VLockFileReinit(struct VLockFile
*lf
)
1107 opr_mutex_enter(&lf
->mutex
);
1109 if (lf
->fd
!= INVALID_FD
) {
1111 lf
->fd
= INVALID_FD
;
1116 opr_mutex_exit(&lf
->mutex
);
1120 * lock a file on disk for the process.
1122 * @param[in] lf the struct VLockFile representing the file to lock
1123 * @param[in] offset the offset in the file to lock
1124 * @param[in] locktype READ_LOCK or WRITE_LOCK
1125 * @param[in] nonblock 0 to wait for conflicting locks to clear before
1126 * obtaining the lock; 1 to fail immediately if a
1127 * conflicting lock is held by someone else
1129 * @return operation status
1131 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1133 * @retval EIO error acquiring file lock
1137 * @note do not try to lock/unlock the same offset in the same file from
1138 * different threads; use VGetDiskLock to protect threads from each other in
1139 * addition to other processes
1142 VLockFileLock(struct VLockFile
*lf
, afs_uint32 offset
, int locktype
, int nonblock
)
1146 opr_Assert(locktype
== READ_LOCK
|| locktype
== WRITE_LOCK
);
1148 opr_mutex_enter(&lf
->mutex
);
1150 if (lf
->fd
== INVALID_FD
) {
1151 opr_Assert(lf
->refcount
== 0);
1152 lf
->fd
= _VOpenPath(lf
->path
);
1153 if (lf
->fd
== INVALID_FD
) {
1154 opr_mutex_exit(&lf
->mutex
);
1161 opr_Assert(lf
->refcount
> 0);
1163 opr_mutex_exit(&lf
->mutex
);
1165 code
= _VLockFd(lf
->fd
, offset
, locktype
, nonblock
);
1168 opr_mutex_enter(&lf
->mutex
);
1169 opr_Assert(lf
->refcount
> 0);
1170 if (--lf
->refcount
< 1) {
1172 lf
->fd
= INVALID_FD
;
1174 opr_mutex_exit(&lf
->mutex
);
1181 VLockFileUnlock(struct VLockFile
*lf
, afs_uint32 offset
)
1183 opr_mutex_enter(&lf
->mutex
);
1185 opr_Assert(lf
->fd
!= INVALID_FD
);
1186 opr_Assert(lf
->refcount
> 0);
1188 if (--lf
->refcount
< 1) {
1190 lf
->fd
= INVALID_FD
;
1192 _VUnlockFd(lf
->fd
, offset
);
1195 opr_mutex_exit(&lf
->mutex
);
1198 #ifdef AFS_DEMAND_ATTACH_FS
1201 * initialize a struct VDiskLock.
1203 * @param[in] dl struct VDiskLock to initialize
1204 * @param[in] lf the struct VLockFile to associate with this disk lock
1207 VDiskLockInit(struct VDiskLock
*dl
, struct VLockFile
*lf
, afs_uint32 offset
)
1210 memset(dl
, 0, sizeof(*dl
));
1211 Lock_Init(&dl
->rwlock
);
1212 opr_mutex_init(&dl
->mutex
);
1213 opr_cv_init(&dl
->cv
);
1215 dl
->offset
= offset
;
1219 * acquire a lock on a file on local disk.
1221 * @param[in] dl the VDiskLock structure corresponding to the file on disk
1222 * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if
1223 * you want a write lock
1224 * @param[in] nonblock 0 to wait for conflicting locks to clear before
1225 * obtaining the lock; 1 to fail immediately if a
1226 * conflicting lock is held by someone else
1228 * @return operation status
1230 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1232 * @retval EIO error acquiring file lock
1236 * @note while normal fcntl-y locks on Unix systems generally only work per-
1237 * process, this interface also deals with locks between threads in the
1238 * process in addition to different processes acquiring the lock
1241 VGetDiskLock(struct VDiskLock
*dl
, int locktype
, int nonblock
)
1244 opr_Assert(locktype
== READ_LOCK
|| locktype
== WRITE_LOCK
);
1247 if (locktype
== READ_LOCK
) {
1248 ObtainReadLockNoBlock(&dl
->rwlock
, code
);
1250 ObtainWriteLockNoBlock(&dl
->rwlock
, code
);
1257 } else if (locktype
== READ_LOCK
) {
1258 ObtainReadLock(&dl
->rwlock
);
1260 ObtainWriteLock(&dl
->rwlock
);
1263 opr_mutex_enter(&dl
->mutex
);
1265 if ((dl
->flags
& VDISKLOCK_ACQUIRING
)) {
1266 /* Some other thread is waiting to acquire an fs lock. If nonblock=1,
1267 * we can return immediately, since we know we'll need to wait to
1268 * acquire. Otherwise, wait for the other thread to finish acquiring
1273 while ((dl
->flags
& VDISKLOCK_ACQUIRING
)) {
1274 opr_cv_wait(&dl
->cv
, &dl
->mutex
);
1279 if (code
== 0 && !(dl
->flags
& VDISKLOCK_ACQUIRED
)) {
1280 /* no other thread holds the lock on the actual file; so grab one */
1282 /* first try, don't block on the lock to see if we can get it without
1284 code
= VLockFileLock(dl
->lockfile
, dl
->offset
, locktype
, 1);
1286 if (code
== EBUSY
&& !nonblock
) {
1288 /* mark that we are waiting on the fs lock */
1289 dl
->flags
|= VDISKLOCK_ACQUIRING
;
1291 opr_mutex_exit(&dl
->mutex
);
1292 code
= VLockFileLock(dl
->lockfile
, dl
->offset
, locktype
, nonblock
);
1293 opr_mutex_enter(&dl
->mutex
);
1295 dl
->flags
&= ~VDISKLOCK_ACQUIRING
;
1298 dl
->flags
|= VDISKLOCK_ACQUIRED
;
1301 opr_cv_broadcast(&dl
->cv
);
1306 if (locktype
== READ_LOCK
) {
1307 ReleaseReadLock(&dl
->rwlock
);
1309 ReleaseWriteLock(&dl
->rwlock
);
1312 /* successfully got the lock, so inc the number of unlocks we need
1313 * to do before we can unlock the actual file */
1317 opr_mutex_exit(&dl
->mutex
);
1323 * release a lock on a file on local disk.
1325 * @param[in] dl the struct VDiskLock to release
1326 * @param[in] locktype READ_LOCK if you are unlocking a read lock, or
1327 * WRITE_LOCK if you are unlocking a write lock
1329 * @return operation status
1333 VReleaseDiskLock(struct VDiskLock
*dl
, int locktype
)
1335 opr_Assert(locktype
== READ_LOCK
|| locktype
== WRITE_LOCK
);
1337 opr_mutex_enter(&dl
->mutex
);
1338 opr_Assert(dl
->lockers
> 0);
1340 if (--dl
->lockers
< 1) {
1341 /* no threads are holding this lock anymore, so we can release the
1342 * actual disk lock */
1343 VLockFileUnlock(dl
->lockfile
, dl
->offset
);
1344 dl
->flags
&= ~VDISKLOCK_ACQUIRED
;
1347 opr_mutex_exit(&dl
->mutex
);
1349 if (locktype
== READ_LOCK
) {
1350 ReleaseReadLock(&dl
->rwlock
);
1352 ReleaseWriteLock(&dl
->rwlock
);
1356 #endif /* AFS_DEMAND_ATTACH_FS */