]> git.cameronkatri.com Git - apple_cmds.git/blob - diskdev_cmds/vsdbutil.tproj/vsdbutil_main.c
system_cmds: shutdown may not work
[apple_cmds.git] / diskdev_cmds / vsdbutil.tproj / vsdbutil_main.c
1 /*
2 * Copyright (c) 2000-2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 About vsdbutil.c:
26 Contains code to manipulate the volume status DB (/var/db/volinfo.database).
27
28 Change History:
29 18-Apr-2000 Pat Dirks New Today.
30
31 */
32
33
34 /* ************************************** I N C L U D E S ***************************************** */
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/stat.h>
40 #include <sys/sysctl.h>
41 #include <sys/time.h>
42 #include <sys/ucred.h>
43 #include <sys/resource.h>
44 #include <sys/vmmeter.h>
45 #include <sys/wait.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <fcntl.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <dirent.h>
53 #include <strings.h>
54
55 #include <sys/attr.h>
56 #include <uuid/uuid.h>
57 #include <System/uuid/namespace.h>
58
59 // This flags array is shared with the mount(8) tool.
60 #include "../mount_flags_dir/mount_flags.h"
61
62 //from mount_flags_dir/mount_flags.c
63 extern mountopt_t optnames[];
64
65
66 /*
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.
70 */
71 #define COMMON_DIGEST_FOR_OPENSSL
72 #include <CommonCrypto/CommonDigest.h>
73 #include <libkern/OSByteOrder.h>
74
75 static char usage[] = "Usage: %s [-a path] | [-c path ] [-d path] [-i]\n";
76
77 static char gHFSTypeName[] = "hfs";
78 static char gAPFSTypeName[] = "apfs";
79
80 /*****************************************************************************
81 *
82 * The following should really be in some system header file:
83 *
84 *****************************************************************************/
85
86 typedef struct VolumeUUID {
87 uuid_t uuid;
88 } VolumeUUID;
89
90 #define VOLUMEUUID64LENGTH 16
91
92 #define VOLUME_USEPERMISSIONS 0x00000001
93 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
94
95 typedef void *VolumeStatusDBHandle;
96
97 void ConvertVolumeUUIDString64ToUUID(const char *UUIDString64, VolumeUUID *volumeID);
98
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);
105
106 /*****************************************************************************
107 *
108 * Internal function prototypes:
109 *
110 *****************************************************************************/
111
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);
120
121
122 static int isVolumeHFS(const char*path);
123
124 int main (int argc, const char *argv[])
125 {
126 int arg;
127 char option;
128 int result = 0;
129
130 if (argc < 2) {
131 fprintf(stderr, usage, argv[0]);
132 exit(1);
133 };
134
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)) {
139 switch (option) {
140 case 'a':
141 case 'A':
142 /* Pick out the pathname argument: */
143 if (++arg >= argc) {
144 fprintf(stderr, usage, argv[0]);
145 exit(1);
146 }
147
148 check_uid();
149 result = AdoptVolume(argv[arg]);
150 break;
151
152 case 'c':
153 case 'C':
154 /* Pick out the pathname argument: */
155 if (++arg >= argc) {
156 fprintf(stderr, usage, argv[0]);
157 exit(1);
158 };
159
160 result = DisplayVolumeStatus(argv[arg]);
161 break;
162
163 case 'd':
164 case 'D':
165 /* Pick out the pathname argument: */
166 if (++arg >= argc) {
167 fprintf(stderr, usage, argv[0]);
168 exit(1);
169 };
170
171 check_uid();
172 result = DisownVolume(argv[arg]);
173 break;
174
175 case 'h':
176 case 'H':
177 printf(usage, argv[0]);
178 printf("where\n");
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");
183 break;
184
185 case 'i':
186 case 'I':
187 check_uid();
188 result = AdoptAllLocalVolumes();
189 break;
190
191 case 'x':
192 case 'X':
193 /* Pick out the pathname argument: */
194 if (++arg >= argc) {
195 fprintf(stderr, usage, argv[0]);
196 exit(1);
197 };
198
199 check_uid();
200 result = ClearVolumeUUID(argv[arg]);
201 break;
202
203 default:
204 fprintf(stderr, usage, argv[0]);
205 exit(1);
206 }
207 }
208 }
209
210 if (result < 0) result = 1; // Make sure only positive exit status codes are generated
211
212 exit(result); // ensure the process exit status is returned
213 return result; // ...and make main fit the ANSI spec.
214 }
215
216
217
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");
223 exit(1);
224 };
225 }
226
227
228
229 /*
230 -- UpdateMountStatus
231 --
232 -- Returns: error code (0 if successful).
233 */
234 static int
235 UpdateMountStatus(const char *path, u_int32_t volstatus) {
236 struct statfs mntstat;
237 int result;
238 union wait status;
239 int pid;
240
241 /*
242 * selectors to determine whether or not certain features
243 * should be re-enabled via mount -u
244 */
245 #ifndef MAXMOUNTLEN
246 #define MAXMOUNTLEN 255
247 #endif
248
249 char mountline[MAXMOUNTLEN];
250 char mountstring[MAXMOUNTLEN];
251
252 mountopt_t* opt = NULL;
253 uint64_t flags;
254 uint64_t flags_mask = (MNT_NOSUID | MNT_NODEV |
255 MNT_NOEXEC | MNT_RDONLY |
256 MNT_CPROTECT | MNT_QUARANTINE |
257 MNT_UNION | MNT_DONTBROWSE);
258
259 result = statfs(path, &mntstat);
260 if (result != 0) {
261 warn("couldn't look up mount status for '%s'", path);
262 return errno;
263 }
264
265 bzero (mountline, MAXMOUNTLEN);
266 bzero (mountstring, MAXMOUNTLEN);
267
268 /* first, check for permissions */
269 if (volstatus & VOLUME_USEPERMISSIONS) {
270 strlcpy(mountline, "perm", MAXMOUNTLEN);
271 }
272 else {
273 strlcpy(mountline, "noperm", MAXMOUNTLEN);
274 }
275
276 /* check the flags */
277 flags = (mntstat.f_flags & flags_mask);
278
279 /*
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
284 */
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.
291 return EINVAL;
292 }
293 flags &= ~opt->o_opt;
294 bzero (mountstring, MAXMOUNTLEN);
295 }
296 }
297
298 #ifdef MAXMOUNTLEN
299 #undef MAXMOUNTLEN
300 #endif
301
302 pid = fork();
303 if (pid == 0) {
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 */
309 return (1);
310 }
311
312 if (pid == -1) {
313 warn("couldn't fork to execute mount command");
314 return errno;
315 };
316
317 /* Success! */
318 if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status))) {
319 result = status.w_retcode;
320 } else {
321 result = -1;
322 };
323
324 return result;
325 }
326
327
328
329 /*
330 -- AdoptAllLocalVolumes
331 --
332 -- Returns: error code (0 if successful).
333 */
334 static int
335 AdoptAllLocalVolumes(void) {
336 struct statfs *mntstatptr;
337 int fscount;
338
339 fscount = getmntinfo(&mntstatptr, MNT_WAIT);
340 if (fscount == 0) {
341 warn("couldn't get information on mounted volumes");
342 return errno;
343 };
344
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);
349 };
350
351 ++mntstatptr;
352 --fscount;
353 };
354
355 return 0;
356 }
357
358
359
360 /*
361 -- AdoptVolume
362 --
363 -- Returns: error code (0 if successful).
364 */
365 static int
366 AdoptVolume(const char *path) {
367 VolumeUUID targetuuid;
368 VolumeStatusDBHandle vsdb;
369 u_int32_t volstatus;
370 int result = 0;
371
372 /* Look up the target volume UUID: */
373 result = GetVolumeUUID(path, &targetuuid);
374 if (result != 0) {
375 warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
376 return result;
377 };
378
379 if (uuid_is_null(targetuuid.uuid)) {
380 warnx("internal error: incomplete UUID generated.");
381 return EINVAL;
382 };
383
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));
387 return result;
388 };
389
390 /* Check to see if an entry exists. If not, prepare a default initial status value: */
391 if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
392 volstatus = 0;
393 };
394
395 /* Enable permissions on the specified volume: */
396 volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;
397
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));
401 return result;
402 };
403
404 (void)CloseVolumeStatusDB(vsdb);
405
406 if ((result = UpdateMountStatus(path, volstatus)) != 0) {
407 warnx("couldn't update mount status of '%s': %s", path, strerror(result));
408 return result;
409 };
410
411 return 0;
412 }
413
414
415
416 /*
417 -- DisownVolume
418 --
419 -- Returns: error code (0 if successful).
420 */
421 static int
422 DisownVolume(const char *path) {
423 VolumeUUID targetuuid;
424 VolumeStatusDBHandle vsdb;
425 u_int32_t volstatus;
426 int result = 0;
427
428 /* Look up the target volume UUID: */
429 result = GetVolumeUUID(path, &targetuuid);
430 if (result != 0) {
431 warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
432 return result;
433 };
434
435 volstatus = 0;
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));
440 return result;
441 };
442
443 /* Check to see if an entry exists. If not, prepare a default initial status value: */
444 if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
445 volstatus = 0;
446 };
447
448 /* Disable permissions on the specified volume: */
449 volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;
450
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));
454 return result;
455 };
456
457 (void)CloseVolumeStatusDB(vsdb);
458
459 };
460
461 if ((result = UpdateMountStatus(path, volstatus)) != 0) {
462 warnx("couldn't update mount status of '%s': %s", path, strerror(result));
463 return result;
464 };
465
466 return result;
467 };
468
469
470
471 /*
472 -- ClearVolumeUUID
473 --
474 -- Returns: error code (0 if successful).
475 */
476 static int
477 ClearVolumeUUID(const char *path) {
478 VolumeUUID targetuuid;
479 VolumeStatusDBHandle vsdb;
480 u_int32_t volstatus;
481 int result = 0;
482
483 /* Check to see whether the target volume has an assigned UUID: */
484 result = GetVolumeUUID(path, &targetuuid);
485 if (result != 0) {
486 warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
487 return result;
488 };
489
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));
494 return result;
495 };
496
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));
502 return result;
503 };
504 };
505
506 (void)CloseVolumeStatusDB(vsdb);
507
508 if ((result = UpdateMountStatus(path, 0)) != 0) {
509 warnx("couldn't update mount status of '%s': %s", path, strerror(result));
510 return result;
511 };
512
513 };
514
515 return result;
516 };
517
518
519
520 /*
521 -- DisplayVolumeStatus
522 --
523 -- Returns: error code (0 if successful).
524 */
525 static int
526 DisplayVolumeStatus(const char *path) {
527 VolumeUUID targetuuid;
528 VolumeStatusDBHandle vsdb;
529 u_int32_t volstatus;
530 int result = 0;
531
532 /* Look up the target volume UUID, exactly as stored on disk: */
533 result = GetVolumeUUID(path, &targetuuid);
534 if (result != 0) {
535 warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
536 return result;
537 };
538
539 if (uuid_is_null(targetuuid.uuid)) {
540 warnx("no valid volume UUID found on '%s': permissions are disabled.", path);
541 return 0;
542 };
543
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));
547 return result;
548 };
549
550 if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
551 printf("No entry found for '%s'.\n", path);
552 goto Std_Exit;
553 };
554
555 if (volstatus & VOLUME_USEPERMISSIONS) {
556 printf("Permissions on '%s' are enabled.\n", path);
557 } else {
558 printf("Permissions on '%s' are disabled.\n", path);
559 };
560
561 Std_Exit:
562 (void)CloseVolumeStatusDB(vsdb);
563
564 return result;
565 }
566
567 static int isVolumeHFS (const char* path) {
568
569 /* default to no */
570 int result = 0;
571 int isHFS = 0;
572
573 struct statfs statfs_buf;
574
575 result = statfs (path, &statfs_buf);
576 if (result == 0) {
577 if (!strncmp(statfs_buf.f_fstypename, gHFSTypeName, 3)) {
578 isHFS = 1;
579 }
580 }
581
582 return isHFS;
583 }
584
585
586
587 //struct definition for calling getattrlist for finderinfos
588 typedef struct FinderInfoBuf {
589 uint32_t info_length;
590 uint32_t finderinfo[8];
591 } FinderInfoBuf_t;
592
593 typedef struct hfsUUID {
594 uint32_t high;
595 uint32_t low;
596 } hfsUUID_t;
597
598 /*
599 -- GetVolumeUUID
600 --
601 -- Returns: error code (0 if successful).
602 */
603
604 static int
605 GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr) {
606 struct attrlist alist;
607 struct { uint32_t size; uuid_t uuid; } volUUID;
608
609 FinderInfoBuf_t finfo;
610
611 int result;
612
613 /*
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.
618 */
619
620 if (isVolumeHFS(path)) {
621 /* then go get the finderinfo, first... */
622 memset (&alist, 0, sizeof(alist));
623 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
624 alist.reserved = 0;
625 alist.commonattr = ATTR_CMN_FNDRINFO;
626 alist.volattr = ATTR_VOL_INFO;
627 alist.dirattr = 0;
628 alist.fileattr = 0;
629 alist.forkattr = 0;
630
631 result = getattrlist (path, &alist, &finfo, sizeof(finfo), 0);
632 if (result) {
633 warn ("failed to getattrlist finderinfo for %s", path);
634 result = errno;
635 goto Err_Exit;
636 }
637
638 hfsUUID_t* hfs_finfo_uuid = (hfsUUID_t*)(&finfo.finderinfo[6]);
639
640 /*
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
645 * UUID.
646 *
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.
650 */
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);
654 return 0;
655 }
656 }
657
658
659 /* Set up the attrlist structure to get the volume's UUID: */
660 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
661 alist.reserved = 0;
662 alist.commonattr = 0;
663 alist.volattr = (ATTR_VOL_INFO | ATTR_VOL_UUID);
664 alist.dirattr = 0;
665 alist.fileattr = 0;
666 alist.forkattr = 0;
667
668 result = getattrlist(path, &alist, &volUUID, sizeof(volUUID), 0);
669 if (result) {
670 warn("Couldn't get volume information for '%s'", path);
671 result = errno;
672 goto Err_Exit;
673 }
674
675 uuid_copy(volumeUUIDPtr->uuid, volUUID.uuid);
676 result = 0;
677
678 Err_Exit:
679 return result;
680 };
681
682
683
684
685
686 /******************************************************************************
687 *
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
689 *
690 *****************************************************************************/
691
692 #define DBHANDLESIGNATURE 0x75917737
693
694 /* Flag values for operation options: */
695 #define DBMARKPOSITION 1
696
697 static char gVSDBPath[] = "/var/db/volinfo.database";
698
699 #define MAXIOMALLOC 16384
700
701 /* Database layout: */
702
703 struct VSDBKey {
704 char uuidString[36];
705 };
706
707 struct VSDBRecord {
708 char statusFlags[8];
709 };
710
711 struct VSDBEntry {
712 struct VSDBKey key;
713 char keySeparator;
714 char space;
715 struct VSDBRecord record;
716 char terminator;
717 };
718
719 struct VSDBKey64 {
720 char uuid[16];
721 };
722
723 struct VSDBEntry64 {
724 struct VSDBKey64 key;
725 char keySeparator;
726 char space;
727 struct VSDBRecord record;
728 char terminator;
729 };
730
731 #define DBKEYSEPARATOR ':'
732 #define DBBLANKSPACE ' '
733 #define DBRECORDTERMINATOR '\n'
734
735 /* In-memory data structures: */
736
737 struct VSDBState {
738 u_int32_t signature;
739 int dbfile;
740 int dbmode;
741 off_t recordPosition;
742 };
743
744 typedef struct VSDBState *VSDBStatePtr;
745
746
747
748 /* Internal function prototypes: */
749 static int LockDB(VSDBStatePtr dbstateptr, int lockmode);
750 static int UnlockDB(VSDBStatePtr dbstateptr);
751
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);
757
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);
763
764
765
766 /******************************************************************************
767 *
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
769 *
770 *****************************************************************************/
771
772 void ConvertVolumeUUIDString64ToUUID(const char *UUIDString64, VolumeUUID *volumeID) {
773 int i;
774 char c;
775 u_int32_t nextdigit;
776 u_int32_t high = 0;
777 u_int32_t low = 0;
778 u_int32_t carry;
779 MD5_CTX ctx;
780
781 for (i = 0; (i < VOLUMEUUID64LENGTH) && ((c = UUIDString64[i]) != (char)0) ; ++i) {
782 if ((c >= '0') && (c <= '9')) {
783 nextdigit = c - '0';
784 } else if ((c >= 'A') && (c <= 'F')) {
785 nextdigit = c - 'A' + 10;
786 } else if ((c >= 'a') && (c <= 'f')) {
787 nextdigit = c - 'a' + 10;
788 } else {
789 nextdigit = 0;
790 };
791 carry = ((low & 0xF0000000) >> 28) & 0x0000000F;
792 high = (high << 4) | carry;
793 low = (low << 4) | nextdigit;
794 };
795
796 high = OSSwapHostToBigInt32(high);
797 low = OSSwapHostToBigInt32(low);
798
799 MD5_Init(&ctx);
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);
804
805 volumeID->uuid[6] = (volumeID->uuid[6] & 0x0F) | 0x30;
806 volumeID->uuid[8] = (volumeID->uuid[8] & 0x3F) | 0x80;
807 }
808
809
810
811 int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
812 VSDBStatePtr dbstateptr;
813
814 *DBHandlePtr = NULL;
815
816 dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr));
817 if (dbstateptr == NULL) {
818 return ENOMEM;
819 };
820
821 dbstateptr->dbmode = O_RDWR;
822 dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
823 if (dbstateptr->dbfile == -1) {
824 /*
825 The file couldn't be opened for read/write access:
826 try read-only access before giving up altogether.
827 */
828 dbstateptr->dbmode = O_RDONLY;
829 dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
830 if (dbstateptr->dbfile == -1) {
831 return errno;
832 };
833 };
834
835 dbstateptr->signature = DBHANDLESIGNATURE;
836 *DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
837 ConvertVolumeStatusDB(*DBHandlePtr);
838 return 0;
839 }
840
841
842
843 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
844 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
845 struct VSDBEntry64 entry64;
846 struct stat dbinfo;
847 int result;
848 u_int32_t iobuffersize;
849 void *iobuffer = NULL;
850 int i;
851
852 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
853
854 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
855
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)) {
862 result = 0;
863 goto ErrExit;
864 } else {
865 if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit;
866 iobuffersize = dbinfo.st_size;
867 iobuffer = malloc(iobuffersize);
868 if (iobuffer == NULL) {
869 result = ENOMEM;
870 goto ErrExit;
871 };
872
873 lseek(dbstateptr->dbfile, 0, SEEK_SET);
874 result = read(dbstateptr->dbfile, iobuffer, iobuffersize);
875 if (result != iobuffersize) {
876 result = errno;
877 goto ErrExit;
878 };
879 if ((result = ftruncate(dbstateptr->dbfile, 0)) != 0) {
880 goto ErrExit;
881 };
882
883 for (i = 0; i < iobuffersize / sizeof(entry64); i++) {
884 VolumeUUID volumeID;
885 u_int32_t VolumeStatus;
886 struct VSDBEntry dbentry;
887
888 entry64 = *(((struct VSDBEntry64 *)iobuffer) + i);
889 if ((entry64.keySeparator != DBKEYSEPARATOR) ||
890 (entry64.space != DBBLANKSPACE) ||
891 (entry64.terminator != DBRECORDTERMINATOR)) {
892 continue;
893 }
894
895 ConvertVolumeUUIDString64ToUUID(entry64.key.uuid, &volumeID);
896 VolumeStatus = ConvertHexStringToULong(entry64.record.statusFlags, sizeof(entry64.record.statusFlags));
897
898 FormatDBEntry(&volumeID, VolumeStatus, &dbentry);
899 if ((result = AddVolumeRecord(dbstateptr, &dbentry)) != sizeof(dbentry)) {
900 warnx("couldn't convert volume status database: %s", strerror(result));
901 goto ErrExit;
902 };
903 };
904
905 fsync(dbstateptr->dbfile);
906
907 result = 0;
908 };
909
910 ErrExit:
911 if (iobuffer) free(iobuffer);
912 UnlockDB(dbstateptr);
913 return result;
914 }
915
916
917
918 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, u_int32_t *VolumeStatus) {
919 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
920 struct VSDBEntry dbentry;
921 int result;
922
923 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
924
925 if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
926
927 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
928 goto ErrExit;
929 };
930 *VolumeStatus = ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
931
932 result = 0;
933
934 ErrExit:
935 UnlockDB(dbstateptr);
936 return result;
937 }
938
939
940
941 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, u_int32_t VolumeStatus) {
942 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
943 struct VSDBEntry dbentry;
944 int result;
945
946 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
947 if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
948
949 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
950
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);
956 } else {
957 goto ErrExit;
958 };
959
960 fsync(dbstateptr->dbfile);
961
962 result = 0;
963
964 ErrExit:
965 UnlockDB(dbstateptr);
966 return result;
967 }
968
969
970
971 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
972 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
973 struct stat dbinfo;
974 int result;
975 u_int32_t iobuffersize;
976 void *iobuffer = NULL;
977 off_t dataoffset;
978 u_int32_t iotransfersize;
979 u_int32_t bytestransferred;
980
981 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
982
983 if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
984
985 if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
986 if (result == -1) result = 0; /* Entry wasn't in the database to begin with? */
987 goto StdEdit;
988 } else {
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);
992 } else {
993 iobuffersize = MAXIOMALLOC;
994 };
995 if (iobuffersize > 0) {
996 iobuffer = malloc(iobuffersize);
997 if (iobuffer == NULL) {
998 result = ENOMEM;
999 goto ErrExit;
1000 };
1001
1002 dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
1003 do {
1004 iotransfersize = dbinfo.st_size - dataoffset;
1005 if (iotransfersize > 0) {
1006 if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
1007
1008 lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
1009 bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
1010 if (bytestransferred != iotransfersize) {
1011 result = errno;
1012 goto ErrExit;
1013 };
1014
1015 lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET);
1016 bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize);
1017 if (bytestransferred != iotransfersize) {
1018 result = errno;
1019 goto ErrExit;
1020 };
1021
1022 dataoffset += (off_t)iotransfersize;
1023 };
1024 } while (iotransfersize > 0);
1025 };
1026 if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
1027 goto ErrExit;
1028 };
1029
1030 fsync(dbstateptr->dbfile);
1031
1032 result = 0;
1033 };
1034
1035 ErrExit:
1036 if (iobuffer) free(iobuffer);
1037 UnlockDB(dbstateptr);
1038
1039 StdEdit:
1040 return result;
1041 }
1042
1043
1044
1045 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
1046 VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
1047
1048 if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
1049
1050 dbstateptr->signature = 0;
1051
1052 close(dbstateptr->dbfile); /* Nothing we can do about any errors... */
1053 dbstateptr->dbfile = 0;
1054
1055 free(dbstateptr);
1056
1057 return 0;
1058 }
1059
1060
1061
1062 /******************************************************************************
1063 *
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
1065 *
1066 *****************************************************************************/
1067
1068 static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
1069 return flock(dbstateptr->dbfile, lockmode);
1070 }
1071
1072
1073
1074 static int UnlockDB(VSDBStatePtr dbstateptr) {
1075 return flock(dbstateptr->dbfile, LOCK_UN);
1076 }
1077
1078
1079
1080 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, u_int32_t options) {
1081 struct VSDBKey searchkey;
1082 struct VSDBEntry dbentry;
1083 int result;
1084
1085 FormatDBKey(volumeID, &searchkey);
1086 lseek(dbstateptr->dbfile, 0, SEEK_SET);
1087
1088 do {
1089 result = GetVSDBEntry(dbstateptr, &dbentry);
1090 if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
1091 if (targetEntry != NULL) {
1092 memcpy(targetEntry, &dbentry, sizeof(*targetEntry));
1093 };
1094 return 0;
1095 };
1096 } while (result == 0);
1097
1098 return -1;
1099 }
1100
1101
1102
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));
1106 }
1107
1108
1109
1110
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));
1114 }
1115
1116
1117
1118 static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
1119 struct VSDBEntry entry;
1120 int result;
1121
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)) {
1128 return -1;
1129 };
1130
1131 memcpy(dbentry, &entry, sizeof(*dbentry));
1132 return 0;
1133 };
1134
1135
1136
1137 static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) {
1138 return memcmp(key1->uuidString, key2->uuidString, sizeof(key1->uuidString));
1139 }
1140
1141
1142
1143 /******************************************************************************
1144 *
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
1146 *
1147 *****************************************************************************/
1148
1149 static void FormatULong(u_int32_t u, char *s) {
1150 u_int32_t d;
1151 int i;
1152 char *digitptr = s;
1153
1154 for (i = 0; i < 8; ++i) {
1155 d = ((u & 0xF0000000) >> 28) & 0x0000000F;
1156 if (d < 10) {
1157 *digitptr++ = (char)(d + '0');
1158 } else {
1159 *digitptr++ = (char)(d - 10 + 'A');
1160 };
1161 u = u << 4;
1162 };
1163 }
1164
1165
1166
1167 static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) {
1168 uuid_string_t uuid_str;
1169
1170 uuid_unparse(volumeID->uuid, uuid_str);
1171 memcpy(dbkey->uuidString, uuid_str, sizeof(dbkey->uuidString));
1172 }
1173
1174
1175
1176 static void FormatDBRecord(u_int32_t volumeStatusFlags, struct VSDBRecord *dbrecord) {
1177 FormatULong(volumeStatusFlags, dbrecord->statusFlags);
1178 }
1179
1180
1181
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;
1188 }
1189
1190
1191
1192 static u_int32_t ConvertHexStringToULong(const char *hs, long maxdigits) {
1193 int i;
1194 char c;
1195 u_int32_t nextdigit;
1196 u_int32_t n;
1197
1198 n = 0;
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;
1206 } else {
1207 nextdigit = 0;
1208 };
1209 n = (n << 4) + nextdigit;
1210 };
1211
1212 return n;
1213 }