mount_msdosfs.c revision 152416
133549Sjkh/* $NetBSD: mount_msdos.c,v 1.18 1997/09/16 12:24:18 lukem Exp $ */ 233549Sjkh 32892Sdfr/* 42892Sdfr * Copyright (c) 1994 Christopher G. Demetriou 52892Sdfr * All rights reserved. 62892Sdfr * 72892Sdfr * Redistribution and use in source and binary forms, with or without 82892Sdfr * modification, are permitted provided that the following conditions 92892Sdfr * are met: 102892Sdfr * 1. Redistributions of source code must retain the above copyright 112892Sdfr * notice, this list of conditions and the following disclaimer. 122892Sdfr * 2. Redistributions in binary form must reproduce the above copyright 132892Sdfr * notice, this list of conditions and the following disclaimer in the 142892Sdfr * documentation and/or other materials provided with the distribution. 152892Sdfr * 3. All advertising materials mentioning features or use of this software 162892Sdfr * must display the following acknowledgement: 172892Sdfr * This product includes software developed by Christopher G. Demetriou. 182892Sdfr * 4. The name of the author may not be used to endorse or promote products 192892Sdfr * derived from this software without specific prior written permission 202892Sdfr * 212892Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 222892Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 232892Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 242892Sdfr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 252892Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 262892Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 272892Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 282892Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 292892Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 302892Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 312892Sdfr */ 322892Sdfr 332892Sdfr#ifndef lint 3415771Swollmanstatic const char rcsid[] = 3550476Speter "$FreeBSD: head/sbin/mount_msdosfs/mount_msdosfs.c 152416 2005-11-14 16:04:01Z maxim $"; 362892Sdfr#endif /* not lint */ 372892Sdfr 382892Sdfr#include <sys/param.h> 392892Sdfr#include <sys/mount.h> 402892Sdfr#include <sys/stat.h> 41120492Sfjoe#include <sys/iconv.h> 42121363Strhodes#include <sys/linker.h> 43121429Strhodes#include <sys/module.h> 4423335Sbde 452892Sdfr#include <ctype.h> 462892Sdfr#include <err.h> 472892Sdfr#include <grp.h> 4833761Sache#include <locale.h> 492892Sdfr#include <pwd.h> 502892Sdfr#include <stdio.h> 5155613Sache/* must be after stdio to declare fparseln */ 5255613Sache#include <libutil.h> 532892Sdfr#include <stdlib.h> 542892Sdfr#include <string.h> 5515771Swollman#include <sysexits.h> 562892Sdfr#include <unistd.h> 57121429Strhodes 582892Sdfr#include "mntopts.h" 592892Sdfr 6015771Swollmanstatic struct mntopt mopts[] = { 612892Sdfr MOPT_STDOPTS, 6228731Sbde MOPT_FORCE, 6328731Sbde MOPT_SYNC, 6428731Sbde MOPT_UPDATE, 65147242Sdelphij MOPT_END 662892Sdfr}; 672892Sdfr 6892882Simpstatic gid_t a_gid(char *); 6992882Simpstatic uid_t a_uid(char *); 7092882Simpstatic mode_t a_mask(char *); 7192882Simpstatic void usage(void) __dead2; 72152362Srodrigcstatic int set_charset(struct iovec *iov, int *iovlen, const char *, const char *); 732892Sdfr 742892Sdfrint 75121373Strhodesmain(int argc, char **argv) 762892Sdfr{ 77152416Smaxim struct iovec *iov = NULL; 78152416Smaxim int iovlen = 0; 792892Sdfr struct stat sb; 80118837Strhodes int c, mntflags, set_gid, set_uid, set_mask, set_dirmask; 81152362Srodrigc int optflags = 0; 82120492Sfjoe char *dev, *dir, mntpath[MAXPATHLEN], *csp; 83152362Srodrigc char fstype[] = "msdosfs"; 84152362Srodrigc char *cs_dos = NULL; 85152362Srodrigc char *cs_local = NULL; 86152362Srodrigc mode_t mask = 0, dirmask = 0; 87152362Srodrigc uid_t uid = 0; 88152362Srodrigc gid_t gid = 0; 89152362Srodrigc getmnt_silent = 1; 902892Sdfr 91118837Strhodes mntflags = set_gid = set_uid = set_mask = set_dirmask = 0; 922892Sdfr 93120492Sfjoe while ((c = getopt(argc, argv, "sl9u:g:m:M:o:L:D:")) != -1) { 942892Sdfr switch (c) { 9533549Sjkh case 's': 96152362Srodrigc build_iovec(&iov, &iovlen, "shortnames", NULL, (size_t)-1); 9733549Sjkh break; 9833549Sjkh case 'l': 99152362Srodrigc build_iovec(&iov, &iovlen, "longnames", NULL, (size_t)-1); 10033549Sjkh break; 10133549Sjkh case '9': 102152362Srodrigc build_iovec_argf(&iov, &iovlen, "nowin95", NULL, (size_t)-1); 10333549Sjkh break; 1042892Sdfr case 'u': 105152362Srodrigc uid = a_uid(optarg); 1062892Sdfr set_uid = 1; 1072892Sdfr break; 1082892Sdfr case 'g': 109152362Srodrigc gid = a_gid(optarg); 1102892Sdfr set_gid = 1; 1112892Sdfr break; 1122892Sdfr case 'm': 113152362Srodrigc mask = a_mask(optarg); 1142892Sdfr set_mask = 1; 1152892Sdfr break; 116118837Strhodes case 'M': 117152362Srodrigc dirmask = a_mask(optarg); 118118837Strhodes set_dirmask = 1; 119118837Strhodes break; 120152362Srodrigc case 'L': { 121152362Srodrigc const char *quirk = NULL; 122120492Sfjoe if (setlocale(LC_CTYPE, optarg) == NULL) 123120492Sfjoe err(EX_CONFIG, "%s", optarg); 124120492Sfjoe csp = strchr(optarg,'.'); 125120492Sfjoe if (!csp) 126120492Sfjoe err(EX_CONFIG, "%s", optarg); 127152362Srodrigc quirk = kiconv_quirkcs(csp + 1, KICONV_VENDOR_MICSFT); 128152362Srodrigc build_iovec_argf(&iov, &iovlen, "cs_local", quirk); 129152362Srodrigc } 13033761Sache break; 131120492Sfjoe case 'D': 132152362Srodrigc cs_dos = strdup(optarg); 133152362Srodrigc build_iovec_argf(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1); 13433749Sache break; 135152362Srodrigc case 'o': { 136152362Srodrigc char *p = NULL; 137152362Srodrigc char *val = strdup(""); 138152362Srodrigc getmntopts(optarg, mopts, &mntflags, &optflags); 139152416Smaxim p = strchr(optarg, '='); 140152416Smaxim if (p != NULL) { 141152362Srodrigc free(val); 142152362Srodrigc *p = '\0'; 143152362Srodrigc val = p + 1; 144152362Srodrigc } 145152362Srodrigc build_iovec(&iov, &iovlen, optarg, val, (size_t)-1); 146152362Srodrigc } 1472892Sdfr break; 148120492Sfjoe case 'W': 149120492Sfjoe if (strcmp(optarg, "iso22dos") == 0) { 150152362Srodrigc cs_local = strdup("ISO8859-2"); 151152362Srodrigc cs_dos = strdup("CP852"); 152120492Sfjoe } else if (strcmp(optarg, "iso72dos") == 0) { 153152362Srodrigc cs_local = strdup("ISO8859-7"); 154152362Srodrigc cs_dos = strdup("CP737"); 155120492Sfjoe } else if (strcmp(optarg, "koi2dos") == 0) { 156152362Srodrigc cs_local = strdup("KOI8-R"); 157152362Srodrigc cs_dos = strdup("CP866"); 158120492Sfjoe } else if (strcmp(optarg, "koi8u2dos") == 0) { 159152362Srodrigc cs_local = strdup("KOI8-U"); 160152362Srodrigc cs_dos = strdup("CP866"); 161120492Sfjoe } else { 162120492Sfjoe err(EX_NOINPUT, "%s", optarg); 163120492Sfjoe } 164152362Srodrigc build_iovec(&iov, &iovlen, "cs_local", cs_local, (size_t)-1); 165152362Srodrigc build_iovec(&iov, &iovlen, "cs_dos", cs_dos, (size_t)-1); 166120492Sfjoe break; 1672892Sdfr case '?': 1682892Sdfr default: 1692892Sdfr usage(); 1702892Sdfr break; 1712892Sdfr } 1722892Sdfr } 1732892Sdfr 1742892Sdfr if (optind + 2 != argc) 1752892Sdfr usage(); 1762892Sdfr 177118837Strhodes if (set_mask && !set_dirmask) { 178152362Srodrigc dirmask = mask; 179118837Strhodes set_dirmask = 1; 180118837Strhodes } 181118837Strhodes else if (set_dirmask && !set_mask) { 182152362Srodrigc mask = dirmask; 183118837Strhodes set_mask = 1; 184118837Strhodes } 185118837Strhodes 1862892Sdfr dev = argv[optind]; 1872892Sdfr dir = argv[optind + 1]; 1882892Sdfr 189152362Srodrigc if (cs_local != NULL) { 190152362Srodrigc if (set_charset(iov, &iovlen, cs_local, cs_dos) == -1) 191120492Sfjoe err(EX_OSERR, "msdosfs_iconv"); 192152362Srodrigc build_iovec_argf(&iov, &iovlen, "kiconv", ""); 193152362Srodrigc } else if (cs_dos != NULL) { 194152362Srodrigc build_iovec_argf(&iov, &iovlen, "cs_local", "ISO8859-1"); 195152362Srodrigc if (set_charset(iov, &iovlen, "ISO8859-1", cs_dos) == -1) 196120492Sfjoe err(EX_OSERR, "msdosfs_iconv"); 197152362Srodrigc build_iovec_argf(&iov, &iovlen, "kiconv", ""); 198120492Sfjoe } 199120492Sfjoe 20052055Sphk /* 20152055Sphk * Resolve the mountpoint with realpath(3) and remove unnecessary 20252055Sphk * slashes from the devicename if there are any. 20352055Sphk */ 20452055Sphk (void)checkpath(dir, mntpath); 20552055Sphk (void)rmslashes(dev, dev); 20652055Sphk 2072892Sdfr if (!set_gid || !set_uid || !set_mask) { 20852055Sphk if (stat(mntpath, &sb) == -1) 20952055Sphk err(EX_OSERR, "stat %s", mntpath); 2102892Sdfr 2112892Sdfr if (!set_uid) 212152362Srodrigc uid = sb.st_uid; 2132892Sdfr if (!set_gid) 214152362Srodrigc gid = sb.st_gid; 2152892Sdfr if (!set_mask) 216152362Srodrigc mask = dirmask = 217118837Strhodes sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); 2182892Sdfr } 2192892Sdfr 220152362Srodrigc build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1); 221152362Srodrigc build_iovec(&iov, &iovlen, "fspath", mntpath, (size_t)-1); 222152362Srodrigc build_iovec(&iov, &iovlen, "from", dev, (size_t)-1); 223152362Srodrigc build_iovec_argf(&iov, &iovlen, "uid", "%d", uid); 224152362Srodrigc build_iovec_argf(&iov, &iovlen, "gid", "%u", gid); 225152362Srodrigc build_iovec_argf(&iov, &iovlen, "mask", "%u", mask); 226152362Srodrigc build_iovec_argf(&iov, &iovlen, "dirmask", "%u", dirmask); 2272892Sdfr 228152362Srodrigc if (nmount(iov, iovlen, mntflags) < 0) 229152362Srodrigc err(1, "%s", dev); 230152362Srodrigc 2312892Sdfr exit (0); 2322892Sdfr} 2332892Sdfr 2342892Sdfrgid_t 2352892Sdfra_gid(s) 2362892Sdfr char *s; 2372892Sdfr{ 2382892Sdfr struct group *gr; 2392892Sdfr char *gname; 2402892Sdfr gid_t gid; 2412892Sdfr 2422892Sdfr if ((gr = getgrnam(s)) != NULL) 2432892Sdfr gid = gr->gr_gid; 2442892Sdfr else { 2452892Sdfr for (gname = s; *s && isdigit(*s); ++s); 2462892Sdfr if (!*s) 2472892Sdfr gid = atoi(gname); 2482892Sdfr else 24915771Swollman errx(EX_NOUSER, "unknown group id: %s", gname); 2502892Sdfr } 2512892Sdfr return (gid); 2522892Sdfr} 2532892Sdfr 2542892Sdfruid_t 2552892Sdfra_uid(s) 2562892Sdfr char *s; 2572892Sdfr{ 2582892Sdfr struct passwd *pw; 2592892Sdfr char *uname; 2602892Sdfr uid_t uid; 2612892Sdfr 2622892Sdfr if ((pw = getpwnam(s)) != NULL) 2632892Sdfr uid = pw->pw_uid; 2642892Sdfr else { 2652892Sdfr for (uname = s; *s && isdigit(*s); ++s); 2662892Sdfr if (!*s) 2672892Sdfr uid = atoi(uname); 2682892Sdfr else 26915771Swollman errx(EX_NOUSER, "unknown user id: %s", uname); 2702892Sdfr } 2712892Sdfr return (uid); 2722892Sdfr} 2732892Sdfr 2742892Sdfrmode_t 2752892Sdfra_mask(s) 2762892Sdfr char *s; 2772892Sdfr{ 2782892Sdfr int done, rv; 2792892Sdfr char *ep; 2802892Sdfr 2812892Sdfr done = 0; 28233549Sjkh rv = -1; 2832892Sdfr if (*s >= '0' && *s <= '7') { 2842892Sdfr done = 1; 2852892Sdfr rv = strtol(optarg, &ep, 8); 2862892Sdfr } 2872892Sdfr if (!done || rv < 0 || *ep) 28815771Swollman errx(EX_USAGE, "invalid file mode: %s", s); 2892892Sdfr return (rv); 2902892Sdfr} 2912892Sdfr 2922892Sdfrvoid 2932892Sdfrusage() 2942892Sdfr{ 295121007Sfjoe fprintf(stderr, "%s\n%s\n%s\n", 296121429Strhodes "usage: mount_msdosfs [-9ls] [-D DOS_codepage] [-g gid] [-L locale]", 297121429Strhodes " [-M mask] [-m mask] [-o options] [-u uid]", 298121429Strhodes " [-W table] special node"); 29915771Swollman exit(EX_USAGE); 3002892Sdfr} 30133749Sache 302120492Sfjoeint 303152362Srodrigcset_charset(struct iovec *iov, int *iovlen, const char *cs_local, const char *cs_dos) 30433749Sache{ 305120492Sfjoe int error; 30633749Sache 307120492Sfjoe if (modfind("msdosfs_iconv") < 0) 308120492Sfjoe if (kldload("msdosfs_iconv") < 0 || modfind("msdosfs_iconv") < 0) { 309134565Strhodes warnx("cannot find or load \"msdosfs_iconv\" kernel module"); 310120492Sfjoe return (-1); 311120492Sfjoe } 312120492Sfjoe 313152416Smaxim build_iovec_argf(&iov, iovlen, "cs_win", ENCODING_UNICODE); 314152362Srodrigc error = kiconv_add_xlat16_cspairs(ENCODING_UNICODE, cs_local); 315120492Sfjoe if (error) 316120492Sfjoe return (-1); 317152362Srodrigc if (cs_dos != NULL) { 318152362Srodrigc error = kiconv_add_xlat16_cspairs(cs_dos, cs_local); 319120492Sfjoe if (error) 320120492Sfjoe return (-1); 321120492Sfjoe } else { 322152362Srodrigc build_iovec_argf(&iov, iovlen, "cs_dos", cs_local); 323152362Srodrigc error = kiconv_add_xlat16_cspair(cs_local, cs_local, 324120492Sfjoe KICONV_FROM_UPPER | KICONV_LOWER); 325120492Sfjoe if (error) 326120492Sfjoe return (-1); 32733749Sache } 32833761Sache 329120492Sfjoe return (0); 33033761Sache} 331