#include <libc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <dev/disk.h>
#include <errno.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <unistd.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <fts.h>
#include <mach/boolean.h>
#include "DiskArbitrationTypes.h"
#include "DiskVolume.h"
#include "Configuration.h"
#include "DiskArbitrationServerMain.h"
#include "FSParticular.h"
#include "FSTableLookup.h"
extern void DiskArbRefresh_rpc(mach_port_t server);
static void
handleSIGHUP(int signal)
{
dwarning(("Recieved Sighup - Refreshing autodiskmount\n"));
DiskArbRefresh_rpc(0);
}
int
GidForConsoleUser()
{
int gid = 20;
int uid = 0;
int realGid = 0;
CFStringRef name = SCDynamicStoreCopyConsoleUser(NULL, &uid, &realGid);
if (name) {
gid = realGid;
CFRelease(name);
}
return gid;
}
int
UidForConsoleUser()
{
int uid = -1;
int gid = 0;
CFStringRef name = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid);
if (name) {
CFRelease(name);
} else {
uid = -1;
}
return uid;
}
void
CleanupDirectory(char *dir)
{
FTS * ftsp;
FTSENT *chp;
const char *rootDir[] = {dir, 0};
char cookieFile[MAXPATHLEN];
char path[MAXPATHLEN];
struct stat sb;
ftsp = fts_open((char * const *)rootDir, FTS_PHYSICAL, NULL);
fts_read(ftsp);
chp = fts_children(ftsp, 0);
if (chp) {
do {
sprintf(cookieFile, "%s/%s/%s", dir, chp->fts_accpath, ADM_COOKIE_FILE);
sprintf(path, "%s/%s", dir, chp->fts_accpath);
if (chp->fts_info & FTS_D) {
if (stat(cookieFile, &sb) == 0) {
if (remove(cookieFile) == 0) {
if (rmdir(path) == 0) {
dwarning(("*** The directory %s was cleaned up and removed by autodiskmount prior to mounting ***\n", chp->fts_name));
continue;
}
}
}
}
} while ((chp = chp->fts_link));
}
fts_close(ftsp);
}
int
string_to_argv(char * str, char * * argv, int n)
{
int count;
char * scan;
if (str == NULL)
return (0);
for (count = 0, scan = str; count < n; ) {
char * space;
argv[count++] = scan;
space = strchr(scan, ' ');
if (space == NULL)
break;
*space = '\0';
scan = space + 1;
}
return (count);
}
boolean_t
fsck_vols(DiskVolumesPtr vols)
{
boolean_t result = TRUE;
int i;
for (i = 0; i < DiskVolumes_count(vols); i++) {
DiskVolumePtr vol = (DiskVolumePtr) DiskVolumes_objectAtIndex(vols, i);
if (!vol) {
return FALSE;
}
if ((vol->writable || shouldFsckReadOnlyMedia()) && vol->dirty && vol->mount_point == NULL) {
#define NUM_ARGV 6
const char * argv[NUM_ARGV] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
int argc;
DiskPtr dp = LookupDiskByIOBSDName(vol->disk_dev_name);
char *fsckCmd = repairPathForFileSystem(vol->fs_type);
char *rprCmd = repairArgsForFileSystem(vol->fs_type);
char devpath[64];
int ret;
snprintf(devpath, sizeof(devpath), "/dev/r%s", vol->disk_dev_name);
argv[0] = fsckCmd;
argc = string_to_argv(rprCmd, (char * *)argv + 1, NUM_ARGV - 3);
argv[1 + argc] = devpath;
dwarning(("sending checking messages\n"));
SendDiskWillBeCheckedMessages(dp);
if (do_exec(NULL, argv, &ret, NULL) == FALSE) {
dwarning(("*** vol dirty? ***\n"));
vol->dirty = FALSE;
vol->run_quotacheck = TRUE;
ret = 0;
}
else if (ret == 0) {
vol->dirty = FALSE;
vol->run_quotacheck = TRUE;
}
else {
dwarning(("'%s' failed: %d\n", fsckCmd, ret));
StartUnmountableDiskThread(dp);
dp->flags |= kDiskArbDiskAppearedCheckFailed;
}
dwarning(("*** result? ***\n"));
result = result && (ret == 0);
dwarning(("*** freeing? ***\n"));
free(fsckCmd);
free(rprCmd);
} } return result;
}
boolean_t
quotacheck_vols(DiskVolumesPtr vols)
{
boolean_t result = TRUE;
int i;
for (i = 0; i < DiskVolumes_count(vols); i++) {
DiskVolumePtr vol = (DiskVolumePtr) DiskVolumes_objectAtIndex(vols, i);
if (!vol) {
return FALSE;
}
if (vol->run_quotacheck == FALSE)
continue;
vol->run_quotacheck = FALSE;
if (vol->mounted && vol->mount_point && vol->writable) {
const char * argv[] = {
NULL,
NULL,
NULL,
NULL,
NULL
};
int ret;
argv[0] = "/sbin/quotacheck";
argv[1] = "-g";
argv[2] = "-u";
argv[3] = vol->mount_point;
dwarning(("check quotas on %s\n", vol->mount_point));
if (do_exec(NULL, argv, &ret, NULL) == FALSE) {
dwarning(("*** vol quotas checked? ***\n"));
ret = 0;
}
dwarning(("*** quota check result? ***\n"));
result = result && (ret == 0);
} } return result;
}
boolean_t
mount_vols(DiskVolumesPtr vols, int takeOwnership) {
boolean_t fstabIsFresh = FALSE;
int i;
dwarning(("*** mount_vols ***\n"));
for (i = 0; i < DiskVolumes_count(vols); i++) {
DiskVolumePtr d = (DiskVolumePtr) DiskVolumes_objectAtIndex(vols, i);
DiskPtr dp = LookupDiskByIOBSDName(d->disk_dev_name);
if (d->mounted) {
if (dp) {
DiskSetMountpoint(dp, d->mount_point);
if (takeOwnership || dp->forceTakeOwnershipOnDisk) {
if ((dp->forceTakeOwnershipOnDisk || dp->mountedUser == -1) && currentConsoleUser != -1) {
int gid = GidForConsoleUser();
dp->mountedUser = currentConsoleUser;
dwarning(("*** Someone should own the disks now %d, %s ***\n", dp->mountedUser, d->mount_point));
chown(d->mount_point, dp->mountedUser, gid);
dp->forceTakeOwnershipOnDisk = 0;
}
}
DiskVolume_SetTrashes(d);
}
continue;
}
if (d->dirty) {
continue;
}
if (dp->state == kDiskStateNew && !dp->approvedForMounting) {
SetStateForOnePartition(dp, kDiskStateWaitingForMountApproval);
PrepareToSendPreMountMsgsForDisk(dp);
continue;
}
if (fstabIsFresh == FALSE) {
FSTableLookup_readFstab();
fstabIsFresh = TRUE;
}
if (DiskVolumes_setVolumeMountPoint(vols, d) == FALSE) {
continue;
}
if (d->mount_point == NULL || strlen(d->mount_point) == 0) {
continue;
}
if (DiskVolume_mount(d)) {
if (dp) {
DiskSetMountpoint(dp, d->mount_point);
if (takeOwnership || dp->forceTakeOwnershipOnDisk) {
if ((dp->forceTakeOwnershipOnDisk || dp->mountedUser == -1) && currentConsoleUser != -1) {
int gid = GidForConsoleUser();
dp->mountedUser = currentConsoleUser;
dwarning(("*** Someone should own the disks now %d, %s ***\n", dp->mountedUser, d->mount_point));
chown(d->mount_point, dp->mountedUser, gid);
dp->forceTakeOwnershipOnDisk = 0;
}
}
DiskVolume_SetTrashes(d);
}
}
}
return (TRUE);
}
void DiskArbDiskAppearedRecognizableSectionMountedApplier(DiskPtr wholePtr) {
wholePtr->flags = wholePtr->flags | kDiskArbDiskAppearedRecognizableSectionMounted;
}
void
autodiskmount(int takeOwnership)
{
DiskVolumesPtr vols;
DiskPtr diskPtr;
DiskVolumes_new(&vols);
CleanupDirectory(mountPath());
DiskVolumes_do_volumes(vols);
if (fsck_vols(vols) == FALSE) {
pwarning(("Some fsck failed!\n"));
}
if (mount_vols(vols, takeOwnership) == FALSE) {
pwarning(("Some mount() failed!\n"));
}
if (quotacheck_vols(vols) == FALSE) {
pwarning(("Some quotacheck failed!\n"));
}
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next) {
if ((diskPtr->mountpoint != NULL &&
strlen(diskPtr->mountpoint)) || diskPtr->state == kDiskStateWaitingForMountApproval) {
LookupWholeDisksForThisPartition(diskPtr->service, DiskArbDiskAppearedRecognizableSectionMountedApplier);
}
}
if (g.verbose) {
DiskVolumes_print(vols);
}
DiskVolumes_delete(vols);
}
boolean_t
autodiskmount_findDisk(boolean_t all, const char * volume_name)
{
boolean_t found = TRUE;
DiskVolumesPtr vols;
DiskVolumes_new(&vols);
DiskVolumes_do_volumes(vols);
if (g.verbose) {
DiskVolumes_print(vols);
}
else {
(void)fsck_vols(vols);
found = DiskVolumes_findDisk(vols, all, volume_name);
}
DiskVolumes_delete(vols);
return (found);
}
GlobalStruct g;
void setReadOnlyStatus()
{
struct statfs *mntbuf;
int numMounts;
int index;
if ((numMounts = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
dwarning(("getmntinfo() call failed!\n"));
}
for (index=0; index < numMounts; index++) {
if (strcmp(mntbuf[index].f_mntonname, "/") != 0) {
continue;
} else {
g.readOnlyBoot = (mntbuf[index].f_flags & MNT_RDONLY);
if (g.readOnlyBoot) {
pwarning(("autodiskmount running on read only file system\n"));
}
}
}
}
int
main(int argc, char * argv[])
{
const char * volume_name = NULL;
char * progname;
char ch;
boolean_t find = FALSE;
boolean_t all = FALSE;
g.Clients = NULL;
g.NumClients = 0;
g.Disks = NULL;
g.NumDisks = 0;
g.NumDisksAddedOrDeleted = 1;
g.verbose = FALSE;
g.debug = FALSE;
progname = argv[0];
if (getuid() != 0) {
pwarning(("%s: must be run as root\n", progname));
exit(1);
}
while ((ch = getopt(argc, argv, "avdFV:")) != -1) {
switch (ch) {
case 'a':
all = TRUE;
break;
case 'v':
g.verbose = TRUE;
break;
case 'd':
g.debug = TRUE;
break;
case 'F':
find = TRUE;
break;
case 'V':
volume_name = optarg;
break;
}
}
if (find == TRUE) {
extern int findDiskInit();
if (findDiskInit() < 0) {
exit(2);
}
if (autodiskmount_findDisk(all, volume_name) == FALSE) {
exit(1);
}
exit(0);
}
signal(SIGHUP, handleSIGHUP);
setReadOnlyStatus();
{
char *argv2[] = {argv[0], "-d"};
int argc2 = (g.debug ? 2 : 1);
(void) DiskArbitrationServerMain(argc2, argv2);
}
exit(0);
}