Logo Search packages:      
Sourcecode: hfsprogs version File versions  Download package

vsdbutil_main.c

/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
      About vsdbutil.c:
            Contains code to manipulate the volume status DB (/var/db/volinfo.database).

      Change History:
      18-Apr-2000 Pat Dirks   New Today.

 */


/* ************************************** I N C L U D E S ***************************************** */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/ucred.h>
#include <sys/resource.h>
#include <sys/vmmeter.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <dirent.h>
#include <strings.h>

#include <sys/attr.h>

#include <openssl/sha.h>
#include <architecture/byte_order.h>

struct FinderAttrBuf {
      unsigned long info_length;
      unsigned long finderinfo[8];
};

static char usage[] = "Usage: %s [-a path] | [-c path ] [-d path] [-i]\n";

static char gHFSTypeName[] = "hfs";

/*****************************************************************************
 *
 * The following should really be in some system header file:
 *
 *****************************************************************************/

#define VOLUMEUUIDVALUESIZE 2
typedef union VolumeUUID {
      unsigned long value[VOLUMEUUIDVALUESIZE];
      struct {
            unsigned long high;
            unsigned long low;
      } v;
} VolumeUUID;

#define VOLUMEUUIDLENGTH 16
typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1];

#define VOLUME_RECORDED 0x80000000
#define VOLUME_USEPERMISSIONS 0x00000001
#define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )

typedef void *VolumeStatusDBHandle;

void GenerateVolumeUUID(VolumeUUID *newVolumeID);
void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID);
void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString);

int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr);
int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus);
int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus);
int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID);
int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle);

/*****************************************************************************
 *
 * Internal function prototypes:
 *
 *****************************************************************************/

static void check_uid(void);
static int GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr, boolean_t generate);
static int SetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr);
static int AdoptAllLocalVolumes(void);
static int AdoptVolume(const char *path);
static int DisownVolume(const char *path);
static int ClearVolumeUUID(const char *path);
static int DisplayVolumeStatus(const char *path);
static int UpdateMountStatus(const char *path, unsigned long volstatus);


int main (int argc, const char *argv[])
{
      int arg;
      char option;
      int result = 0;

      if (argc < 2) {
            fprintf(stderr, usage, argv[0]);
            exit(1);
      };

      for (arg = 1; arg < argc; ++arg) {
            if ((argv[arg][0] == '-') &&
                  ((option = argv[arg][1]) != (char)0) &&
                  (argv[arg][2] == (char)0)) {
                  switch (option) {
                        case 'a':
                        case 'A':
                              /* Pick out the pathname argument: */
                              if (++arg >= argc) {
                                    fprintf(stderr, usage, argv[0]);
                                    exit(1);
                              }
                              
                              check_uid();
                              result = AdoptVolume(argv[arg]);
                              break;

                        case 'c':
                        case 'C':
                              /* Pick out the pathname argument: */
                              if (++arg >= argc) {
                                    fprintf(stderr, usage, argv[0]);
                                    exit(1);
                              };

                              result = DisplayVolumeStatus(argv[arg]);
                              break;
                        
                        case 'd':
                        case 'D':
                              /* Pick out the pathname argument: */
                              if (++arg >= argc) {
                                    fprintf(stderr, usage, argv[0]);
                                    exit(1);
                              };

                              check_uid();
                              result = DisownVolume(argv[arg]);
                              break;
                        
                        case 'h':
                        case 'H':
                              printf(usage, argv[0]);
                              printf("where\n");
                              printf("\t-a adopts (activates) on-disk permissions on the specified path,\n");
                              printf("\t-c checks the status of the permissions usage on the specified path\n");
                              printf("\t-d disowns (deactivates) the on-disk permissions on the specified path\n");
                              printf("\t-i initializes the permissions database to include all mounted HFS/HFS+ volumes\n");
                              break;
                        
                        case 'i':
                        case 'I':
                              check_uid();
                              result = AdoptAllLocalVolumes();
                              break;
                        
                        case 'x':
                        case 'X':
                              /* Pick out the pathname argument: */
                              if (++arg >= argc) {
                                    fprintf(stderr, usage, argv[0]);
                                    exit(1);
                              };

                              check_uid();
                              result = ClearVolumeUUID(argv[arg]);
                              break;
                              
                        default:
                              fprintf(stderr, usage, argv[0]);
                              exit(1);
                  }
            }
      }

      if (result < 0) result = 1;         // Make sure only positive exit status codes are generated
      
      exit(result);                             // ensure the process exit status is returned
      return result;                      // ...and make main fit the ANSI spec.
}



