2 * Copyright (c) 2000-2018 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains code to manipulate the volume status DB (/var/db/volinfo.database).
29 18-Apr-2000 Pat Dirks New Today.
34 /* ************************************** I N C L U D E S ***************************************** */
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
40 #include <sys/sysctl.h>
42 #include <sys/ucred.h>
43 #include <sys/resource.h>
44 #include <sys/vmmeter.h>
56 #include <uuid/uuid.h>
57 #include <System/uuid/namespace.h>
59 // This flags array is shared with the mount(8) tool.
60 #include "../mount_flags_dir/mount_flags.h"
62 //from mount_flags_dir/mount_flags.c
63 extern mountopt_t optnames
[];
67 * CommonCrypto is meant to be a more stable API than OpenSSL.
68 * Defining COMMON_DIGEST_FOR_OPENSSL gives API-compatibility
69 * with OpenSSL, so we don't have to change the code.
71 #define COMMON_DIGEST_FOR_OPENSSL
72 #include <CommonCrypto/CommonDigest.h>
73 #include <libkern/OSByteOrder.h>
75 static char usage
[] = "Usage: %s [-a path] | [-c path ] [-d path] [-i]\n";
77 static char gHFSTypeName
[] = "hfs";
78 static char gAPFSTypeName
[] = "apfs";
80 /*****************************************************************************
82 * The following should really be in some system header file:
84 *****************************************************************************/
86 typedef struct VolumeUUID
{
90 #define VOLUMEUUID64LENGTH 16
92 #define VOLUME_USEPERMISSIONS 0x00000001
93 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
95 typedef void *VolumeStatusDBHandle
;
97 void ConvertVolumeUUIDString64ToUUID(const char *UUIDString64
, VolumeUUID
*volumeID
);
99 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
100 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
101 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t
*VolumeStatus
);
102 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t VolumeStatus
);
103 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
104 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
106 /*****************************************************************************
108 * Internal function prototypes:
110 *****************************************************************************/
112 static void check_uid(void);
113 static int GetVolumeUUID(const char *path
, VolumeUUID
*volumeUUIDPtr
);
114 static int AdoptAllLocalVolumes(void);
115 static int AdoptVolume(const char *path
);
116 static int DisownVolume(const char *path
);
117 static int ClearVolumeUUID(const char *path
);
118 static int DisplayVolumeStatus(const char *path
);
119 static int UpdateMountStatus(const char *path
, u_int32_t volstatus
);
122 static int isVolumeHFS(const char*path
);
124 int main (int argc
, const char *argv
[])
131 fprintf(stderr
, usage
, argv
[0]);
135 for (arg
= 1; arg
< argc
; ++arg
) {
136 if ((argv
[arg
][0] == '-') &&
137 ((option
= argv
[arg
][1]) != (char)0) &&
138 (argv
[arg
][2] == (char)0)) {
142 /* Pick out the pathname argument: */
144 fprintf(stderr
, usage
, argv
[0]);
149 result
= AdoptVolume(argv
[arg
]);
154 /* Pick out the pathname argument: */
156 fprintf(stderr
, usage
, argv
[0]);
160 result
= DisplayVolumeStatus(argv
[arg
]);
165 /* Pick out the pathname argument: */
167 fprintf(stderr
, usage
, argv
[0]);
172 result
= DisownVolume(argv
[arg
]);
177 printf(usage
, argv
[0]);
179 printf("\t-a adopts (activates) on-disk permissions on the specified path,\n");
180 printf("\t-c checks the status of the permissions usage on the specified path\n");
181 printf("\t-d disowns (deactivates) the on-disk permissions on the specified path\n");
182 printf("\t-i initializes the permissions database to include all mounted HFS/HFS+ volumes\n");
188 result
= AdoptAllLocalVolumes();
193 /* Pick out the pathname argument: */
195 fprintf(stderr
, usage
, argv
[0]);
200 result
= ClearVolumeUUID(argv
[arg
]);
204 fprintf(stderr
, usage
, argv
[0]);
210 if (result
< 0) result
= 1; // Make sure only positive exit status codes are generated
212 exit(result
); // ensure the process exit status is returned
213 return result
; // ...and make main fit the ANSI spec.
218 static void check_uid(void) {
219 if (geteuid() != 0) {
220 fprintf(stderr
, "###\n");
221 fprintf(stderr
, "### You must be root to perform this operation.\n");
222 fprintf(stderr
, "###\n");
232 -- Returns: error code (0 if successful).
235 UpdateMountStatus(const char *path
, u_int32_t volstatus
) {
236 struct statfs mntstat
;
242 * selectors to determine whether or not certain features
243 * should be re-enabled via mount -u
246 #define MAXMOUNTLEN 255
249 char mountline
[MAXMOUNTLEN
];
250 char mountstring
[MAXMOUNTLEN
];
252 mountopt_t
* opt
= NULL
;
254 uint64_t flags_mask
= (MNT_NOSUID
| MNT_NODEV
|
255 MNT_NOEXEC
| MNT_RDONLY
|
256 MNT_CPROTECT
| MNT_QUARANTINE
|
257 MNT_UNION
| MNT_DONTBROWSE
);
259 result
= statfs(path
, &mntstat
);
261 warn("couldn't look up mount status for '%s'", path
);
265 bzero (mountline
, MAXMOUNTLEN
);
266 bzero (mountstring
, MAXMOUNTLEN
);
268 /* first, check for permissions */
269 if (volstatus
& VOLUME_USEPERMISSIONS
) {
270 strlcpy(mountline
, "perm", MAXMOUNTLEN
);
273 strlcpy(mountline
, "noperm", MAXMOUNTLEN
);
276 /* check the flags */
277 flags
= (mntstat
.f_flags
& flags_mask
);
280 * now iterate over all of the strings in the optname array and
281 * add them into the "mount" string if the flag they represent is set.
282 * The optnames array is extern'd (at the top of this file), and is defined
283 * in a .c file within the mount_flags directory
285 for (opt
= optnames
; flags
&& opt
->o_opt
; opt
++) {
286 if ((flags
& opt
->o_opt
) && opt
->o_cmd
) {
287 snprintf(mountstring
, sizeof(mountstring
), ",%s", opt
->o_cmd
);
288 result
= strlcat(mountline
, mountstring
, MAXMOUNTLEN
);
289 if (result
>= MAXMOUNTLEN
) {
290 // bail out, string is too long.
293 flags
&= ~opt
->o_opt
;
294 bzero (mountstring
, MAXMOUNTLEN
);
304 result
= execl("/sbin/mount", "mount",
305 "-t", mntstat
.f_fstypename
,
306 "-u","-o", mountline
,
307 mntstat
.f_mntfromname
, mntstat
.f_mntonname
, NULL
);
308 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
313 warn("couldn't fork to execute mount command");
318 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
))) {
319 result
= status
.w_retcode
;
330 -- AdoptAllLocalVolumes
332 -- Returns: error code (0 if successful).
335 AdoptAllLocalVolumes(void) {
336 struct statfs
*mntstatptr
;
339 fscount
= getmntinfo(&mntstatptr
, MNT_WAIT
);
341 warn("couldn't get information on mounted volumes");
345 while (fscount
> 0) {
346 if ((strncmp(mntstatptr
->f_fstypename
, gHFSTypeName
, MFSNAMELEN
) == 0) ||
347 (strncmp(mntstatptr
->f_fstypename
, gAPFSTypeName
, MFSNAMELEN
) == 0)) {
348 (void)AdoptVolume(mntstatptr
->f_mntonname
);
363 -- Returns: error code (0 if successful).
366 AdoptVolume(const char *path
) {
367 VolumeUUID targetuuid
;
368 VolumeStatusDBHandle vsdb
;
372 /* Look up the target volume UUID: */
373 result
= GetVolumeUUID(path
, &targetuuid
);
375 warnx("no valid volume UUID found on '%s': %s", path
, strerror(result
));
379 if (uuid_is_null(targetuuid
.uuid
)) {
380 warnx("internal error: incomplete UUID generated.");
384 /* Open the volume status DB to look up the entry for the volume in question: */
385 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
386 warnx("couldn't access volume status database: %s", strerror(result
));
390 /* Check to see if an entry exists. If not, prepare a default initial status value: */
391 if ((result
= GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
)) != 0) {
395 /* Enable permissions on the specified volume: */
396 volstatus
= (volstatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
398 /* Update the entry in the volume status database: */
399 if ((result
= SetVolumeStatusDBEntry(vsdb
, &targetuuid
, volstatus
)) != 0) {
400 warnx("couldn't update volume status database: %s", strerror(result
));
404 (void)CloseVolumeStatusDB(vsdb
);
406 if ((result
= UpdateMountStatus(path
, volstatus
)) != 0) {
407 warnx("couldn't update mount status of '%s': %s", path
, strerror(result
));
419 -- Returns: error code (0 if successful).
422 DisownVolume(const char *path
) {
423 VolumeUUID targetuuid
;
424 VolumeStatusDBHandle vsdb
;
428 /* Look up the target volume UUID: */
429 result
= GetVolumeUUID(path
, &targetuuid
);
431 warnx("no valid volume UUID found on '%s': %s", path
, strerror(result
));
436 if (!uuid_is_null(targetuuid
.uuid
)) {
437 /* Open the volume status DB to look up the entry for the volume in question: */
438 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
439 warnx("couldn't access volume status database: %s", strerror(result
));
443 /* Check to see if an entry exists. If not, prepare a default initial status value: */
444 if ((result
= GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
)) != 0) {
448 /* Disable permissions on the specified volume: */
449 volstatus
= (volstatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
451 /* Update the entry in the volume status database: */
452 if ((result
= SetVolumeStatusDBEntry(vsdb
, &targetuuid
, volstatus
)) != 0) {
453 warnx("couldn't update volume status database: %s", strerror(result
));
457 (void)CloseVolumeStatusDB(vsdb
);
461 if ((result
= UpdateMountStatus(path
, volstatus
)) != 0) {
462 warnx("couldn't update mount status of '%s': %s", path
, strerror(result
));
474 -- Returns: error code (0 if successful).
477 ClearVolumeUUID(const char *path
) {
478 VolumeUUID targetuuid
;
479 VolumeStatusDBHandle vsdb
;
483 /* Check to see whether the target volume has an assigned UUID: */
484 result
= GetVolumeUUID(path
, &targetuuid
);
486 warnx("couldn't read volume UUID on '%s': %s", path
, strerror(result
));
490 if (uuid_is_null(targetuuid
.uuid
) == 0) {
491 /* Open the volume status DB to look up the entry for the volume in question: */
492 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
493 warnx("couldn't access volume status database: %s", strerror(result
));
497 /* Check to see if an entry exists: */
498 if (GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
) == 0) {
499 /* Remove the entry from the volume status database: */
500 if ((result
= DeleteVolumeStatusDBEntry(vsdb
, &targetuuid
)) != 0) {
501 warnx("couldn't update volume status database: %s", strerror(result
));
506 (void)CloseVolumeStatusDB(vsdb
);
508 if ((result
= UpdateMountStatus(path
, 0)) != 0) {
509 warnx("couldn't update mount status of '%s': %s", path
, strerror(result
));
521 -- DisplayVolumeStatus
523 -- Returns: error code (0 if successful).
526 DisplayVolumeStatus(const char *path
) {
527 VolumeUUID targetuuid
;
528 VolumeStatusDBHandle vsdb
;
532 /* Look up the target volume UUID, exactly as stored on disk: */
533 result
= GetVolumeUUID(path
, &targetuuid
);
535 warnx("couldn't read volume UUID on '%s': %s", path
, strerror(result
));
539 if (uuid_is_null(targetuuid
.uuid
)) {
540 warnx("no valid volume UUID found on '%s': permissions are disabled.", path
);
544 /* Open the volume status DB to look up the entry for the volume in question: */
545 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
546 warnx("couldn't access volume status database: %s", strerror(result
));
550 if ((result
= GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
)) != 0) {
551 printf("No entry found for '%s'.\n", path
);
555 if (volstatus
& VOLUME_USEPERMISSIONS
) {
556 printf("Permissions on '%s' are enabled.\n", path
);
558 printf("Permissions on '%s' are disabled.\n", path
);
562 (void)CloseVolumeStatusDB(vsdb
);
567 static int isVolumeHFS (const char* path
) {
573 struct statfs statfs_buf
;
575 result
= statfs (path
, &statfs_buf
);
577 if (!strncmp(statfs_buf
.f_fstypename
, gHFSTypeName
, 3)) {
587 //struct definition for calling getattrlist for finderinfos
588 typedef struct FinderInfoBuf
{
589 uint32_t info_length
;
590 uint32_t finderinfo
[8];
593 typedef struct hfsUUID
{
601 -- Returns: error code (0 if successful).
605 GetVolumeUUID(const char *path
, VolumeUUID
*volumeUUIDPtr
) {
606 struct attrlist alist
;
607 struct { uint32_t size
; uuid_t uuid
; } volUUID
;
609 FinderInfoBuf_t finfo
;
614 * For a bit more definition on why we have to do this, check out
615 * hfs.util source. The gist is that IFF the volume is HFS, then
616 * we must check the finderinfo UUID FIRST before querying the
617 * fs for its full UUID via the getattrlist volume call.
620 if (isVolumeHFS(path
)) {
621 /* then go get the finderinfo, first... */
622 memset (&alist
, 0, sizeof(alist
));
623 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
625 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
626 alist
.volattr
= ATTR_VOL_INFO
;
631 result
= getattrlist (path
, &alist
, &finfo
, sizeof(finfo
), 0);
633 warn ("failed to getattrlist finderinfo for %s", path
);
638 hfsUUID_t
* hfs_finfo_uuid
= (hfsUUID_t
*)(&finfo
.finderinfo
[6]);
641 * Note: this is a bit of HFS-specific chicanery. When HFS+ generates
642 * the volume UUID, the formula it uses to generate the 8 bytes of internal
643 * UUID is re-looped/restarted if either high or low is zero. Thus, if we
644 * see either word as '0' then that means we should treat it as an uninitialized
647 * As a result, if we see either word as zero, then clear out the caller's buffer
648 * and return the NULL UUID. Otherwise, we'd get the 8 bytes which potentially include
649 * one or more zeroes run through HFS+'s MD5 algorithm which is not what we want.
651 //technically should endian-swap this but not necessary here
652 if ((hfs_finfo_uuid
->high
== 0) || (hfs_finfo_uuid
->low
== 0)) {
653 uuid_clear (volumeUUIDPtr
->uuid
);
659 /* Set up the attrlist structure to get the volume's UUID: */
660 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
662 alist
.commonattr
= 0;
663 alist
.volattr
= (ATTR_VOL_INFO
| ATTR_VOL_UUID
);
668 result
= getattrlist(path
, &alist
, &volUUID
, sizeof(volUUID
), 0);
670 warn("Couldn't get volume information for '%s'", path
);
675 uuid_copy(volumeUUIDPtr
->uuid
, volUUID
.uuid
);
686 /******************************************************************************
688 * V O L U M E S T A T U S D A T A B A S E R O U T I N E S
690 *****************************************************************************/
692 #define DBHANDLESIGNATURE 0x75917737
694 /* Flag values for operation options: */
695 #define DBMARKPOSITION 1
697 static char gVSDBPath
[] = "/var/db/volinfo.database";
699 #define MAXIOMALLOC 16384
701 /* Database layout: */
715 struct VSDBRecord record
;
724 struct VSDBKey64 key
;
727 struct VSDBRecord record
;
731 #define DBKEYSEPARATOR ':'
732 #define DBBLANKSPACE ' '
733 #define DBRECORDTERMINATOR '\n'
735 /* In-memory data structures: */
741 off_t recordPosition
;
744 typedef struct VSDBState
*VSDBStatePtr
;
748 /* Internal function prototypes: */
749 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
750 static int UnlockDB(VSDBStatePtr dbstateptr
);
752 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, u_int32_t options
);
753 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
754 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
755 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
756 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
758 static void FormatULong(u_int32_t u
, char *s
);
759 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
760 static void FormatDBRecord(u_int32_t volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
761 static void FormatDBEntry(VolumeUUID
*volumeID
, u_int32_t volumeStatusFlags
, struct VSDBEntry
*dbentry
);
762 static u_int32_t
ConvertHexStringToULong(const char *hs
, long maxdigits
);
766 /******************************************************************************
768 * P U B L I S H E D I N T E R F A C E R O U T I N E S
770 *****************************************************************************/
772 void ConvertVolumeUUIDString64ToUUID(const char *UUIDString64
, VolumeUUID
*volumeID
) {
781 for (i
= 0; (i
< VOLUMEUUID64LENGTH
) && ((c
= UUIDString64
[i
]) != (char)0) ; ++i
) {
782 if ((c
>= '0') && (c
<= '9')) {
784 } else if ((c
>= 'A') && (c
<= 'F')) {
785 nextdigit
= c
- 'A' + 10;
786 } else if ((c
>= 'a') && (c
<= 'f')) {
787 nextdigit
= c
- 'a' + 10;
791 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
792 high
= (high
<< 4) | carry
;
793 low
= (low
<< 4) | nextdigit
;
796 high
= OSSwapHostToBigInt32(high
);
797 low
= OSSwapHostToBigInt32(low
);
800 MD5_Update(&ctx
, kFSUUIDNamespaceSHA1
, sizeof(uuid_t
));
801 MD5_Update(&ctx
, &high
, sizeof(high
));
802 MD5_Update(&ctx
, &low
, sizeof(low
));
803 MD5_Final(volumeID
->uuid
, &ctx
);
805 volumeID
->uuid
[6] = (volumeID
->uuid
[6] & 0x0F) | 0x30;
806 volumeID
->uuid
[8] = (volumeID
->uuid
[8] & 0x3F) | 0x80;
811 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
812 VSDBStatePtr dbstateptr
;
816 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
817 if (dbstateptr
== NULL
) {
821 dbstateptr
->dbmode
= O_RDWR
;
822 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
);
823 if (dbstateptr
->dbfile
== -1) {
825 The file couldn't be opened for read/write access:
826 try read-only access before giving up altogether.
828 dbstateptr
->dbmode
= O_RDONLY
;
829 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
);
830 if (dbstateptr
->dbfile
== -1) {
835 dbstateptr
->signature
= DBHANDLESIGNATURE
;
836 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
837 ConvertVolumeStatusDB(*DBHandlePtr
);
843 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
844 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
845 struct VSDBEntry64 entry64
;
848 u_int32_t iobuffersize
;
849 void *iobuffer
= NULL
;
852 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
854 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
856 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
857 result
= read(dbstateptr
->dbfile
, &entry64
, sizeof(entry64
));
858 if ((result
!= sizeof(entry64
)) ||
859 (entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
860 (entry64
.space
!= DBBLANKSPACE
) ||
861 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
865 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
866 iobuffersize
= dbinfo
.st_size
;
867 iobuffer
= malloc(iobuffersize
);
868 if (iobuffer
== NULL
) {
873 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
874 result
= read(dbstateptr
->dbfile
, iobuffer
, iobuffersize
);
875 if (result
!= iobuffersize
) {
879 if ((result
= ftruncate(dbstateptr
->dbfile
, 0)) != 0) {
883 for (i
= 0; i
< iobuffersize
/ sizeof(entry64
); i
++) {
885 u_int32_t VolumeStatus
;
886 struct VSDBEntry dbentry
;
888 entry64
= *(((struct VSDBEntry64
*)iobuffer
) + i
);
889 if ((entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
890 (entry64
.space
!= DBBLANKSPACE
) ||
891 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
895 ConvertVolumeUUIDString64ToUUID(entry64
.key
.uuid
, &volumeID
);
896 VolumeStatus
= ConvertHexStringToULong(entry64
.record
.statusFlags
, sizeof(entry64
.record
.statusFlags
));
898 FormatDBEntry(&volumeID
, VolumeStatus
, &dbentry
);
899 if ((result
= AddVolumeRecord(dbstateptr
, &dbentry
)) != sizeof(dbentry
)) {
900 warnx("couldn't convert volume status database: %s", strerror(result
));
905 fsync(dbstateptr
->dbfile
);
911 if (iobuffer
) free(iobuffer
);
912 UnlockDB(dbstateptr
);
918 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t
*VolumeStatus
) {
919 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
920 struct VSDBEntry dbentry
;
923 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
925 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
927 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
930 *VolumeStatus
= ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
935 UnlockDB(dbstateptr
);
941 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t VolumeStatus
) {
942 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
943 struct VSDBEntry dbentry
;
946 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
947 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
949 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
951 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
952 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
953 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
954 } else if (result
== -1) {
955 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
960 fsync(dbstateptr
->dbfile
);
965 UnlockDB(dbstateptr
);
971 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
972 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
975 u_int32_t iobuffersize
;
976 void *iobuffer
= NULL
;
978 u_int32_t iotransfersize
;
979 u_int32_t bytestransferred
;
981 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
983 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
985 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
986 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
989 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
990 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
991 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
993 iobuffersize
= MAXIOMALLOC
;
995 if (iobuffersize
> 0) {
996 iobuffer
= malloc(iobuffersize
);
997 if (iobuffer
== NULL
) {
1002 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
1004 iotransfersize
= dbinfo
.st_size
- dataoffset
;
1005 if (iotransfersize
> 0) {
1006 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
1008 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
1009 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
1010 if (bytestransferred
!= iotransfersize
) {
1015 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
1016 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
1017 if (bytestransferred
!= iotransfersize
) {
1022 dataoffset
+= (off_t
)iotransfersize
;
1024 } while (iotransfersize
> 0);
1026 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
1030 fsync(dbstateptr
->dbfile
);
1036 if (iobuffer
) free(iobuffer
);
1037 UnlockDB(dbstateptr
);
1045 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
1046 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
1048 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
1050 dbstateptr
->signature
= 0;
1052 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
1053 dbstateptr
->dbfile
= 0;
1062 /******************************************************************************
1064 * I N T E R N A L O N L Y D A T A B A S E R O U T I N E S
1066 *****************************************************************************/
1068 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
1069 return flock(dbstateptr
->dbfile
, lockmode
);
1074 static int UnlockDB(VSDBStatePtr dbstateptr
) {
1075 return flock(dbstateptr
->dbfile
, LOCK_UN
);
1080 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, u_int32_t options
) {
1081 struct VSDBKey searchkey
;
1082 struct VSDBEntry dbentry
;
1085 FormatDBKey(volumeID
, &searchkey
);
1086 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
1089 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
1090 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
1091 if (targetEntry
!= NULL
) {
1092 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
1096 } while (result
== 0);
1103 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
1104 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
1105 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
1111 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
1112 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
1113 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
1118 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
1119 struct VSDBEntry entry
;
1122 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
1123 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
1124 if ((result
!= sizeof(entry
)) ||
1125 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
1126 (entry
.space
!= DBBLANKSPACE
) ||
1127 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
1131 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
1137 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
1138 return memcmp(key1
->uuidString
, key2
->uuidString
, sizeof(key1
->uuidString
));
1143 /******************************************************************************
1145 * F O R M A T T I N G A N D C O N V E R S I O N R O U T I N E S
1147 *****************************************************************************/
1149 static void FormatULong(u_int32_t u
, char *s
) {
1154 for (i
= 0; i
< 8; ++i
) {
1155 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
1157 *digitptr
++ = (char)(d
+ '0');
1159 *digitptr
++ = (char)(d
- 10 + 'A');
1167 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
1168 uuid_string_t uuid_str
;
1170 uuid_unparse(volumeID
->uuid
, uuid_str
);
1171 memcpy(dbkey
->uuidString
, uuid_str
, sizeof(dbkey
->uuidString
));
1176 static void FormatDBRecord(u_int32_t volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
1177 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
1182 static void FormatDBEntry(VolumeUUID
*volumeID
, u_int32_t volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
1183 FormatDBKey(volumeID
, &dbentry
->key
);
1184 dbentry
->keySeparator
= DBKEYSEPARATOR
;
1185 dbentry
->space
= DBBLANKSPACE
;
1186 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
1187 dbentry
->terminator
= DBRECORDTERMINATOR
;
1192 static u_int32_t
ConvertHexStringToULong(const char *hs
, long maxdigits
) {
1195 u_int32_t nextdigit
;
1199 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
1200 if ((c
>= '0') && (c
<= '9')) {
1201 nextdigit
= c
- '0';
1202 } else if ((c
>= 'A') && (c
<= 'F')) {
1203 nextdigit
= c
- 'A' + 10;
1204 } else if ((c
>= 'a') && (c
<= 'f')) {
1205 nextdigit
= c
- 'a' + 10;
1209 n
= (n
<< 4) + nextdigit
;