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 #include <libiosexec.h>
61 // This flags array is shared with the mount(8) tool.
62 #include "../mount_flags_dir/mount_flags.h"
64 //from mount_flags_dir/mount_flags.c
65 extern mountopt_t optnames
[];
69 * CommonCrypto is meant to be a more stable API than OpenSSL.
70 * Defining COMMON_DIGEST_FOR_OPENSSL gives API-compatibility
71 * with OpenSSL, so we don't have to change the code.
73 #define COMMON_DIGEST_FOR_OPENSSL
74 #include <CommonCrypto/CommonDigest.h>
75 #include <libkern/OSByteOrder.h>
77 static char usage
[] = "Usage: %s [-a path] | [-c path ] [-d path] [-i]\n";
79 static char gHFSTypeName
[] = "hfs";
80 static char gAPFSTypeName
[] = "apfs";
82 /*****************************************************************************
84 * The following should really be in some system header file:
86 *****************************************************************************/
88 typedef struct VolumeUUID
{
92 #define VOLUMEUUID64LENGTH 16
94 #define VOLUME_USEPERMISSIONS 0x00000001
95 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
97 typedef void *VolumeStatusDBHandle
;
99 void ConvertVolumeUUIDString64ToUUID(const char *UUIDString64
, VolumeUUID
*volumeID
);
101 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
102 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
103 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t
*VolumeStatus
);
104 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t VolumeStatus
);
105 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
106 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
108 /*****************************************************************************
110 * Internal function prototypes:
112 *****************************************************************************/
114 static void check_uid(void);
115 static int GetVolumeUUID(const char *path
, VolumeUUID
*volumeUUIDPtr
);
116 static int AdoptAllLocalVolumes(void);
117 static int AdoptVolume(const char *path
);
118 static int DisownVolume(const char *path
);
119 static int ClearVolumeUUID(const char *path
);
120 static int DisplayVolumeStatus(const char *path
);
121 static int UpdateMountStatus(const char *path
, u_int32_t volstatus
);
124 static int isVolumeHFS(const char*path
);
126 int main (int argc
, const char *argv
[])
133 fprintf(stderr
, usage
, argv
[0]);
137 for (arg
= 1; arg
< argc
; ++arg
) {
138 if ((argv
[arg
][0] == '-') &&
139 ((option
= argv
[arg
][1]) != (char)0) &&
140 (argv
[arg
][2] == (char)0)) {
144 /* Pick out the pathname argument: */
146 fprintf(stderr
, usage
, argv
[0]);
151 result
= AdoptVolume(argv
[arg
]);
156 /* Pick out the pathname argument: */
158 fprintf(stderr
, usage
, argv
[0]);
162 result
= DisplayVolumeStatus(argv
[arg
]);
167 /* Pick out the pathname argument: */
169 fprintf(stderr
, usage
, argv
[0]);
174 result
= DisownVolume(argv
[arg
]);
179 printf(usage
, argv
[0]);
181 printf("\t-a adopts (activates) on-disk permissions on the specified path,\n");
182 printf("\t-c checks the status of the permissions usage on the specified path\n");
183 printf("\t-d disowns (deactivates) the on-disk permissions on the specified path\n");
184 printf("\t-i initializes the permissions database to include all mounted HFS/HFS+ volumes\n");
190 result
= AdoptAllLocalVolumes();
195 /* Pick out the pathname argument: */
197 fprintf(stderr
, usage
, argv
[0]);
202 result
= ClearVolumeUUID(argv
[arg
]);
206 fprintf(stderr
, usage
, argv
[0]);
212 if (result
< 0) result
= 1; // Make sure only positive exit status codes are generated
214 exit(result
); // ensure the process exit status is returned
215 return result
; // ...and make main fit the ANSI spec.
220 static void check_uid(void) {
221 if (geteuid() != 0) {
222 fprintf(stderr
, "###\n");
223 fprintf(stderr
, "### You must be root to perform this operation.\n");
224 fprintf(stderr
, "###\n");
234 -- Returns: error code (0 if successful).
237 UpdateMountStatus(const char *path
, u_int32_t volstatus
) {
238 struct statfs mntstat
;
244 * selectors to determine whether or not certain features
245 * should be re-enabled via mount -u
248 #define MAXMOUNTLEN 255
251 char mountline
[MAXMOUNTLEN
];
252 char mountstring
[MAXMOUNTLEN
];
254 mountopt_t
* opt
= NULL
;
256 uint64_t flags_mask
= (MNT_NOSUID
| MNT_NODEV
|
257 MNT_NOEXEC
| MNT_RDONLY
|
258 MNT_CPROTECT
| MNT_QUARANTINE
|
259 MNT_UNION
| MNT_DONTBROWSE
);
261 result
= statfs(path
, &mntstat
);
263 warn("couldn't look up mount status for '%s'", path
);
267 bzero (mountline
, MAXMOUNTLEN
);
268 bzero (mountstring
, MAXMOUNTLEN
);
270 /* first, check for permissions */
271 if (volstatus
& VOLUME_USEPERMISSIONS
) {
272 strlcpy(mountline
, "perm", MAXMOUNTLEN
);
275 strlcpy(mountline
, "noperm", MAXMOUNTLEN
);
278 /* check the flags */
279 flags
= (mntstat
.f_flags
& flags_mask
);
282 * now iterate over all of the strings in the optname array and
283 * add them into the "mount" string if the flag they represent is set.
284 * The optnames array is extern'd (at the top of this file), and is defined
285 * in a .c file within the mount_flags directory
287 for (opt
= optnames
; flags
&& opt
->o_opt
; opt
++) {
288 if ((flags
& opt
->o_opt
) && opt
->o_cmd
) {
289 snprintf(mountstring
, sizeof(mountstring
), ",%s", opt
->o_cmd
);
290 result
= strlcat(mountline
, mountstring
, MAXMOUNTLEN
);
291 if (result
>= MAXMOUNTLEN
) {
292 // bail out, string is too long.
295 flags
&= ~opt
->o_opt
;
296 bzero (mountstring
, MAXMOUNTLEN
);
306 result
= execl("/sbin/mount", "mount",
307 "-t", mntstat
.f_fstypename
,
308 "-u","-o", mountline
,
309 mntstat
.f_mntfromname
, mntstat
.f_mntonname
, NULL
);
310 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
315 warn("couldn't fork to execute mount command");
320 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
))) {
321 result
= status
.w_retcode
;
332 -- AdoptAllLocalVolumes
334 -- Returns: error code (0 if successful).
337 AdoptAllLocalVolumes(void) {
338 struct statfs
*mntstatptr
;
341 fscount
= getmntinfo(&mntstatptr
, MNT_WAIT
);
343 warn("couldn't get information on mounted volumes");
347 while (fscount
> 0) {
348 if ((strncmp(mntstatptr
->f_fstypename
, gHFSTypeName
, MFSNAMELEN
) == 0) ||
349 (strncmp(mntstatptr
->f_fstypename
, gAPFSTypeName
, MFSNAMELEN
) == 0)) {
350 (void)AdoptVolume(mntstatptr
->f_mntonname
);
365 -- Returns: error code (0 if successful).
368 AdoptVolume(const char *path
) {
369 VolumeUUID targetuuid
;
370 VolumeStatusDBHandle vsdb
;
374 /* Look up the target volume UUID: */
375 result
= GetVolumeUUID(path
, &targetuuid
);
377 warnx("no valid volume UUID found on '%s': %s", path
, strerror(result
));
381 if (uuid_is_null(targetuuid
.uuid
)) {
382 warnx("internal error: incomplete UUID generated.");
386 /* Open the volume status DB to look up the entry for the volume in question: */
387 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
388 warnx("couldn't access volume status database: %s", strerror(result
));
392 /* Check to see if an entry exists. If not, prepare a default initial status value: */
393 if ((result
= GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
)) != 0) {
397 /* Enable permissions on the specified volume: */
398 volstatus
= (volstatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
400 /* Update the entry in the volume status database: */
401 if ((result
= SetVolumeStatusDBEntry(vsdb
, &targetuuid
, volstatus
)) != 0) {
402 warnx("couldn't update volume status database: %s", strerror(result
));
406 (void)CloseVolumeStatusDB(vsdb
);
408 if ((result
= UpdateMountStatus(path
, volstatus
)) != 0) {
409 warnx("couldn't update mount status of '%s': %s", path
, strerror(result
));
421 -- Returns: error code (0 if successful).
424 DisownVolume(const char *path
) {
425 VolumeUUID targetuuid
;
426 VolumeStatusDBHandle vsdb
;
430 /* Look up the target volume UUID: */
431 result
= GetVolumeUUID(path
, &targetuuid
);
433 warnx("no valid volume UUID found on '%s': %s", path
, strerror(result
));
438 if (!uuid_is_null(targetuuid
.uuid
)) {
439 /* Open the volume status DB to look up the entry for the volume in question: */
440 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
441 warnx("couldn't access volume status database: %s", strerror(result
));
445 /* Check to see if an entry exists. If not, prepare a default initial status value: */
446 if ((result
= GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
)) != 0) {
450 /* Disable permissions on the specified volume: */
451 volstatus
= (volstatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
453 /* Update the entry in the volume status database: */
454 if ((result
= SetVolumeStatusDBEntry(vsdb
, &targetuuid
, volstatus
)) != 0) {
455 warnx("couldn't update volume status database: %s", strerror(result
));
459 (void)CloseVolumeStatusDB(vsdb
);
463 if ((result
= UpdateMountStatus(path
, volstatus
)) != 0) {
464 warnx("couldn't update mount status of '%s': %s", path
, strerror(result
));
476 -- Returns: error code (0 if successful).
479 ClearVolumeUUID(const char *path
) {
480 VolumeUUID targetuuid
;
481 VolumeStatusDBHandle vsdb
;
485 /* Check to see whether the target volume has an assigned UUID: */
486 result
= GetVolumeUUID(path
, &targetuuid
);
488 warnx("couldn't read volume UUID on '%s': %s", path
, strerror(result
));
492 if (uuid_is_null(targetuuid
.uuid
) == 0) {
493 /* Open the volume status DB to look up the entry for the volume in question: */
494 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
495 warnx("couldn't access volume status database: %s", strerror(result
));
499 /* Check to see if an entry exists: */
500 if (GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
) == 0) {
501 /* Remove the entry from the volume status database: */
502 if ((result
= DeleteVolumeStatusDBEntry(vsdb
, &targetuuid
)) != 0) {
503 warnx("couldn't update volume status database: %s", strerror(result
));
508 (void)CloseVolumeStatusDB(vsdb
);
510 if ((result
= UpdateMountStatus(path
, 0)) != 0) {
511 warnx("couldn't update mount status of '%s': %s", path
, strerror(result
));
523 -- DisplayVolumeStatus
525 -- Returns: error code (0 if successful).
528 DisplayVolumeStatus(const char *path
) {
529 VolumeUUID targetuuid
;
530 VolumeStatusDBHandle vsdb
;
534 /* Look up the target volume UUID, exactly as stored on disk: */
535 result
= GetVolumeUUID(path
, &targetuuid
);
537 warnx("couldn't read volume UUID on '%s': %s", path
, strerror(result
));
541 if (uuid_is_null(targetuuid
.uuid
)) {
542 warnx("no valid volume UUID found on '%s': permissions are disabled.", path
);
546 /* Open the volume status DB to look up the entry for the volume in question: */
547 if ((result
= OpenVolumeStatusDB(&vsdb
)) != 0) {
548 warnx("couldn't access volume status database: %s", strerror(result
));
552 if ((result
= GetVolumeStatusDBEntry(vsdb
, &targetuuid
, &volstatus
)) != 0) {
553 printf("No entry found for '%s'.\n", path
);
557 if (volstatus
& VOLUME_USEPERMISSIONS
) {
558 printf("Permissions on '%s' are enabled.\n", path
);
560 printf("Permissions on '%s' are disabled.\n", path
);
564 (void)CloseVolumeStatusDB(vsdb
);
569 static int isVolumeHFS (const char* path
) {
575 struct statfs statfs_buf
;
577 result
= statfs (path
, &statfs_buf
);
579 if (!strncmp(statfs_buf
.f_fstypename
, gHFSTypeName
, 3)) {
589 //struct definition for calling getattrlist for finderinfos
590 typedef struct FinderInfoBuf
{
591 uint32_t info_length
;
592 uint32_t finderinfo
[8];
595 typedef struct hfsUUID
{
603 -- Returns: error code (0 if successful).
607 GetVolumeUUID(const char *path
, VolumeUUID
*volumeUUIDPtr
) {
608 struct attrlist alist
;
609 struct { uint32_t size
; uuid_t uuid
; } volUUID
;
611 FinderInfoBuf_t finfo
;
616 * For a bit more definition on why we have to do this, check out
617 * hfs.util source. The gist is that IFF the volume is HFS, then
618 * we must check the finderinfo UUID FIRST before querying the
619 * fs for its full UUID via the getattrlist volume call.
622 if (isVolumeHFS(path
)) {
623 /* then go get the finderinfo, first... */
624 memset (&alist
, 0, sizeof(alist
));
625 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
627 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
628 alist
.volattr
= ATTR_VOL_INFO
;
633 result
= getattrlist (path
, &alist
, &finfo
, sizeof(finfo
), 0);
635 warn ("failed to getattrlist finderinfo for %s", path
);
640 hfsUUID_t
* hfs_finfo_uuid
= (hfsUUID_t
*)(&finfo
.finderinfo
[6]);
643 * Note: this is a bit of HFS-specific chicanery. When HFS+ generates
644 * the volume UUID, the formula it uses to generate the 8 bytes of internal
645 * UUID is re-looped/restarted if either high or low is zero. Thus, if we
646 * see either word as '0' then that means we should treat it as an uninitialized
649 * As a result, if we see either word as zero, then clear out the caller's buffer
650 * and return the NULL UUID. Otherwise, we'd get the 8 bytes which potentially include
651 * one or more zeroes run through HFS+'s MD5 algorithm which is not what we want.
653 //technically should endian-swap this but not necessary here
654 if ((hfs_finfo_uuid
->high
== 0) || (hfs_finfo_uuid
->low
== 0)) {
655 uuid_clear (volumeUUIDPtr
->uuid
);
661 /* Set up the attrlist structure to get the volume's UUID: */
662 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
664 alist
.commonattr
= 0;
665 alist
.volattr
= (ATTR_VOL_INFO
| ATTR_VOL_UUID
);
670 result
= getattrlist(path
, &alist
, &volUUID
, sizeof(volUUID
), 0);
672 warn("Couldn't get volume information for '%s'", path
);
677 uuid_copy(volumeUUIDPtr
->uuid
, volUUID
.uuid
);
688 /******************************************************************************
690 * 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
692 *****************************************************************************/
694 #define DBHANDLESIGNATURE 0x75917737
696 /* Flag values for operation options: */
697 #define DBMARKPOSITION 1
699 static char gVSDBPath
[] = "/var/db/volinfo.database";
701 #define MAXIOMALLOC 16384
703 /* Database layout: */
717 struct VSDBRecord record
;
726 struct VSDBKey64 key
;
729 struct VSDBRecord record
;
733 #define DBKEYSEPARATOR ':'
734 #define DBBLANKSPACE ' '
735 #define DBRECORDTERMINATOR '\n'
737 /* In-memory data structures: */
743 off_t recordPosition
;
746 typedef struct VSDBState
*VSDBStatePtr
;
750 /* Internal function prototypes: */
751 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
752 static int UnlockDB(VSDBStatePtr dbstateptr
);
754 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, u_int32_t options
);
755 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
756 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
757 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
758 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
760 static void FormatULong(u_int32_t u
, char *s
);
761 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
762 static void FormatDBRecord(u_int32_t volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
763 static void FormatDBEntry(VolumeUUID
*volumeID
, u_int32_t volumeStatusFlags
, struct VSDBEntry
*dbentry
);
764 static u_int32_t
ConvertHexStringToULong(const char *hs
, long maxdigits
);
768 /******************************************************************************
770 * 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
772 *****************************************************************************/
774 void ConvertVolumeUUIDString64ToUUID(const char *UUIDString64
, VolumeUUID
*volumeID
) {
783 for (i
= 0; (i
< VOLUMEUUID64LENGTH
) && ((c
= UUIDString64
[i
]) != (char)0) ; ++i
) {
784 if ((c
>= '0') && (c
<= '9')) {
786 } else if ((c
>= 'A') && (c
<= 'F')) {
787 nextdigit
= c
- 'A' + 10;
788 } else if ((c
>= 'a') && (c
<= 'f')) {
789 nextdigit
= c
- 'a' + 10;
793 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
794 high
= (high
<< 4) | carry
;
795 low
= (low
<< 4) | nextdigit
;
798 high
= OSSwapHostToBigInt32(high
);
799 low
= OSSwapHostToBigInt32(low
);
802 MD5_Update(&ctx
, kFSUUIDNamespaceSHA1
, sizeof(uuid_t
));
803 MD5_Update(&ctx
, &high
, sizeof(high
));
804 MD5_Update(&ctx
, &low
, sizeof(low
));
805 MD5_Final(volumeID
->uuid
, &ctx
);
807 volumeID
->uuid
[6] = (volumeID
->uuid
[6] & 0x0F) | 0x30;
808 volumeID
->uuid
[8] = (volumeID
->uuid
[8] & 0x3F) | 0x80;
813 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
814 VSDBStatePtr dbstateptr
;
818 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
819 if (dbstateptr
== NULL
) {
823 dbstateptr
->dbmode
= O_RDWR
;
824 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
);
825 if (dbstateptr
->dbfile
== -1) {
827 The file couldn't be opened for read/write access:
828 try read-only access before giving up altogether.
830 dbstateptr
->dbmode
= O_RDONLY
;
831 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
);
832 if (dbstateptr
->dbfile
== -1) {
837 dbstateptr
->signature
= DBHANDLESIGNATURE
;
838 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
839 ConvertVolumeStatusDB(*DBHandlePtr
);
845 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
846 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
847 struct VSDBEntry64 entry64
;
850 u_int32_t iobuffersize
;
851 void *iobuffer
= NULL
;
854 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
856 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
858 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
859 result
= read(dbstateptr
->dbfile
, &entry64
, sizeof(entry64
));
860 if ((result
!= sizeof(entry64
)) ||
861 (entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
862 (entry64
.space
!= DBBLANKSPACE
) ||
863 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
867 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
868 iobuffersize
= dbinfo
.st_size
;
869 iobuffer
= malloc(iobuffersize
);
870 if (iobuffer
== NULL
) {
875 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
876 result
= read(dbstateptr
->dbfile
, iobuffer
, iobuffersize
);
877 if (result
!= iobuffersize
) {
881 if ((result
= ftruncate(dbstateptr
->dbfile
, 0)) != 0) {
885 for (i
= 0; i
< iobuffersize
/ sizeof(entry64
); i
++) {
887 u_int32_t VolumeStatus
;
888 struct VSDBEntry dbentry
;
890 entry64
= *(((struct VSDBEntry64
*)iobuffer
) + i
);
891 if ((entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
892 (entry64
.space
!= DBBLANKSPACE
) ||
893 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
897 ConvertVolumeUUIDString64ToUUID(entry64
.key
.uuid
, &volumeID
);
898 VolumeStatus
= ConvertHexStringToULong(entry64
.record
.statusFlags
, sizeof(entry64
.record
.statusFlags
));
900 FormatDBEntry(&volumeID
, VolumeStatus
, &dbentry
);
901 if ((result
= AddVolumeRecord(dbstateptr
, &dbentry
)) != sizeof(dbentry
)) {
902 warnx("couldn't convert volume status database: %s", strerror(result
));
907 fsync(dbstateptr
->dbfile
);
913 if (iobuffer
) free(iobuffer
);
914 UnlockDB(dbstateptr
);
920 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t
*VolumeStatus
) {
921 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
922 struct VSDBEntry dbentry
;
925 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
927 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
929 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
932 *VolumeStatus
= ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
937 UnlockDB(dbstateptr
);
943 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, u_int32_t VolumeStatus
) {
944 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
945 struct VSDBEntry dbentry
;
948 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
949 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
951 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
953 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
954 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
955 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
956 } else if (result
== -1) {
957 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
962 fsync(dbstateptr
->dbfile
);
967 UnlockDB(dbstateptr
);
973 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
974 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
977 u_int32_t iobuffersize
;
978 void *iobuffer
= NULL
;
980 u_int32_t iotransfersize
;
981 u_int32_t bytestransferred
;
983 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
985 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
987 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
988 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
991 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
992 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
993 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
995 iobuffersize
= MAXIOMALLOC
;
997 if (iobuffersize
> 0) {
998 iobuffer
= malloc(iobuffersize
);
999 if (iobuffer
== NULL
) {
1004 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
1006 iotransfersize
= dbinfo
.st_size
- dataoffset
;
1007 if (iotransfersize
> 0) {
1008 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
1010 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
1011 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
1012 if (bytestransferred
!= iotransfersize
) {
1017 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
1018 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
1019 if (bytestransferred
!= iotransfersize
) {
1024 dataoffset
+= (off_t
)iotransfersize
;
1026 } while (iotransfersize
> 0);
1028 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
1032 fsync(dbstateptr
->dbfile
);
1038 if (iobuffer
) free(iobuffer
);
1039 UnlockDB(dbstateptr
);
1047 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
1048 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
1050 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
1052 dbstateptr
->signature
= 0;
1054 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
1055 dbstateptr
->dbfile
= 0;
1064 /******************************************************************************
1066 * 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
1068 *****************************************************************************/
1070 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
1071 return flock(dbstateptr
->dbfile
, lockmode
);
1076 static int UnlockDB(VSDBStatePtr dbstateptr
) {
1077 return flock(dbstateptr
->dbfile
, LOCK_UN
);
1082 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, u_int32_t options
) {
1083 struct VSDBKey searchkey
;
1084 struct VSDBEntry dbentry
;
1087 FormatDBKey(volumeID
, &searchkey
);
1088 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
1091 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
1092 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
1093 if (targetEntry
!= NULL
) {
1094 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
1098 } while (result
== 0);
1105 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
1106 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
1107 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
1113 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
1114 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
1115 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
1120 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
1121 struct VSDBEntry entry
;
1124 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
1125 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
1126 if ((result
!= sizeof(entry
)) ||
1127 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
1128 (entry
.space
!= DBBLANKSPACE
) ||
1129 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
1133 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
1139 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
1140 return memcmp(key1
->uuidString
, key2
->uuidString
, sizeof(key1
->uuidString
));
1145 /******************************************************************************
1147 * 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
1149 *****************************************************************************/
1151 static void FormatULong(u_int32_t u
, char *s
) {
1156 for (i
= 0; i
< 8; ++i
) {
1157 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
1159 *digitptr
++ = (char)(d
+ '0');
1161 *digitptr
++ = (char)(d
- 10 + 'A');
1169 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
1170 uuid_string_t uuid_str
;
1172 uuid_unparse(volumeID
->uuid
, uuid_str
);
1173 memcpy(dbkey
->uuidString
, uuid_str
, sizeof(dbkey
->uuidString
));
1178 static void FormatDBRecord(u_int32_t volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
1179 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
1184 static void FormatDBEntry(VolumeUUID
*volumeID
, u_int32_t volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
1185 FormatDBKey(volumeID
, &dbentry
->key
);
1186 dbentry
->keySeparator
= DBKEYSEPARATOR
;
1187 dbentry
->space
= DBBLANKSPACE
;
1188 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
1189 dbentry
->terminator
= DBRECORDTERMINATOR
;
1194 static u_int32_t
ConvertHexStringToULong(const char *hs
, long maxdigits
) {
1197 u_int32_t nextdigit
;
1201 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
1202 if ((c
>= '0') && (c
<= '9')) {
1203 nextdigit
= c
- '0';
1204 } else if ((c
>= 'A') && (c
<= 'F')) {
1205 nextdigit
= c
- 'A' + 10;
1206 } else if ((c
>= 'a') && (c
<= 'f')) {
1207 nextdigit
= c
- 'a' + 10;
1211 n
= (n
<< 4) + nextdigit
;