static void check_uid(void) {
      if (getuid() != 0) {
            fprintf(stderr, "###\n");
            fprintf(stderr, "### You must be root to perform this operation.\n");
            fprintf(stderr, "###\n");
            exit(1);
      };
}



/*
 --   UpdateMountStatus
 --
 --   Returns: error code (0 if successful).
 */
static int
UpdateMountStatus(const char *path, unsigned long volstatus) {
      struct statfs mntstat;
      int result;
      union wait status;
      int pid;
      int nosuid;
      int nodev;
      int noexec;
      int readonly;
      char mountline[255];

      result = statfs(path, &mntstat);
      if (result != 0) {
            warn("couldn't look up mount status for '%s'", path);
            return errno;
      };

      nosuid = mntstat.f_flags & MNT_NOSUID;
      nodev = mntstat.f_flags & MNT_NODEV;
      noexec = mntstat.f_flags & MNT_NOEXEC;
      readonly = mntstat.f_flags & MNT_RDONLY;

      sprintf(mountline, "%s%s%s%s%s", (volstatus & VOLUME_USEPERMISSIONS) ? "perm" : "noperm",(nosuid)?",nosuid":"",(nodev)?",nodev":"",(noexec)?",noexec":"",(readonly)?",rdonly":"");

      pid = fork();
      if (pid == 0) {
            result = execl("/sbin/mount", "mount",
                                    "-t", mntstat.f_fstypename,
                                    "-u","-o", mountline,
                                    mntstat.f_mntfromname, mntstat.f_mntonname, NULL);
#if DEBUG_TRACE
            if (result) {
                  fprintf(stderr, "Error from execl(/sbin/mount...): result = %d.\n", result);
            };
            fprintf(stderr, "UpdateMountStatus in child: returning 1...\n");
#endif
            /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
            return (1);
      }

      if (pid == -1) {
            warn("couldn't fork to execute mount command");
            return errno;
      };

      /* Success! */
      if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status))) {
            result = status.w_retcode;
      } else {
            result = -1;
      };

      return result;
}



/*
 --   AdoptAllLocalVolumes
 --
 --   Returns: error code (0 if successful).
 */
static int
AdoptAllLocalVolumes(void) {
      struct statfs *mntstatptr;
      int fscount;
      
      fscount = getmntinfo(&mntstatptr, MNT_WAIT);
      if (fscount == 0) {
            warn("couldn't get information on mounted volumes");
            return errno;
      };
      
      while (fscount > 0) {
            if (strcmp(mntstatptr->f_fstypename, gHFSTypeName) == 0) {
                  (void)AdoptVolume(mntstatptr->f_mntonname);
            };
            
            ++mntstatptr;
            --fscount;
      };
      
      return 0;
}



/*
 --   AdoptVolume
 --
 --   Returns: error code (0 if successful).
 */
