#ifndef lint
static const char rcsid[] =
"$FreeBSD: src/sbin/mount_msdos/mount_msdos.c,v 1.19 2000/01/08 16:47:55 ache Exp $";
#endif
#include <stdint.h>
#include <mach/machine/boolean.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "../msdosfs.kextproj/msdosfs.kmodproj/msdosfsmount.h"
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <fcntl.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFStringEncodingExt.h>
#define Radar_2238317 1
#if ! Radar_2238317
#include <mntopts.h>
#else // Radar_2238317
struct mntopt {
const char *m_option;
int m_inverse;
int m_flag;
int m_altloc;
};
#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 }
#define MOPT_NODEV { "dev", 1, MNT_NODEV, 0 }
#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC, 0 }
#define MOPT_NOSUID { "suid", 1, MNT_NOSUID, 0 }
#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY, 0 }
#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS, 0 }
#define MOPT_UNION { "union", 0, MNT_UNION, 0 }
#define MOPT_USERQUOTA { "userquota", 0, 0, 0 }
#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 }
#define MOPT_PERMISSIONS { "perm", 1, MNT_UNKNOWNPERMISSIONS, 0 }
#define MOPT_BROWSE { "browse", 1, MNT_DONTBROWSE, 0 }
#define MOPT_AUTOMOUNTED { "automounted",0, MNT_AUTOMOUNTED, 0 }
#define MOPT_DEFWRITE { "defwrite", 0, MNT_DEFWRITE, 0}
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 }
#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 }
#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 }
#define MOPT_AUTO { "auto", 0, 0, 0 }
#define MOPT_FSTAB_COMPAT \
MOPT_RO, \
MOPT_RW, \
MOPT_AUTO
#define MOPT_STDOPTS \
MOPT_USERQUOTA, \
MOPT_GROUPQUOTA, \
MOPT_FSTAB_COMPAT, \
MOPT_NODEV, \
MOPT_NOEXEC, \
MOPT_NOSUID, \
MOPT_RDONLY, \
MOPT_UNION, \
MOPT_PERMISSIONS, \
MOPT_BROWSE
void getmntopts __P((const char *, const struct mntopt *, int *, int *));
void checkpath __P((const char *, char resolved_path[]));
void rmslashes __P((char *, char *));
extern int getmnt_silent;
#endif // Radar_2238317
static struct mntopt mopts[] = {
MOPT_STDOPTS,
MOPT_FORCE,
MOPT_SYNC,
MOPT_UPDATE,
{ NULL }
};
static gid_t a_gid __P((char *));
static uid_t a_uid __P((char *));
static mode_t a_mask __P((char *));
static void usage __P((void));
static int checkLoadable();
static char *progname;
static int load_kmod();
static void FindVolumeName(struct msdosfs_args *args);
#define DEBUG 0
#if DEBUG
#define dprintf(x) printf x;
#else
#define dprintf(x) ;
#endif
int
main(argc, argv)
int argc;
char **argv;
{
struct msdosfs_args args;
struct stat sb;
int c, mntflags, set_gid, set_uid, set_mask;
char *dev, *dir, mntpath[MAXPATHLEN];
struct timezone local_tz;
mntflags = set_gid = set_uid = set_mask = 0;
(void)memset(&args, '\0', sizeof(args));
args.magic = MSDOSFS_ARGSMAGIC;
progname = argv[0];
while ((c = getopt(argc, argv, "sl9u:g:m:o:")) != -1) {
switch (c) {
case 'u':
args.uid = a_uid(optarg);
set_uid = 1;
break;
case 'g':
args.gid = a_gid(optarg);
set_gid = 1;
break;
case 'm':
args.mask = a_mask(optarg);
set_mask = 1;
break;
case 'o':
getmntopts(optarg, mopts, &mntflags, &args.flags);
break;
case '?':
default:
usage();
break;
}
}
if (optind + 2 != argc)
usage();
dev = argv[optind];
dir = argv[optind + 1];
(void)checkpath(dir, mntpath);
(void)rmslashes(dev, dev);
if (!set_gid || !set_uid || !set_mask) {
if (stat(mntpath, &sb) == -1)
err(EX_OSERR, "stat %s", mntpath);
if (!set_uid)
args.uid = sb.st_uid;
if (!set_gid)
args.gid = sb.st_gid;
if (!set_mask)
args.mask = sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
if (!set_gid && !set_uid && !set_mask) {
args.mask = ACCESSPERMS;
mntflags |= MNT_UNKNOWNPERMISSIONS;
}
}
args.fspec = dev;
gettimeofday(NULL, &local_tz);
args.secondsWest = local_tz.tz_minuteswest * 60 -
(local_tz.tz_dsttime ? 3600 : 0);
args.flags |= MSDOSFSMNT_SECONDSWEST;
FindVolumeName(&args);
if (checkLoadable())
if (load_kmod())
errx(EX_OSERR, "msdos filesystem is not available");
if (mount("msdos", mntpath, mntflags, &args) < 0)
err(EX_OSERR, "%s on %s", dev, mntpath);
exit (0);
}
gid_t
a_gid(s)
char *s;
{
struct group *gr;
char *gname;
gid_t gid;
if ((gr = getgrnam(s)) != NULL)
gid = gr->gr_gid;
else {
for (gname = s; *s && isdigit(*s); ++s);
if (!*s)
gid = atoi(gname);
else
errx(EX_NOUSER, "unknown group id: %s", gname);
}
return (gid);
}
uid_t
a_uid(s)
char *s;
{
struct passwd *pw;
char *uname;
uid_t uid;
if ((pw = getpwnam(s)) != NULL)
uid = pw->pw_uid;
else {
for (uname = s; *s && isdigit(*s); ++s);
if (!*s)
uid = atoi(uname);
else
errx(EX_NOUSER, "unknown user id: %s", uname);
}
return (uid);
}
mode_t
a_mask(s)
char *s;
{
int done, rv;
char *ep;
done = 0;
rv = -1;
if (*s >= '0' && *s <= '7') {
done = 1;
rv = strtol(optarg, &ep, 8);
}
if (!done || rv < 0 || *ep)
errx(EX_USAGE, "invalid file mode: %s", s);
return (rv);
}
void
usage()
{
fprintf(stderr, "%s\n%s\n",
"usage: mount_msdos [-o options] [-u user] [-g group] [-m mask]",
" [-s] [-l] [-9] bdev dir");
exit(EX_USAGE);
}
#define FS_TYPE "msdos"
static int checkLoadable(void)
{
int error;
struct vfsconf vfc;
error = getvfsbyname(FS_TYPE, &vfc);
return error;
}
#define LOAD_COMMAND "/sbin/kextload"
#define MSDOS_MODULE_PATH "/System/Library/Extensions/msdosfs.kext"
static int load_kmod()
{
int pid;
int result = -1;
union wait status;
pid = fork();
if (pid == 0) {
result = execl(LOAD_COMMAND, LOAD_COMMAND, MSDOS_MODULE_PATH,NULL);
goto Err_Exit;
}
if (pid == -1) {
result = errno;
goto Err_Exit;
}
if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status))) {
result = status.w_retcode;
}
else {
result = -1;
}
Err_Exit:
return (result);
}
static int
oklabel(const char *src)
{
int c, i;
for (i = 0, c = 0; i <= 11; i++) {
c = (u_char)*src++;
if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
break;
}
return i && !c;
}
static CFStringEncoding GetDefaultDOSEncoding(void)
{
CFStringEncoding encoding;
struct passwd *passwdp;
int fd;
size_t size;
char buffer[MAXPATHLEN + 1];
encoding = kCFStringEncodingMacRoman;
if ((passwdp = getpwuid(getuid()))) {
strcpy(buffer, passwdp->pw_dir);
strcat(buffer, "/.CFUserTextEncoding");
if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
size = read(fd, buffer, MAXPATHLEN);
buffer[(size < 0 ? 0 : size)] = '\0';
close(fd);
encoding = strtol(buffer, NULL, 0);
}
}
switch (encoding) {
case kCFStringEncodingMacRoman:
encoding = kCFStringEncodingDOSLatin1;
break;
case kCFStringEncodingMacJapanese:
encoding = kCFStringEncodingDOSJapanese;
break;
case kCFStringEncodingMacChineseTrad:
encoding = kCFStringEncodingDOSChineseTrad;
break;
case kCFStringEncodingMacKorean:
encoding = kCFStringEncodingDOSKorean;
break;
case kCFStringEncodingMacArabic:
encoding = kCFStringEncodingDOSArabic;
break;
case kCFStringEncodingMacHebrew:
encoding = kCFStringEncodingDOSHebrew;
break;
case kCFStringEncodingMacGreek:
encoding = kCFStringEncodingDOSGreek;
break;
case kCFStringEncodingMacCyrillic:
case kCFStringEncodingMacUkrainian:
encoding = kCFStringEncodingDOSCyrillic;
break;
case kCFStringEncodingMacThai:
encoding = kCFStringEncodingDOSThai;
break;
case kCFStringEncodingMacChineseSimp:
encoding = kCFStringEncodingDOSChineseSimplif;
break;
case kCFStringEncodingMacCentralEurRoman:
case kCFStringEncodingMacCroatian:
case kCFStringEncodingMacRomanian:
encoding = kCFStringEncodingDOSLatin2;
break;
case kCFStringEncodingMacTurkish:
encoding = kCFStringEncodingDOSTurkish;
break;
case kCFStringEncodingMacIcelandic:
encoding = kCFStringEncodingDOSIcelandic;
break;
case kCFStringEncodingMacFarsi:
encoding = kCFStringEncodingDOSArabic;
break;
default:
encoding = kCFStringEncodingInvalidId;
break;
}
return encoding;
}
#define MAX_DOS_BLOCKSIZE 2048
struct dosdirentry {
u_int8_t name[11];
u_int8_t attr;
u_int8_t reserved;
u_int8_t createTimeTenth;
u_int16_t createTime;
u_int16_t createDate;
u_int16_t accessDate;
u_int16_t clusterHi;
u_int16_t modTime;
u_int16_t modDate;
u_int16_t clusterLo;
u_int32_t size;
};
#define ATTR_VOLUME_NAME 0x08
#define ATTR_VOLUME_MASK 0x18
#define ATTR_LONG_NAME 0x0F
#define ATTR_MASK 0x3F
#define SLOT_EMPTY 0x00
#define SLOT_DELETED 0xE5U
#define SLOT_E5 0x05
#define CLUST_FIRST 2
#define CLUST_RESERVED 0x0FFFFFF7
static void FindVolumeName(struct msdosfs_args *args)
{
int fd;
u_int32_t i;
struct dosdirentry *dir;
void *rootBuffer;
unsigned bytesPerSector;
unsigned sectorsPerCluster;
unsigned rootDirEntries;
unsigned reservedSectors;
unsigned numFATs;
u_int32_t sectorsPerFAT;
off_t readOffset;
ssize_t readAmount;
unsigned char buf[MAX_DOS_BLOCKSIZE];
unsigned char label[12];
CFStringRef cfstr;
bzero(label, sizeof(label));
rootBuffer = NULL;
fd = open(args->fspec, O_RDONLY, 0);
if (fd<0)
err(EX_OSERR, "%s", args->fspec);
if (pread(fd, buf, MAX_DOS_BLOCKSIZE, 0) != MAX_DOS_BLOCKSIZE)
err(EX_OSERR, "%s", args->fspec);
bytesPerSector = buf[11] + buf[12]*256;
if (bytesPerSector < 512 || bytesPerSector > 2048 || (bytesPerSector & (bytesPerSector-1)))
errx(EX_OSERR, "Unsupported sector size (%u)", bytesPerSector);
sectorsPerCluster = buf[13];
if (sectorsPerCluster==0 || (sectorsPerCluster & (sectorsPerCluster-1)))
errx(EX_OSERR, "Unsupported sectors per cluster (%u)", sectorsPerCluster);
reservedSectors = buf[14] + buf[15]*256;
numFATs = buf[16];
rootDirEntries = buf[17] + buf[18]*256;
if (rootDirEntries == 0) {
bcopy(&buf[71], label, 11);
} else {
if (buf[38] == 0x29)
bcopy(&buf[43], label, 11);
}
if (rootDirEntries != 0) {
u_int32_t firstRootSector;
sectorsPerFAT = buf[22] + buf[23]*256;
firstRootSector = reservedSectors + numFATs * sectorsPerFAT;
readOffset = firstRootSector * bytesPerSector;
readAmount = (rootDirEntries * sizeof(struct dosdirentry) + bytesPerSector-1) / bytesPerSector;
readAmount *= bytesPerSector;
rootBuffer = malloc(readAmount);
if (rootBuffer == NULL)
errx(EX_OSERR, "Out of memory");
if (pread(fd, rootBuffer, readAmount, readOffset) != readAmount)
err(EX_OSERR, "%s", args->fspec);
for (i=0,dir=rootBuffer; i<rootDirEntries; ++i,++dir) {
if (dir->name[0] == SLOT_EMPTY)
goto end_of_dir;
if (dir->name[0] == SLOT_DELETED)
continue;
if ((dir->attr & ATTR_MASK) == ATTR_LONG_NAME)
continue;
if ((dir->attr & ATTR_VOLUME_MASK) == ATTR_VOLUME_NAME) {
bcopy(dir->name, label, 11);
goto end_of_dir;
}
}
} else {
u_int32_t cluster;
u_int32_t clusterOffset;
sectorsPerFAT = buf[36] + (buf[37]<<8L) + (buf[38]<<16L) + (buf[39]<<24L);
clusterOffset = reservedSectors + numFATs * sectorsPerFAT;
readAmount = bytesPerSector * sectorsPerCluster;
rootBuffer = malloc(readAmount);
if (rootBuffer == NULL)
errx(EX_OSERR, "Out of memory");
rootDirEntries = readAmount / sizeof(struct dosdirentry);
cluster = buf[44] + (buf[45]<<8L) + (buf[46]<<16L) + (buf[47]<<24L);
while (cluster >= CLUST_FIRST && cluster < CLUST_RESERVED) {
readOffset = (cluster - CLUST_FIRST) * sectorsPerCluster + clusterOffset;
readOffset *= bytesPerSector;
if (pread(fd, rootBuffer, readAmount, readOffset) != readAmount)
err(EX_OSERR, "%s", args->fspec);
for (i=0,dir=rootBuffer; i<rootDirEntries; ++i,++dir) {
if (dir->name[0] == SLOT_EMPTY)
goto end_of_dir;
if (dir->name[0] == SLOT_DELETED)
continue;
if ((dir->attr & ATTR_MASK) == ATTR_LONG_NAME)
continue;
if ((dir->attr & ATTR_VOLUME_MASK) == ATTR_VOLUME_NAME) {
bcopy(dir->name, label, 11);
goto end_of_dir;
}
}
readOffset = reservedSectors + ((cluster * 4) / bytesPerSector);
readOffset *= bytesPerSector;
if (pread(fd, buf, bytesPerSector, readOffset) != bytesPerSector)
err(EX_OSERR, "%s", args->fspec);
i = (cluster * 4) % bytesPerSector;
cluster = buf[i] + (buf[i+1]<<8L) + (buf[i+2]<<16L) + (buf[i+3]<<24L);
cluster &= 0x0FFFFFFF;
}
}
end_of_dir:
if (rootBuffer)
free(rootBuffer);
close(fd);
if (label[0] == 0x05)
label[0] = 0xE5;
if (!oklabel(label))
label[0] = 0;
i = 11;
do {
--i;
if (label[i] == ' ')
label[i] = 0;
else
break;
} while (i != 0);
cfstr = CFStringCreateWithCString(NULL, label, GetDefaultDOSEncoding());
if (cfstr == NULL)
cfstr = CFStringCreateWithCString(NULL, label, kCFStringEncodingDOSLatin1);
if (cfstr == NULL)
args->label[0] = 0;
else {
CFStringGetCString(cfstr, args->label, sizeof(args->label), kCFStringEncodingUTF8);
CFRelease(cfstr);
}
args->flags |= MSDOSFSMNT_LABEL;
}