1/* 2 * Copyright (c) 2006-2008 Apple Inc. All rights reserved. 3 * 4 * This file contains Original Code and/or Modifications of Original Code as 5 * defined in and that are subject to the Apple Public Source License Version 6 * 2.0 (the 'License'). You may not use this file except in compliance with the 7 * License. 8 * 9 * Please obtain a copy of the License at http://www.opensource.apple.com/apsl/ 10 * and read it before using this file. 11 * 12 * The Original Code and all software distributed under the License are 13 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR 16 * A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations under the 18 * License. 19 */ 20 21#include <sys/mount.h> 22#include <sys/param.h> 23#include <sys/stat.h> 24#include <sys/types.h> 25#include <sys/wait.h> 26 27#include <err.h> 28#include <errno.h> 29#include <mntopts.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <sysexits.h> 34#include <unistd.h> 35 36#include "ntfs.h" 37#include "ntfs_types.h" 38 39static struct mntopt mopts[] = { 40 MOPT_STDOPTS, 41 MOPT_FSTAB_COMPAT, 42 MOPT_ASYNC, 43 MOPT_SYNC, 44 MOPT_FORCE, 45 MOPT_UPDATE, 46 MOPT_RELOAD, 47 { NULL, 0, 0, 0 } 48}; 49 50static void usage(const char *progname) __attribute__((noreturn)); 51static void usage(const char *progname) 52{ 53 errx(EX_USAGE, "usage: %s [-s] [-o options] special-device " 54 "filesystem-node\n", progname); 55} 56 57/** 58 * do_exec - Execute an external command. 59 */ 60static int do_exec(const char *progname, char *const args[]) 61{ 62 pid_t pid; 63 union wait status; 64 int eo; 65 66 pid = fork(); 67 if (pid == -1) { 68 fprintf(stderr, "%s: fork failed: %s\n", progname, 69 strerror(errno)); 70 return -1; 71 } 72 if (!pid) { 73 /* In child process, execute external command. */ 74 (void)execv(args[0], args); 75 /* We only get here if the execv() failed. */ 76 eo = errno; 77 fprintf(stderr, "%s: execv %s failed: %s\n", progname, args[0], 78 strerror(eo)); 79 exit(eo); 80 } 81 /* In parent process, wait for exernal command to finish. */ 82 if (wait4(pid, (int*)&status, 0, NULL) != pid) { 83 fprintf(stderr, "%s: BUG executing %s command.\n", progname, 84 args[0]); 85 return -1; 86 } 87 if (!WIFEXITED(status)) { 88 fprintf(stderr, "%s: %s command aborted by signal %d.\n", 89 progname, args[0], WTERMSIG(status)); 90 return -1; 91 } 92 eo = WEXITSTATUS(status); 93 if (eo) { 94 fprintf(stderr, "%s: %s command failed: %s\n", progname, 95 args[0], strerror(eo)); 96 return -1; 97 } 98 return 0; 99} 100 101static void rmslashes(char *rrpin, char *rrpout) 102{ 103 char *rrpoutstart; 104 105 *rrpout = *rrpin; 106 for (rrpoutstart = rrpout; *rrpin != '\0'; *rrpout++ = *rrpin++) { 107 /* Skip all double slashes. */ 108 while (*rrpin == '/' && *(rrpin + 1) == '/') 109 rrpin++; 110 } 111 /* Remove trailing slash if necessary. */ 112 if (rrpout - rrpoutstart > 1 && *(rrpout - 1) == '/') 113 *(rrpout - 1) = '\0'; 114 else 115 *rrpout = '\0'; 116} 117 118static void checkpath(const char *path, char *resolved) 119{ 120 struct stat sb; 121 122 if (!realpath(path, resolved) || stat(resolved, &sb)) 123 err(EX_USAGE, "%s", resolved); 124 if (!S_ISDIR(sb.st_mode)) 125 errx(EX_USAGE, "%s: not a directory", resolved); 126} 127 128int main(int argc, char **argv) 129{ 130 char *progname, *dev; 131 ntfs_mount_options_header *opts_hdr; 132 ntfs_mount_options_1_0 *opts; 133 int ch, dummy, flags = 0; 134 char dir[MAXPATHLEN]; 135 const char ntfs[] = "ntfs"; 136 char *const kextargs[] = { "/sbin/kextload", 137 "/System/Library/Extensions/ntfs.kext", NULL }; 138 struct vfsconf vfc; 139 BOOL case_sensitive; 140 141 /* Default to mounting read-only. */ 142 flags = MNT_RDONLY; 143 144 /* Save & strip off program name. */ 145 progname = argv[0]; 146 /* Set up default options. */ 147 case_sensitive = FALSE; 148 /* Parse the options. */ 149 while ((ch = getopt(argc, argv, "so:h?")) != -1) { 150 switch (ch) { 151 case 's': 152 case_sensitive = TRUE; 153 break; 154 case 'o': { 155 mntoptparse_t tmp; 156 157 tmp = getmntopts(optarg, mopts, &flags, &dummy); 158 if (!tmp) 159 err(EX_OSERR, "getmntopts() failed"); 160 freemntopts(tmp); 161 break; 162 } 163 case 'h': 164 case '?': 165 default: 166 usage(progname); 167 break; 168 } 169 } 170 argc -= optind; 171 argv += optind; 172 /* Parse the device to mount and the directory to mount it on. */ 173 if (argc != 2) 174 usage(progname); 175 dev = argv[0]; 176 checkpath(argv[1], dir); 177 rmslashes(dev, dev); 178 /* 179 * Set up the NTFS mount options structure for the mount(2) call. 180 * 181 * We currently implement version 1.0, which only has the flags option 182 * and the only currently defined flag is NTFS_OPT_CASE_SENSITIVE. 183 */ 184 opts_hdr = valloc(((sizeof(*opts_hdr) + 7) & ~7) + sizeof(*opts)); 185 if (!opts_hdr) 186 err(EX_OSERR, "valloc() failed"); 187 *opts_hdr = (ntfs_mount_options_header) { 188 .fspec = dev, 189 .major_ver = 1, 190 .minor_ver = 0, 191 }; 192 opts = (ntfs_mount_options_1_0*)((char*)opts_hdr + 193 ((sizeof(*opts_hdr) + 7) & ~7)); 194 *opts = (ntfs_mount_options_1_0) { 195 .flags = case_sensitive ? NTFS_MNT_OPT_CASE_SENSITIVE : 0, 196 }; 197 /* If the kext is not loaded, load it now. */ 198 if (getvfsbyname(ntfs, &vfc)) { 199 /* 200 * Ignore errors from the load attempt and instead simply check 201 * that NTFS is now loaded and if not bail out now. 202 */ 203 (void)do_exec(progname, kextargs); 204 if (getvfsbyname(ntfs, &vfc)) 205 errx(EX_OSERR, "Failed to load NTFS file system kext."); 206 } 207 if (mount(ntfs, dir, flags, opts_hdr) < 0) 208 err(EX_OSERR, "%s on %s", dev, dir); 209 free(opts_hdr); 210 return 0; 211} 212