static int
AdoptVolume(const char *path) {
      VolumeUUID targetuuid;
      VolumeStatusDBHandle vsdb;
      unsigned long volstatus;
      int result = 0;

      /* Look up the target volume UUID, generating one if no valid one has been assigned yet: */
      result = GetVolumeUUID(path, &targetuuid, TRUE);
      if (result != 0) {
            warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
            return result;
      };
      
      if ((targetuuid.v.high == 0) || (targetuuid.v.low == 0)) {
            warnx("internal error: incomplete UUID generated.");
            return EINVAL;
      };
      
      /* Open the volume status DB to look up the entry for the volume in question: */
      if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
            warnx("couldn't access volume status database: %s", strerror(result));
            return result;
      };
      
      /* Check to see if an entry exists.  If not, prepare a default initial status value: */
      if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
            volstatus = 0;
      };
      
      /* Enable permissions on the specified volume: */
      volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;

      /* Update the entry in the volume status database: */
      if ((result = SetVolumeStatusDBEntry(vsdb, &targetuuid, volstatus)) != 0) {
            warnx("couldn't update volume status database: %s", strerror(result));
            return result;
      };
      
      (void)CloseVolumeStatusDB(CloseVolumeStatusDB);

      sync();

      if ((result = UpdateMountStatus(path, volstatus)) != 0) {
            warnx("couldn't update mount status of '%s': %s", path, strerror(result));
            return result;
      };
      
      return 0;
}



/*
 --   DisownVolume
 --
 --   Returns: error code (0 if successful).
 */
static int
DisownVolume(const char *path) {
      VolumeUUID targetuuid;
      VolumeStatusDBHandle vsdb;
      unsigned long volstatus;
      int result = 0;

      /* Look up the target volume UUID, generating one if no valid one has been assigned yet: */
      result = GetVolumeUUID(path, &targetuuid, TRUE);
      if (result != 0) {
            warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
            return result;
      };
      
      volstatus = 0;
      if ((targetuuid.v.high != 0) || (targetuuid.v.low != 0)) {
            /* Open the volume status DB to look up the entry for the volume in question: */
            if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
                  warnx("couldn't access volume status database: %s", strerror(result));
                  return result;
            };
            
            /* Check to see if an entry exists.  If not, prepare a default initial status value: */
            if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
                  volstatus = 0;
            };
            
            /* Disable permissions on the specified volume: */
            volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;

            /* Update the entry in the volume status database: */
            if ((result = SetVolumeStatusDBEntry(vsdb, &targetuuid, volstatus)) != 0) {
                  warnx("couldn't update volume status database: %s", strerror(result));
                  return result;
            };
            
            (void)CloseVolumeStatusDB(CloseVolumeStatusDB);

            sync();
      };
      
      if ((result = UpdateMountStatus(path, volstatus)) != 0) {
            warnx("couldn't update mount status of '%s': %s", path, strerror(result));
            return result;
      };
      
      return result;
};



/*
 --   ClearVolumeUUID
 --
 --   Returns: error code (0 if successful).
 */
static int
ClearVolumeUUID(const char *path) {
      VolumeUUID targetuuid;
      VolumeStatusDBHandle vsdb;
      unsigned long volstatus;
      int result = 0;

      /* Check to see whether the target volume has an assigned UUID: */
      result = GetVolumeUUID(path, &targetuuid, FALSE);
      if (result != 0) {
            warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
            return result;
      };
      
      if ((targetuuid.v.high != 0) || (targetuuid.v.low != 0)) {
            /* Open the volume status DB to look up the entry for the volume in question: */
            if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
                  warnx("couldn't access volume status database: %s", strerror(result));
                  return result;
            };
            
            /* Check to see if an entry exists: */
            if (GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus) == 0) {
                  /* Remove the entry from the volume status database: */
                  if ((result = DeleteVolumeStatusDBEntry(vsdb, &targetuuid)) != 0) {
                        warnx("couldn't update volume status database: %s", strerror(result));
                        return result;
                  };
            };
            
            (void)CloseVolumeStatusDB(CloseVolumeStatusDB);

            sync();

            if ((result = UpdateMountStatus(path, 0)) != 0) {
                  warnx("couldn't update mount status of '%s': %s", path, strerror(result));
                  return result;
            };
      
            /* Erase the volume's UUID: */
            targetuuid.v.high = 0;
            targetuuid.v.low = 0;
            result = SetVolumeUUID(path, &targetuuid);
      
      };

      return result;
};



