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>
22 #ifdef HAVE_SYS_FILE_H
26 #ifdef HAVE_SYS_LOCKF_H
27 #include <sys/lockf.h>
32 #include <afs/afsint.h>
34 #include <afs/errors.h>
37 #include <afs/afssyscalls.h>
39 #include <afs/afsutil.h>
45 #include "volume_inline.h"
46 #include "partition.h"
47 #include "viceinode.h"
49 #include "volinodes.h"
50 #include "vol_prototypes.h"
55 # define AFS_SETLKW F_SETLKW64
56 # define AFS_SETLK F_SETLK64
57 # define afs_st_flock flock64
59 # define AFS_SETLKW F_SETLKW
60 # define AFS_SETLK F_SETLK
61 # define afs_st_flock flock
65 /* Note: the volume creation functions herein leave the destroyMe flag in the
66 volume header ON: this means that the volumes will not be attached by the
67 file server and WILL BE DESTROYED the next time a system salvage is performed */
69 #ifdef FSSYNC_BUILD_CLIENT
71 RemoveInodes(struct afs_inode_info *stuff, Device dev, VolumeId parent,
77 /* This relies on the fact that IDEC only needs the device and NT only
78 * needs the dev and vid to decrement volume special files.
80 IH_INIT(handle, dev, parent, -1);
81 for (i = 0; i < MAXINODETYPE; i++) {
82 Inode inode = *stuff[i].inode;
83 if (VALID_INO(inode)) {
84 if (stuff[i].inodeType == VI_LINKTABLE) {
85 IH_DEC(handle, inode, parent);
87 IH_DEC(handle, inode, vid);
95 VCreateVolume(Error * ec, char *partname, VolId volumeId, VolId parentId)
96 { /* Should be the same as volumeId if there is
100 retVal = VCreateVolume_r(ec, partname, volumeId, parentId);
106 VCreateVolume_r(Error * ec, char *partname, VolId volumeId, VolId parentId)
107 { /* Should be the same as volumeId if there is
111 char headerName[VMAXPATHLEN], volumePath[VMAXPATHLEN];
113 struct DiskPartition64 *partition;
114 struct VolumeDiskHeader diskHeader;
117 Inode nearInode AFS_UNUSED = 0;
120 struct VolumeHeader tempHeader;
121 struct afs_inode_info stuff[MAXINODETYPE];
123 # ifdef AFS_DEMAND_ATTACH_FS
125 # endif /* AFS_DEMAND_ATTACH_FS */
127 init_inode_info(&tempHeader, stuff);
130 memset(&vol, 0, sizeof(vol));
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 */
137 /* Initialize handle for error case below. */
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);
146 #if defined(NEARINODE_HINT)
147 nearInodeHash(volumeId, nearInode);
148 nearInode %= partition->f_files;
150 VGetVolumePath(ec, vol.id, &part, &name);
151 if (*ec == VNOVOL || !strcmp(partition->name, part)) {
152 /* this case is ok */
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) {
163 # ifdef AFS_DEMAND_ATTACH_FS
164 /* volume doesn't exist yet, but we must lock it to try to prevent something
165 * else from reading it when we're e.g. half way through creating it (or
166 * something tries to create the same volume at the same time) */
167 locktype = VVolLockType(V_VOLUPD, 1);
168 rc = VLockVolumeByIdNB(volumeId, partition, locktype);
170 Log("VCreateVolume: vol %lu already locked by someone else\n",
171 afs_printable_uint32_lu(volumeId));
175 # else /* AFS_DEMAND_ATTACH_FS */
176 VLockPartition_r(partname);
177 # endif /* !AFS_DEMAND_ATTACH_FS */
179 memset(&tempHeader, 0, sizeof(tempHeader));
180 tempHeader.stamp.magic = VOLUMEHEADERMAGIC;
181 tempHeader.stamp.version = VOLUMEHEADERVERSION;
182 tempHeader.id = vol.id;
183 tempHeader.parent = vol.parentId;
184 vol.stamp.magic = VOLUMEINFOMAGIC;
185 vol.stamp.version = VOLUMEINFOVERSION;
186 vol.destroyMe = DESTROY_ME;
187 snprintf(headerName, sizeof headerName, VFORMAT,
188 afs_printable_uint32_lu(vol.id));
189 snprintf(volumePath, sizeof volumePath, "%s" OS_DIRSEP "%s",
190 VPartitionPath(partition), headerName);
191 rc = stat(volumePath, &st);
192 if (rc == 0 || errno != ENOENT) {
194 Log("VCreateVolume: Header file %s already exists!\n",
198 Log("VCreateVolume: Error %d trying to stat header file %s\n",
204 device = partition->device;
206 for (i = 0; i < MAXINODETYPE; i++) {
207 struct afs_inode_info *p = &stuff[i];
212 IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
213 (p->inodeType == VI_LINKTABLE) ? vol.parentId : vol.id,
214 INODESPECIAL, p->inodeType, vol.parentId);
215 if (!(VALID_INO(*(p->inode)))) {
216 if (errno == EEXIST && (p->inodeType == VI_LINKTABLE)) {
217 /* Increment the reference count instead. */
221 *(p->inode) = namei_MakeSpecIno(vol.parentId, VI_LINKTABLE);
222 IH_INIT(lh, device, parentId, *(p->inode));
228 code = IH_INC(lh, *(p->inode), parentId);
229 FDH_REALLYCLOSE(fdP);
238 IH_CREATE(NULL, device, VPartitionPath(partition), nearInode,
239 vol.id, INODESPECIAL, p->inodeType, vol.parentId);
242 if (!VALID_INO(*(p->inode))) {
243 Log("VCreateVolume: Problem creating %s file associated with volume header %s\n", p->description, volumePath);
247 RemoveInodes(stuff, device, vol.parentId, vol.id);
251 VDestroyVolumeDiskHeader(partition, volumeId, parentId);
253 # ifdef AFS_DEMAND_ATTACH_FS
255 VUnlockVolumeById(volumeId, partition);
257 # endif /* AFS_DEMAND_ATTACH_FS */
260 IH_INIT(handle, device, vol.parentId, *(p->inode));
261 fdP = IH_OPEN(handle);
263 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
264 PrintInode(stmp, *(p->inode)), errno);
267 if (FDH_PWRITE(fdP, (char *)&p->stamp, sizeof(p->stamp), 0) !=
269 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
270 PrintInode(stmp, *(p->inode)), errno);
271 FDH_REALLYCLOSE(fdP);
274 FDH_REALLYCLOSE(fdP);
276 nearInode = *(p->inode);
279 IH_INIT(handle, device, vol.parentId, tempHeader.volumeInfo);
280 fdP = IH_OPEN(handle);
282 Log("VCreateVolume: Problem iopen inode %s (err=%d)\n",
283 PrintInode(stmp, tempHeader.volumeInfo), errno);
286 if (FDH_PWRITE(fdP, (char *)&vol, sizeof(vol), 0) != sizeof(vol)) {
287 Log("VCreateVolume: Problem writing to inode %s (err=%d)\n",
288 PrintInode(stmp, tempHeader.volumeInfo), errno);
289 FDH_REALLYCLOSE(fdP);
295 VolumeHeaderToDisk(&diskHeader, &tempHeader);
296 rc = VCreateVolumeDiskHeader(&diskHeader, partition);
298 Log("VCreateVolume: Error %d trying to write volume header for "
299 "volume %u on partition %s; volume not created\n", rc,
300 vol.id, VPartitionPath(partition));
307 # ifdef AFS_DEMAND_ATTACH_FS
309 VUnlockVolumeById(volumeId, partition);
311 # endif /* AFS_DEMAND_ATTACH_FS */
312 return (VAttachVolumeByName_r(ec, partname, headerName, V_SECRETLY));
314 #endif /* FSSYNC_BUILD_CLIENT */
318 AssignVolumeName(VolumeDiskData * vol, char *name, char *ext)
321 AssignVolumeName_r(vol, name, ext);
326 AssignVolumeName_r(VolumeDiskData * vol, char *name, char *ext)
329 strncpy(vol->name, name, VNAMESIZE - 1);
330 vol->name[VNAMESIZE - 1] = '\0';
331 dot = strrchr(vol->name, '.');
332 if (dot && (strcmp(dot, ".backup") == 0 || strcmp(dot, ".readonly") == 0))
335 strncat(vol->name, ext, VNAMESIZE - 1 - strlen(vol->name));
339 CopyVolumeHeader_r(VolumeDiskData * from, VolumeDiskData * to)
341 /* The id and parentId fields are not copied; these are inviolate--the to volume
342 * is assumed to have already been created. The id's cannot be changed once
343 * creation has taken place, since they are embedded in the various inodes associated
344 * with the volume. The copydate is also inviolate--it always reflects the time
345 * this volume was created (compare with the creation date--the creation date of
346 * a backup volume is the creation date of the original parent, because the backup
347 * is used to backup the parent volume). */
351 parent = to->parentId;
352 copydate = to->copyDate;
353 memcpy(to, from, sizeof(*from));
355 to->parentId = parent;
356 to->copyDate = copydate;
357 to->destroyMe = DESTROY_ME; /* Caller must always clear this!!! */
358 to->stamp.magic = VOLUMEINFOMAGIC;
359 to->stamp.version = VOLUMEINFOVERSION;
364 CopyVolumeHeader(VolumeDiskData * from, VolumeDiskData * to)
369 code = CopyVolumeHeader_r(from, to);
375 ClearVolumeStats(VolumeDiskData * vol)
378 ClearVolumeStats_r(vol);
383 ClearVolumeStats_r(VolumeDiskData * vol)
385 memset(vol->weekUse, 0, sizeof(vol->weekUse));
391 CopyVolumeStats_r(VolumeDiskData * from, VolumeDiskData * to)
393 memcpy(to->weekUse, from->weekUse, sizeof(to->weekUse));
394 to->dayUse = from->dayUse;
395 to->dayUseDate = from->dayUseDate;
396 if (from->stat_initialized) {
397 memcpy(to->stat_reads, from->stat_reads, sizeof(to->stat_reads));
398 memcpy(to->stat_writes, from->stat_writes, sizeof(to->stat_writes));
399 memcpy(to->stat_fileSameAuthor, from->stat_fileSameAuthor,
400 sizeof(to->stat_fileSameAuthor));
401 memcpy(to->stat_fileDiffAuthor, from->stat_fileDiffAuthor,
402 sizeof(to->stat_fileDiffAuthor));
403 memcpy(to->stat_dirSameAuthor, from->stat_dirSameAuthor,
404 sizeof(to->stat_dirSameAuthor));
405 memcpy(to->stat_dirDiffAuthor, from->stat_dirDiffAuthor,
406 sizeof(to->stat_dirDiffAuthor));
411 CopyVolumeStats(VolumeDiskData * from, VolumeDiskData * to)
414 CopyVolumeStats_r(from, to);
419 * read an existing volume disk header.
421 * @param[in] volid volume id
422 * @param[in] dp disk partition object
423 * @param[out] hdr volume disk header or NULL
425 * @note if hdr is NULL, this is essentially an existence test for the vol
428 * @return operation status
430 * @retval -1 volume header doesn't exist
431 * @retval EIO failed to read volume header
436 VReadVolumeDiskHeader(VolumeId volid,
437 struct DiskPartition64 * dp,
438 VolumeDiskHeader_t * hdr)
442 char path[MAXPATHLEN];
444 snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
445 VPartitionPath(dp), afs_printable_uint32_lu(volid));
446 fd = open(path, O_RDONLY);
448 Log("VReadVolumeDiskHeader: Couldn't open header for volume %lu (errno %d).\n",
449 afs_printable_uint32_lu(volid), errno);
452 } else if (hdr && read(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
453 Log("VReadVolumeDiskHeader: Couldn't read header for volume %lu.\n",
454 afs_printable_uint32_lu(volid));
464 #ifdef FSSYNC_BUILD_CLIENT
466 * write an existing volume disk header.
468 * @param[in] hdr volume disk header
469 * @param[in] dp disk partition object
470 * @param[in] cr assert if O_CREAT | O_EXCL should be passed to open()
472 * @return operation status
474 * @retval -1 volume header doesn't exist
475 * @retval EIO failed to write volume header
480 _VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
481 struct DiskPartition64 * dp,
486 char path[MAXPATHLEN];
488 #ifdef AFS_DEMAND_ATTACH_FS
489 /* prevent racing with VGC scanners reading the vol header while we are
491 code = VPartHeaderLock(dp, READ_LOCK);
495 #endif /* AFS_DEMAND_ATTACH_FS */
499 snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
500 VPartitionPath(dp), afs_printable_uint32_lu(hdr->id));
501 fd = open(path, flags, 0644);
504 Log("_VWriteVolumeDiskHeader: Couldn't open header for volume %lu, "
505 "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
506 } else if (write(fd, hdr, sizeof(*hdr)) != sizeof(*hdr)) {
507 Log("_VWriteVolumeDiskHeader: Couldn't write header for volume %lu, "
508 "error = %d\n", afs_printable_uint32_lu(hdr->id), errno);
513 if (close(fd) != 0) {
514 Log("_VWriteVolumeDiskHeader: Error closing header for volume "
515 "%lu, errno %d\n", afs_printable_uint32_lu(hdr->id), errno);
519 #ifdef AFS_DEMAND_ATTACH_FS
520 VPartHeaderUnlock(dp, READ_LOCK);
521 #endif /* AFS_DEMAND_ATTACH_FS */
527 * write an existing volume disk header.
529 * @param[in] hdr volume disk header
530 * @param[in] dp disk partition object
532 * @return operation status
534 * @retval ENOENT volume header doesn't exist
535 * @retval EIO failed to write volume header
538 VWriteVolumeDiskHeader(VolumeDiskHeader_t * hdr,
539 struct DiskPartition64 * dp)
543 #ifdef AFS_DEMAND_ATTACH_FS
544 VolumeDiskHeader_t oldhdr;
545 int delvgc = 0, addvgc = 0;
548 /* first, see if anything with the volume IDs have changed; if so, we
549 * need to update the VGC */
551 code = VReadVolumeDiskHeader(hdr->id, dp, &oldhdr);
552 if (code == 0 && (oldhdr.id != hdr->id || oldhdr.parent != hdr->parent)) {
553 /* the vol id or parent vol id changed; need to delete the VGC entry
554 * for the old vol id/parent, and add the new one */
559 /* couldn't get the old header info; add the new header info to the
560 * VGC in case it hasn't been added yet */
564 #endif /* AFS_DEMAND_ATTACH_FS */
566 code = _VWriteVolumeDiskHeader(hdr, dp, 0);
571 #ifdef AFS_DEMAND_ATTACH_FS
573 memset(&res, 0, sizeof(res));
574 code = FSYNC_VGCDel(dp->name, oldhdr.parent, oldhdr.id, FSYNC_WHATEVER, &res);
576 /* unknown vol id is okay; it just further suggests the old header
577 * data was bogus, which is fine since we're trying to fix it */
578 if (code && res.hdr.reason != FSYNC_UNKNOWN_VOLID) {
579 Log("VWriteVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) "
580 "failed with code %ld reason %ld\n", dp->name,
581 afs_printable_uint32_lu(oldhdr.parent),
582 afs_printable_uint32_lu(oldhdr.id),
583 afs_printable_int32_ld(code),
584 afs_printable_int32_ld(res.hdr.reason));
589 memset(&res, 0, sizeof(res));
590 code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
592 Log("VWriteVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) "
593 "failed with code %ld reason %ld\n", dp->name,
594 afs_printable_uint32_lu(hdr->parent),
595 afs_printable_uint32_lu(hdr->id),
596 afs_printable_int32_ld(code),
597 afs_printable_int32_ld(res.hdr.reason));
601 #endif /* AFS_DEMAND_ATTACH_FS */
608 * create and write a volume disk header to disk.
610 * @param[in] hdr volume disk header
611 * @param[in] dp disk partition object
613 * @return operation status
615 * @retval EEXIST volume header already exists
616 * @retval EIO failed to write volume header
621 VCreateVolumeDiskHeader(VolumeDiskHeader_t * hdr,
622 struct DiskPartition64 * dp)
625 #ifdef AFS_DEMAND_ATTACH_FS
627 #endif /* AFS_DEMAND_ATTACH_FS */
629 code = _VWriteVolumeDiskHeader(hdr, dp, O_CREAT | O_EXCL);
634 #ifdef AFS_DEMAND_ATTACH_FS
635 memset(&res, 0, sizeof(res));
636 code = FSYNC_VGCAdd(dp->name, hdr->parent, hdr->id, FSYNC_WHATEVER, &res);
638 Log("VCreateVolumeDiskHeader: FSYNC_VGCAdd(%s, %lu, %lu) failed "
639 "with code %ld reason %ld\n", dp->name,
640 afs_printable_uint32_lu(hdr->parent),
641 afs_printable_uint32_lu(hdr->id),
642 afs_printable_int32_ld(code),
643 afs_printable_int32_ld(res.hdr.reason));
645 #endif /* AFS_DEMAND_ATTACH_FS */
653 * destroy a volume disk header.
655 * @param[in] dp disk partition object
656 * @param[in] volid volume id
657 * @param[in] parent parent's volume id, 0 if unknown
659 * @return operation status
662 * @note if parent is 0, the parent volume ID will be looked up from the
665 * @note for non-DAFS, parent is currently ignored
668 VDestroyVolumeDiskHeader(struct DiskPartition64 * dp,
673 char path[MAXPATHLEN];
674 #ifdef AFS_DEMAND_ATTACH_FS
676 #endif /* AFS_DEMAND_ATTACH_FS */
678 snprintf(path, sizeof(path), "%s" OS_DIRSEP VFORMAT,
679 VPartitionPath(dp), afs_printable_uint32_lu(volid));
682 Log("VDestroyVolumeDiskHeader: Couldn't unlink disk header, error = %d\n", errno);
686 #ifdef AFS_DEMAND_ATTACH_FS
687 memset(&res, 0, sizeof(res));
689 FSSYNC_VGQry_response_t q_res;
691 code = FSYNC_VGCQuery(dp->name, volid, &q_res, &res);
693 Log("VDestroyVolumeDiskHeader: FSYNC_VGCQuery(%s, %lu) failed "
694 "with code %ld, reason %ld\n", dp->name,
695 afs_printable_uint32_lu(volid), afs_printable_int32_ld(code),
696 afs_printable_int32_ld(res.hdr.reason));
703 code = FSYNC_VGCDel(dp->name, parent, volid, FSYNC_WHATEVER, &res);
705 Log("VDestroyVolumeDiskHeader: FSYNC_VGCDel(%s, %lu, %lu) failed "
706 "with code %ld reason %ld\n", dp->name,
707 afs_printable_uint32_lu(parent),
708 afs_printable_uint32_lu(volid),
709 afs_printable_int32_ld(code),
710 afs_printable_int32_ld(res.hdr.reason));
712 #endif /* AFS_DEMAND_ATTACH_FS */
717 #endif /* FSSYNC_BUILD_CLIENT */
720 * handle a single vol header as part of VWalkVolumeHeaders.
722 * @param[in] dp disk partition
723 * @param[in] volfunc function to call when a vol header is successfully read
724 * @param[in] name full path name to the .vol header
725 * @param[out] hdr header data read in from the .vol header
726 * @param[in] locked 1 if the partition headers are locked, 0 otherwise
727 * @param[in] rock the rock to pass to volfunc
729 * @return operation status
731 * @retval -1 fatal error, stop scanning
732 * @retval 1 failed to read header
733 * @retval 2 volfunc callback indicated error after header read
736 _VHandleVolumeHeader(struct DiskPartition64 *dp, VWalkVolFunc volfunc,
737 const char *name, struct VolumeDiskHeader *hdr,
738 int locked, void *rock)
743 if ((fd = OS_OPEN(name, O_RDONLY, 0)) == INVALID_FD
744 || OS_READ(fd, hdr, sizeof(*hdr))
746 || hdr->stamp.magic != VOLUMEHEADERMAGIC) {
750 if (fd != INVALID_FD) {
754 #ifdef AFSFS_DEMAND_ATTACH_FS
756 VPartHeaderUnlock(dp);
758 #endif /* AFS_DEMAND_ATTACH_FS */
760 if (!error && volfunc) {
761 /* the volume header seems fine; call the caller-supplied
762 * 'we-found-a-volume-header' function */
765 #ifdef AFS_DEMAND_ATTACH_FS
769 #endif /* AFS_DEMAND_ATTACH_FS */
771 error = (*volfunc) (dp, name, hdr, last, rock);
780 #ifdef AFS_DEMAND_ATTACH_FS
781 if (error && !locked) {
783 /* retry reading the volume header under the partition
784 * header lock, just to be safe and ensure we're not
785 * racing something rewriting the vol header */
786 code = VPartHeaderLock(dp, WRITE_LOCK);
788 Log("Error acquiring partition write lock when "
789 "looking at header %s\n", name);
793 return _VHandleVolumeHeader(dp, volfunc, name, hdr, 1, rock);
795 #endif /* AFS_DEMAND_ATTACH_FS */
801 * walk through the list of volume headers on a partition.
803 * This function looks through all of the .vol headers on a partition, reads in
804 * each header, and calls the supplied volfunc function on each one. If the
805 * header cannot be read (or volfunc returns a positive error code), DAFS will
806 * VPartHeaderExLock() and retry. If that fails, or if we are non-DAFS, errfunc
807 * will be called (which typically will unlink the problem volume header).
809 * If volfunc returns a negative error code, walking the partition will stop
810 * and we will return an error immediately.
812 * @param[in] dp partition to walk
813 * @param[in] partpath the path opendir()
814 * @param[in] volfunc the function to call when a header is encountered, or
815 * NULL to just skip over valid headers
816 * @param[in] errfunc the function to call when a problematic header is
817 * encountered, or NULL to just skip over bad headers
818 * @param[in] rock rock for volfunc and errfunc
823 * @return operation status
825 * @retval negative fatal error, walk did not finish
828 VWalkVolumeHeaders(struct DiskPartition64 *dp, const char *partpath,
829 VWalkVolFunc volfunc, VWalkErrFunc errfunc, void *rock)
832 struct dirent *dentry;
834 struct VolumeDiskHeader diskHeader;
836 dirp = opendir(partpath);
838 Log("VWalkVolumeHeaders: cannot open directory %s\n", partpath);
843 while ((dentry = readdir(dirp)) != NULL) {
845 p = strrchr(dentry->d_name, '.');
846 if (p != NULL && strcmp(p, VHDREXT) == 0) {
847 char name[VMAXPATHLEN];
849 sprintf(name, "%s" OS_DIRSEP "%s", partpath, dentry->d_name);
851 code = _VHandleVolumeHeader(dp, volfunc, name, &diskHeader, -1, rock);
853 /* fatal error, stop walking */
856 if (code && errfunc) {
857 /* error with header; call the caller-supplied vol error
860 struct VolumeDiskHeader *hdr = &diskHeader;
862 /* we failed to read the header at all, so don't pass in
866 (*errfunc) (dp, name, hdr, rock);
881 * initialize a struct VLockFile.
883 * @param[in] lf struct VLockFile to initialize
884 * @param[in] path Full path to the file to use for locks. The string contents
888 VLockFileInit(struct VLockFile *lf, const char *path)
890 memset(lf, 0, sizeof(*lf));
891 lf->path = strdup(path);
893 MUTEX_INIT(&lf->mutex, "vlockfile", MUTEX_DEFAULT, 0);
898 _VOpenPath(const char *path)
902 handle = CreateFile(path,
903 GENERIC_READ | GENERIC_WRITE,
904 FILE_SHARE_READ | FILE_SHARE_WRITE,
907 FILE_ATTRIBUTE_HIDDEN,
909 if (handle == INVALID_HANDLE_VALUE) {
917 _VLockFd(FD_t handle, afs_uint32 offset, int locktype, int nonblock)
922 if (locktype == WRITE_LOCK) {
923 flags |= LOCKFILE_EXCLUSIVE_LOCK;
926 flags |= LOCKFILE_FAIL_IMMEDIATELY;
929 memset(&lap, 0, sizeof(lap));
932 if (!LockFileEx(handle, flags, 0, 1, 0, &lap)) {
933 if (GetLastError() == ERROR_LOCK_VIOLATION) {
943 _VUnlockFd(FD_t handle, afs_uint32 offset)
947 memset(&lap, 0, sizeof(lap));
950 UnlockFileEx(handle, 0, 1, 0, &lap);
954 _VCloseFd(FD_t handle)
959 #else /* !AFS_NT40_ENV */
962 * open a file on the local filesystem suitable for locking
964 * @param[in] path abs path of the file to open
966 * @return file descriptor
967 * @retval INVALID_FD failure opening file
970 _VOpenPath(const char *path)
974 fd = open(path, O_RDWR | O_CREAT, 0660);
982 * lock an offset in a file descriptor.
984 * @param[in] fd file descriptor to lock
985 * @param[in] offset offset in file to lock
986 * @param[in] locktype READ_LOCK or WRITE_LOCK
987 * @param[in] nonblock 1 to fail immediately, 0 to wait to acquire lock
989 * @return operation status
991 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
993 * @retval EIO error acquiring file lock
996 _VLockFd(FD_t fd, afs_uint32 offset, int locktype, int nonblock)
998 int l_type = F_WRLCK;
999 int cmd = AFS_SETLKW;
1000 struct afs_st_flock sf;
1002 if (locktype == READ_LOCK) {
1009 sf.l_start = offset;
1012 sf.l_whence = SEEK_SET;
1014 if (fcntl(fd, cmd, &sf)) {
1015 if (nonblock && (errno == EACCES || errno == EAGAIN)) {
1016 /* We asked for a nonblocking lock, and it was already locked */
1018 if (fcntl(fd, F_GETLK, &sf) != 0 || sf.l_pid == 0) {
1019 Log("_VLockFd: fcntl failed with error %d when trying to "
1020 "query the conflicting lock for fd %d (locktype=%d, "
1021 "offset=%lu)\n", errno, fd, locktype,
1022 afs_printable_uint32_lu(offset));
1024 Log("_VLockFd: conflicting lock held on fd %d, offset %lu by "
1025 "pid %ld (locktype=%d)\n", fd,
1026 afs_printable_uint32_lu(offset), (long int)sf.l_pid,
1031 Log("_VLockFd: fcntl failed with error %d when trying to lock "
1032 "fd %d (locktype=%d, offset=%lu)\n", errno, fd, locktype,
1033 afs_printable_uint32_lu(offset));
1041 * close a file descriptor used for file locking.
1043 * @param[in] fd file descriptor to close
1049 Log("_VCloseFd: error %d closing fd %d\n",
1055 * unlock a file offset in a file descriptor.
1057 * @param[in] fd file descriptor to unlock
1058 * @param[in] offset offset to unlock
1061 _VUnlockFd(FD_t fd, afs_uint32 offset)
1063 struct afs_st_flock sf;
1065 sf.l_start = offset;
1067 sf.l_type = F_UNLCK;
1068 sf.l_whence = SEEK_SET;
1070 if (fcntl(fd, AFS_SETLK, &sf)) {
1071 Log("_VUnlockFd: fcntl failed with error %d when trying to unlock "
1072 "fd %d\n", errno, fd);
1075 #endif /* !AFS_NT40_ENV */
1078 * reinitialize a struct VLockFile.
1080 * Use this to close the lock file (unlocking any locks in it), and effectively
1081 * restore lf to the state it was in when it was initialized. This is the same
1082 * as unlocking all of the locks on the file, without having to remember what
1083 * all of the locks were. Do not unlock previously held locks after calling
1086 * @param[in] lf struct VLockFile to reinit
1088 * @pre nobody is waiting for a lock on this lockfile or otherwise using
1089 * this lockfile at all
1092 VLockFileReinit(struct VLockFile *lf)
1094 MUTEX_ENTER(&lf->mutex);
1096 if (lf->fd != INVALID_FD) {
1098 lf->fd = INVALID_FD;
1103 MUTEX_EXIT(&lf->mutex);
1107 * lock a file on disk for the process.
1109 * @param[in] lf the struct VLockFile representing the file to lock
1110 * @param[in] offset the offset in the file to lock
1111 * @param[in] locktype READ_LOCK or WRITE_LOCK
1112 * @param[in] nonblock 0 to wait for conflicting locks to clear before
1113 * obtaining the lock; 1 to fail immediately if a
1114 * conflicting lock is held by someone else
1116 * @return operation status
1118 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1120 * @retval EIO error acquiring file lock
1124 * @note do not try to lock/unlock the same offset in the same file from
1125 * different threads; use VGetDiskLock to protect threads from each other in
1126 * addition to other processes
1129 VLockFileLock(struct VLockFile *lf, afs_uint32 offset, int locktype, int nonblock)
1133 opr_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1135 MUTEX_ENTER(&lf->mutex);
1137 if (lf->fd == INVALID_FD) {
1138 lf->fd = _VOpenPath(lf->path);
1139 if (lf->fd == INVALID_FD) {
1140 MUTEX_EXIT(&lf->mutex);
1147 MUTEX_EXIT(&lf->mutex);
1149 code = _VLockFd(lf->fd, offset, locktype, nonblock);
1152 MUTEX_ENTER(&lf->mutex);
1153 if (--lf->refcount < 1) {
1155 lf->fd = INVALID_FD;
1157 MUTEX_EXIT(&lf->mutex);
1164 VLockFileUnlock(struct VLockFile *lf, afs_uint32 offset)
1166 MUTEX_ENTER(&lf->mutex);
1168 opr_Assert(lf->fd != INVALID_FD);
1170 if (--lf->refcount < 1) {
1172 lf->fd = INVALID_FD;
1174 _VUnlockFd(lf->fd, offset);
1177 MUTEX_EXIT(&lf->mutex);
1180 #ifdef AFS_DEMAND_ATTACH_FS
1183 * initialize a struct VDiskLock.
1185 * @param[in] dl struct VDiskLock to initialize
1186 * @param[in] lf the struct VLockFile to associate with this disk lock
1189 VDiskLockInit(struct VDiskLock *dl, struct VLockFile *lf, afs_uint32 offset)
1192 memset(dl, 0, sizeof(*dl));
1193 Lock_Init(&dl->rwlock);
1194 MUTEX_INIT(&dl->mutex, "disklock", MUTEX_DEFAULT, 0);
1195 CV_INIT(&dl->cv, "disklock cv", CV_DEFAULT, 0);
1197 dl->offset = offset;
1201 * acquire a lock on a file on local disk.
1203 * @param[in] dl the VDiskLock structure corresponding to the file on disk
1204 * @param[in] locktype READ_LOCK if you want a read lock, or WRITE_LOCK if
1205 * you want a write lock
1206 * @param[in] nonblock 0 to wait for conflicting locks to clear before
1207 * obtaining the lock; 1 to fail immediately if a
1208 * conflicting lock is held by someone else
1210 * @return operation status
1212 * @retval EBUSY someone else is holding a conflicting lock and nonblock=1 was
1214 * @retval EIO error acquiring file lock
1218 * @note while normal fcntl-y locks on Unix systems generally only work per-
1219 * process, this interface also deals with locks between threads in the
1220 * process in addition to different processes acquiring the lock
1223 VGetDiskLock(struct VDiskLock *dl, int locktype, int nonblock)
1226 opr_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1229 if (locktype == READ_LOCK) {
1230 ObtainReadLockNoBlock(&dl->rwlock, code);
1232 ObtainWriteLockNoBlock(&dl->rwlock, code);
1239 } else if (locktype == READ_LOCK) {
1240 ObtainReadLock(&dl->rwlock);
1242 ObtainWriteLock(&dl->rwlock);
1245 MUTEX_ENTER(&dl->mutex);
1247 if ((dl->flags & VDISKLOCK_ACQUIRING)) {
1248 /* Some other thread is waiting to acquire an fs lock. If nonblock=1,
1249 * we can return immediately, since we know we'll need to wait to
1250 * acquire. Otherwise, wait for the other thread to finish acquiring
1255 while ((dl->flags & VDISKLOCK_ACQUIRING)) {
1256 CV_WAIT(&dl->cv, &dl->mutex);
1261 if (code == 0 && !(dl->flags & VDISKLOCK_ACQUIRED)) {
1262 /* no other thread holds the lock on the actual file; so grab one */
1264 /* first try, don't block on the lock to see if we can get it without
1266 code = VLockFileLock(dl->lockfile, dl->offset, locktype, 1);
1268 if (code == EBUSY && !nonblock) {
1270 /* mark that we are waiting on the fs lock */
1271 dl->flags |= VDISKLOCK_ACQUIRING;
1273 MUTEX_EXIT(&dl->mutex);
1274 code = VLockFileLock(dl->lockfile, dl->offset, locktype, nonblock);
1275 MUTEX_ENTER(&dl->mutex);
1277 dl->flags &= ~VDISKLOCK_ACQUIRING;
1280 dl->flags |= VDISKLOCK_ACQUIRED;
1283 CV_BROADCAST(&dl->cv);
1288 if (locktype == READ_LOCK) {
1289 ReleaseReadLock(&dl->rwlock);
1291 ReleaseWriteLock(&dl->rwlock);
1294 /* successfully got the lock, so inc the number of unlocks we need
1295 * to do before we can unlock the actual file */
1299 MUTEX_EXIT(&dl->mutex);
1305 * release a lock on a file on local disk.
1307 * @param[in] dl the struct VDiskLock to release
1308 * @param[in] locktype READ_LOCK if you are unlocking a read lock, or
1309 * WRITE_LOCK if you are unlocking a write lock
1311 * @return operation status
1315 VReleaseDiskLock(struct VDiskLock *dl, int locktype)
1317 opr_Assert(locktype == READ_LOCK || locktype == WRITE_LOCK);
1319 MUTEX_ENTER(&dl->mutex);
1320 opr_Assert(dl->lockers > 0);
1322 if (--dl->lockers < 1) {
1323 /* no threads are holding this lock anymore, so we can release the
1324 * actual disk lock */
1325 VLockFileUnlock(dl->lockfile, dl->offset);
1326 dl->flags &= ~VDISKLOCK_ACQUIRED;
1329 MUTEX_EXIT(&dl->mutex);
1331 if (locktype == READ_LOCK) {
1332 ReleaseReadLock(&dl->rwlock);
1334 ReleaseWriteLock(&dl->rwlock);
1338 #endif /* AFS_DEMAND_ATTACH_FS */