/*
 --   DisplayVolumeStatus
 --
 --   Returns: error code (0 if successful).
 */
static int
DisplayVolumeStatus(const char *path) {
      VolumeUUID targetuuid;
      VolumeStatusDBHandle vsdb;
      unsigned long volstatus;
      int result = 0;

      /* Look up the target volume UUID, exactly as stored on disk: */
      result = GetVolumeUUID(path, &targetuuid, FALSE);
      if (result != 0) {
            warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
            return result;
      };
      
      if ((targetuuid.v.high == 0) || (targetuuid.v.low == 0)) {
            warnx("no valid volume UUID found on '%s': permissions are disabled.", path);
            return 0;
      };
      
      /* Open the volume status DB to look up the entry for the volume in question: */
      if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
            warnx("couldn't access volume status database: %s", strerror(result));
            return result;
      };
      
      if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
            printf("No entry found for '%s'.\n", path);
            goto Std_Exit;
      };
      
      if (volstatus & VOLUME_USEPERMISSIONS) {
            printf("Permissions on '%s' are enabled.\n", path);
      } else {
            printf("Permissions on '%s' are disabled.\n", path);
      };

Std_Exit:
      (void)CloseVolumeStatusDB(CloseVolumeStatusDB);
      
      return result;
}



/*
 --   GetVolumeUUID
 --
 --   Returns: error code (0 if successful).
 */

static int
GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr, boolean_t generate) {
      struct attrlist alist;
      struct FinderAttrBuf volFinderInfo;
      VolumeUUID *finderInfoUUIDPtr;
      int result;

      /* Set up the attrlist structure to get the volume's Finder Info: */
      alist.bitmapcount = 5;
      alist.reserved = 0;
      alist.commonattr = ATTR_CMN_FNDRINFO;
      alist.volattr = ATTR_VOL_INFO;
      alist.dirattr = 0;
      alist.fileattr = 0;
      alist.forkattr = 0;

      result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
      if (result) {
            warn("Couldn't get volume information for '%s'", path);
            result = errno;
            goto Err_Exit;
      }

      finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
      if (generate && ((finderInfoUUIDPtr->v.high == 0) || (finderInfoUUIDPtr->v.low == 0))) {
            GenerateVolumeUUID(volumeUUIDPtr);
            finderInfoUUIDPtr->v.high = NXSwapHostLongToBig(volumeUUIDPtr->v.high);
            finderInfoUUIDPtr->v.low = NXSwapHostLongToBig(volumeUUIDPtr->v.low);
            result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
            if (result) {
                  warn("Couldn't update volume information for '%s'", path);
                  result = errno;
                  goto Err_Exit;
            };
      };

      volumeUUIDPtr->v.high = NXSwapBigLongToHost(finderInfoUUIDPtr->v.high);
      volumeUUIDPtr->v.low = NXSwapBigLongToHost(finderInfoUUIDPtr->v.low);
      result = 0;

Err_Exit:
      return result;
};



/*
 --   SetVolumeUUID (needed only to update existing volume UUIDs)
 --
 --   Returns: error code (0 if successful).
 */

static int
SetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr) {
      struct attrlist alist;
      struct FinderAttrBuf volFinderInfo;
      VolumeUUID *finderInfoUUIDPtr;
      int result;

      /* Set up the attrlist structure to get the volume's Finder Info: */
      alist.bitmapcount = 5;
      alist.reserved = 0;
      alist.commonattr = ATTR_CMN_FNDRINFO;
      alist.volattr = ATTR_VOL_INFO;
      alist.dirattr = 0;
      alist.fileattr = 0;
      alist.forkattr = 0;

      result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
      if (result) {
            warn("Couldn't get volume information for '%s'", path);
            result = errno;
            goto Err_Exit;
      }

      finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
      finderInfoUUIDPtr->v.high = NXSwapHostLongToBig(volumeUUIDPtr->v.high);
      finderInfoUUIDPtr->v.low = NXSwapHostLongToBig(volumeUUIDPtr->v.low);
      
      result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
      if (result != 0) {
            warn("Couldn't set volume information for '%s'", path);
            result = errno;
            goto Err_Exit;
      };

Err_Exit:
      return result;
};





/******************************************************************************
 *
 *  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
 *
 *****************************************************************************/

#define DBHANDLESIGNATURE 0x75917737

/* Flag values for operation options: */
#define DBMARKPOSITION 1

static char gVSDBPath[] = "/var/db/volinfo.database";

#define MAXIOMALLOC 16384

/* Database layout: */

struct VSDBKey {
      char uuid[16];
};

struct VSDBRecord {
      char statusFlags[8];
};

struct VSDBEntry {
      struct VSDBKey key;
      char keySeparator;
      char space;
      struct VSDBRecord record;
      char terminator;
};

#define DBKEYSEPARATOR ':'
#define DBBLANKSPACE ' '
#define DBRECORDTERMINATOR '\n'

/* In-memory data structures: */

struct VSDBState {
      unsigned long signature;
      int dbfile;
      int dbmode;
      off_t recordPosition;
};

typedef struct VSDBState *VSDBStatePtr;



/* Internal function prototypes: */
static int LockDB(VSDBStatePtr dbstateptr, int lockmode);
static int UnlockDB(VSDBStatePtr dbstateptr);

static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *dbentry, unsigned long options);
static int AddVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2);

static void FormatULong(unsigned long u, char *s);
static void FormatUUID(VolumeUUID *volumeID, char *UUIDField);
static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey);
static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord);
static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry);
static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits);



/******************************************************************************
 *
 *  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
 *
 *****************************************************************************/

void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
      SHA_CTX context;
      char randomInputBuffer[26];
      unsigned char digest[20];
      time_t now;
      clock_t uptime;
      int mib[2];
      int sysdata;
      char sysctlstring[128];
      size_t datalen;
      double  sysloadavg[3];
      struct vmtotal sysvmtotal;
      
      do {
            /* Initialize the SHA-1 context for processing: */
            SHA1_Init(&context);
            
            /* Now process successive bits of "random" input to seed the process: */
            
            /* The current system's uptime: */
            uptime = clock();
            SHA1_Update(&context, &uptime, sizeof(uptime));
            
            /* The kernel's boot time: */
            mib[0] = CTL_KERN;
            mib[1] = KERN_BOOTTIME;
            datalen = sizeof(sysdata);
            sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
            SHA1_Update(&context, &sysdata, datalen);
            
            /* The system's host id: */
            mib[0] = CTL_KERN;
            mib[1] = KERN_HOSTID;
            datalen = sizeof(sysdata);
            sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
            SHA1_Update(&context, &sysdata, datalen);

            /* The system's host name: */
            mib[0] = CTL_KERN;
            mib[1] = KERN_HOSTNAME;
            datalen = sizeof(sysctlstring);
            sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
            SHA1_Update(&context, sysctlstring, datalen);

            /* The running kernel's OS release string: */
            mib[0] = CTL_KERN;
            mib[1] = KERN_OSRELEASE;
            datalen = sizeof(sysctlstring);
            sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
            SHA1_Update(&context, sysctlstring, datalen);

            /* The running kernel's version string: */
            mib[0] = CTL_KERN;
            mib[1] = KERN_VERSION;
            datalen = sizeof(sysctlstring);
            sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
            SHA1_Update(&context, sysctlstring, datalen);

            /* The system's load average: */
            datalen = sizeof(sysloadavg);
            getloadavg(sysloadavg, 3);
            SHA1_Update(&context, &sysloadavg, datalen);

            /* The system's VM statistics: */
            mib[0] = CTL_VM;
            mib[1] = VM_METER;
            datalen = sizeof(sysvmtotal);
            sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
            SHA1_Update(&context, &sysvmtotal, datalen);

            /* The current GMT (26 ASCII characters): */
            time(&now);
            strncpy(randomInputBuffer, asctime(gmtime(&now)), 26);      /* "Mon Mar 27 13:46:26 2000" */
            SHA1_Update(&context, randomInputBuffer, 26);
            
            /* Pad the accumulated input and extract the final digest hash: */
            SHA1_Final(digest, &context);
      
            memcpy(newVolumeID, digest, sizeof(*newVolumeID));
      } while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0));
}



void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID) {
      int i;
      char c;
      unsigned long nextdigit;
      unsigned long high = 0;
      unsigned long low = 0;
      unsigned long carry;
      
      for (i = 0; (i < VOLUMEUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) {
            if ((c >= '0') && (c <= '9')) {
                  nextdigit = c - '0';
            } else if ((c >= 'A') && (c <= 'F')) {
                  nextdigit = c - 'A' + 10;
            } else if ((c >= 'a') && (c <= 'f')) {
                  nextdigit = c - 'a' + 10;
            } else {
                  nextdigit = 0;
            };
            carry = ((low & 0xF0000000) >> 28) & 0x0000000F;
            high = (high << 4) | carry;
            low = (low << 4) | nextdigit;
      };
      
      volumeID->v.high = high;
      volumeID->v.low = low;
}



void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) {
      FormatUUID(volumeID, UUIDString);
      *(UUIDString+16) = (char)0;         /* Append a terminating null character */
}



int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
      VSDBStatePtr dbstateptr;
      
      *DBHandlePtr = NULL;

      dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr));
      if (dbstateptr == NULL) {
            return ENOMEM;
      };
      
      dbstateptr->dbmode = O_RDWR;
      dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
      if (dbstateptr->dbfile == -1) {
            /*
               The file couldn't be opened for read/write access:
               try read-only access before giving up altogether.
             */
            dbstateptr->dbmode = O_RDONLY;
            dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
            if (dbstateptr->dbfile == -1) {
                  return errno;
            };
      };
      
      dbstateptr->signature = DBHANDLESIGNATURE;
      *DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
      return 0;
}



int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus) {
      VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
      struct VSDBEntry dbentry;
      int result;

      if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;

      if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
      
      if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
            goto ErrExit;
      };
      *VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
      
      result = 0;

ErrExit:
      UnlockDB(dbstateptr);
      return result;
}



int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus) {
      VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
      struct VSDBEntry dbentry;
      int result;
      
      if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
      if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
      
      if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
      
      FormatDBEntry(volumeID, VolumeStatus, &dbentry);
      if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) == 0) {
#if DEBUG_TRACE
            fprintf(stderr,"AddLocalVolumeUUID: found record in database; updating in place.\n");
#endif
            result = UpdateVolumeRecord(dbstateptr, &dbentry);
      } else if (result == -1) {
#if DEBUG_TRACE
            fprintf(stderr,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
#endif
            result = AddVolumeRecord(dbstateptr, &dbentry);
      } else {
            goto ErrExit;
      };
      
      fsync(dbstateptr->dbfile);
      
      result = 0;

ErrExit:    
      UnlockDB(dbstateptr);
      return result;
}



int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
      VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
      struct stat dbinfo;
      int result;
      unsigned long iobuffersize;
      void *iobuffer = NULL;
      off_t dataoffset;
      unsigned long iotransfersize;
      unsigned long bytestransferred;

      if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;

      if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
      
      if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
#if DEBUG_TRACE
            fprintf(stderr, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result);
#endif
            if (result == -1) result = 0; /* Entry wasn't in the database to begin with? */
            goto StdEdit;
      } else {
#if DEBUG_TRACE
            fprintf(stderr, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
#endif
            if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit;
            if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry)) <= MAXIOMALLOC) {
                  iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry);
            } else {
                  iobuffersize = MAXIOMALLOC;
            };
#if DEBUG_TRACE
            fprintf(stderr, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n", 
                                          (unsigned long)dbinfo.st_size, (unsigned long)dbstateptr->recordPosition);
            fprintf(stderr, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize);
#endif
            if (iobuffersize > 0) {
                  iobuffer = malloc(iobuffersize);
                  if (iobuffer == NULL) {
                        result = ENOMEM;
                        goto ErrExit;
                  };
                  
                  dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
                  do {
                        iotransfersize = dbinfo.st_size - dataoffset;
                        if (iotransfersize > 0) {
                              if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
      
#if DEBUG_TRACE
                              fprintf(stderr, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)dataoffset);
#endif
                              lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
                              bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
                              if (bytestransferred != iotransfersize) {
                                    result = errno;
                                    goto ErrExit;
                              };
      
#if DEBUG_TRACE
                              fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)(dataoffset - (off_t)sizeof(struct VSDBEntry)));
#endif
                              lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET);
                              bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize);
                              if (bytestransferred != iotransfersize) {
                                    result = errno;
                                    goto ErrExit;
                              };
                              
                              dataoffset += (off_t)iotransfersize;
                        };
                  } while (iotransfersize > 0);
            };
#if DEBUG_TRACE
            fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry))));
#endif
            if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
                  goto ErrExit;
            };
            
            fsync(dbstateptr->dbfile);
            
            result = 0;
      };

ErrExit:
      if (iobuffer) free(iobuffer);
      UnlockDB(dbstateptr);
      
StdEdit:
      return result;
}



int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
      VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;

      if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;

      dbstateptr->signature = 0;
      
      close(dbstateptr->dbfile);          /* Nothing we can do about any errors... */
      dbstateptr->dbfile = 0;
      
      free(dbstateptr);
      
      return 0;
}



/******************************************************************************
 *
 *  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
 *
 *****************************************************************************/

static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
#if DEBUG_TRACE
      fprintf(stderr, "LockDB: Locking VSDB file...\n");
#endif
      return flock(dbstateptr->dbfile, lockmode);
}

      

static int UnlockDB(VSDBStatePtr dbstateptr) {
#if DEBUG_TRACE
      fprintf(stderr, "UnlockDB: Unlocking VSDB file...\n");
#endif
      return flock(dbstateptr->dbfile, LOCK_UN);
}



static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, unsigned long options) {
      struct VSDBKey searchkey;
      struct VSDBEntry dbentry;
      int result;
      
      FormatDBKey(volumeID, &searchkey);
      lseek(dbstateptr->dbfile, 0, SEEK_SET);
      
      do {
            result = GetVSDBEntry(dbstateptr, &dbentry);
            if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
                  if (targetEntry != NULL) {
#if DEBUG_TRACE
                        fprintf(stderr, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry), &dbentry, targetEntry);
#endif
                        memcpy(targetEntry, &dbentry, sizeof(*targetEntry));
                  };
                  return 0;
            };
      } while (result == 0);
      
      return -1;
}



static int AddVolumeRecord(VSDBStatePtr dbstateptr , struct VSDBEntry *dbentry) {
#if DEBUG_TRACE
      VolumeUUIDString id;
#endif

#if DEBUG_TRACE
      strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
      id[sizeof(dbentry->key.uuid)] = (char)0;
      fprintf(stderr, "AddVolumeRecord: Adding record for UUID #%s...\n", id);
#endif
      lseek(dbstateptr->dbfile, 0, SEEK_END);
      return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry));
}




static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
#if DEBUG_TRACE
      VolumeUUIDString id;
#endif

#if DEBUG_TRACE
      strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
      id[sizeof(dbentry->key.uuid)] = (char)0;
      fprintf(stderr, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id, (unsigned long)dbstateptr->recordPosition);
#endif
      lseek(dbstateptr->dbfile, dbstateptr->recordPosition, SEEK_SET);
#if DEBUG_TRACE
      fprintf(stderr, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry));
#endif
      return write(dbstateptr->dbfile, dbentry, sizeof(*dbentry));
}



static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
      struct VSDBEntry entry;
      int result;
#if DEBUG_TRACE
      VolumeUUIDString id;
#endif
      
      dbstateptr->recordPosition = lseek(dbstateptr->dbfile, 0, SEEK_CUR);
#if 0 // DEBUG_TRACE
      fprintf(stderr, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr->recordPosition);
#endif
      result = read(dbstateptr->dbfile, &entry, sizeof(entry));
      if ((result != sizeof(entry)) ||
            (entry.keySeparator != DBKEYSEPARATOR) ||
            (entry.space != DBBLANKSPACE) ||
            (entry.terminator != DBRECORDTERMINATOR)) {
            return -1;
      };
      
#if DEBUG_TRACE
      strncpy(id, entry.key.uuid, sizeof(entry.key.uuid));
      id[sizeof(entry.key.uuid)] = (char)0;
      fprintf(stderr, "GetVSDBEntry: returning entry for UUID #%s...\n", id);
#endif
      memcpy(dbentry, &entry, sizeof(*dbentry));
      return 0;
};



static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) {
#if 0 // DEBUG_TRACE
      VolumeUUIDString id;

      strncpy(id, key1->uuid, sizeof(key1->uuid));
      id[sizeof(key1->uuid)] = (char)0;
      fprintf(stderr, "CompareVSDBKeys: comparing #%s to ", id);
      strncpy(id, key2->uuid, sizeof(key2->uuid));
      id[sizeof(key2->uuid)] = (char)0;
      fprintf(stderr, "%s (%d.)...\n", id, sizeof(key1->uuid));
#endif
      
      return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid));
}



/******************************************************************************
 *
 *  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
 *
 *****************************************************************************/

static void FormatULong(unsigned long u, char *s) {
      unsigned long d;
      int i;
      char *digitptr = s;

      for (i = 0; i < 8; ++i) {
            d = ((u & 0xF0000000) >> 28) & 0x0000000F;
            if (d < 10) {
                  *digitptr++ = (char)(d + '0');
            } else {
                  *digitptr++ = (char)(d - 10 + 'A');
            };
            u = u << 4;
      };
}



static void FormatUUID(VolumeUUID *volumeID, char *UUIDField) {
      FormatULong(volumeID->v.high, UUIDField);
      FormatULong(volumeID->v.low, UUIDField+8);

};



static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) {
      FormatUUID(volumeID, dbkey->uuid);
}



static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord) {
      FormatULong(volumeStatusFlags, dbrecord->statusFlags);
}



static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry) {
      FormatDBKey(volumeID, &dbentry->key);
      dbentry->keySeparator = DBKEYSEPARATOR;
      dbentry->space = DBBLANKSPACE;
      FormatDBRecord(volumeStatusFlags, &dbentry->record);
#if 0 // DEBUG_TRACE
      dbentry->terminator = (char)0;
      fprintf(stderr, "FormatDBEntry: '%s' (%d.)\n", dbentry, sizeof(*dbentry));
#endif
      dbentry->terminator = DBRECORDTERMINATOR;
}



static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits) {
      int i;
      char c;
      unsigned long nextdigit;
      unsigned long n;
      
      n = 0;
      for (i = 0; (i < 8) && ((c = hs[i]) != (char)0) ; ++i) {
            if ((c >= '0') && (c <= '9')) {
                  nextdigit = c - '0';
            } else if ((c >= 'A') && (c <= 'F')) {
                  nextdigit = c - 'A' + 10;
            } else if ((c >= 'a') && (c <= 'f')) {
                  nextdigit = c - 'a' + 10;
            } else {
                  nextdigit = 0;
            };
            n = (n << 4) + nextdigit;
      };
      
      return n;
}

Generated by  Doxygen 1.6.0   Back to index