1/*
2 * Copyright (c) 2000-2005,2008,2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (c) 1998 Robert Nordier
25 * All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in
34 *    the documentation and/or other materials provided with the
35 *    distribution.
36 *
37 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
38 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
39 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
41 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
43 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
45 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
46 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
47 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48 */
49
50/*	@(#)dosutil.c	3.0	13/09/00	(c) 2000 Apple Computer, Inc.	*/
51
52#include <stdint.h>
53#include <mach/machine/boolean.h>
54
55#include <sys/types.h>
56#include <sys/param.h>
57#include <sys/wait.h>
58#include <sys/errno.h>
59#include <sys/stat.h>
60#include <sys/mount.h>
61#include <sys/sysctl.h>
62#include <sys/loadable_fs.h>
63
64#include <sys/disk.h>
65
66#include <machine/byte_order.h>
67
68#include <err.h>
69#include <sysexits.h>
70#include <fcntl.h>
71#include <ctype.h>
72#include <stdio.h>
73#include <unistd.h>
74#include <string.h>
75#include <stdlib.h>
76#include <pwd.h>
77
78#include <CoreFoundation/CFString.h>
79#include <CoreFoundation/CFStringEncodingExt.h>
80
81#include "../msdosfs.kextproj/msdosfs.kmodproj/bootsect.h"
82#include "../msdosfs.kextproj/msdosfs.kmodproj/bpb.h"
83#include "../msdosfs.kextproj/msdosfs.kmodproj/direntry.h"
84
85#define FS_TYPE			"msdos"
86#define FS_NAME_FILE		"MSDOS"
87#define FS_BUNDLE_NAME		"msdosfs.kext"
88#define FS_KEXT_DIR			"/System/Library/Extensions/msdosfs.kext"
89#define FS_KMOD_DIR			"/System/Library/Extensions/msdosfs.kext/msdosfs"
90#define RAWDEV_PREFIX		"/dev/r"
91#define BLOCKDEV_PREFIX		"/dev/"
92#define MOUNT_COMMAND		"/sbin/mount"
93#define UMOUNT_COMMAND		"/sbin/umount"
94#define KEXTLOAD_COMMAND	"/sbin/kextload"
95#define KMODLOAD_COMMAND	"/sbin/kmodload"
96#define READWRITE_OPT		"-w"
97#define READONLY_OPT		"-r"
98#define SUID_OPT			"suid"
99#define NOSUID_OPT			"nosuid"
100#define DEV_OPT				"dev"
101#define NODEV_OPT			"nodev"
102#define LABEL_LENGTH		11
103#define MAX_DOS_BLOCKSIZE	4096
104
105#define FSUC_LABEL		'n'
106
107#define UNKNOWN_LABEL		"Unlabeled"
108
109#define	DEVICE_SUID			"suid"
110#define	DEVICE_NOSUID		"nosuid"
111
112#define	DEVICE_DEV			"dev"
113#define	DEVICE_NODEV		"nodev"
114
115#define	CLUST_FIRST	2			/* first legal cluster number */
116#define	CLUST_RSRVD	0x0ffffff6	/* reserved cluster range */
117
118
119
120/* globals */
121const char	*progname;	/* our program name, from argv[0] */
122int		debug;	/* use -D to enable debug printfs */
123
124
125
126/*
127 * The following code is re-usable for all FS_util programs
128 */
129void usage(void);
130
131static int fs_probe(char *devpath, int removable, int writable);
132static int fs_mount(char *devpath, char *mount_point, int removable,
133	int writable, int suid, int dev);
134static int fs_unmount(char *devpath);
135static int fs_label(char *devpath, char *volName);
136static void fs_set_label_file(char *labelPtr);
137
138static int safe_open(char *path, int flags, mode_t mode);
139static void safe_read(int fd, void *buf, int nbytes, off_t off);
140static void safe_close(int fd);
141static void safe_write(int fd, char *data, int len, off_t off);
142static void safe_execv(const char *args[]);
143
144static int checkLoadable(void);
145static int oklabel(const char *src);
146static void mklabel(char *dest, const char *src);
147
148int ret = 0;
149
150
151void usage()
152{
153        fprintf(stderr, "usage: %s action_arg device_arg [mount_point_arg] [Flags]\n", progname);
154        fprintf(stderr, "action_arg:\n");
155        fprintf(stderr, "       -%c (Probe)\n", FSUC_PROBE);
156        fprintf(stderr, "       -%c (Mount)\n", FSUC_MOUNT);
157        fprintf(stderr, "       -%c (Unmount)\n", FSUC_UNMOUNT);
158        fprintf(stderr, "       -%c name\n", 'n');
159    fprintf(stderr, "device_arg:\n");
160    fprintf(stderr, "       device we are acting upon (for example, 'disk0s2')\n");
161    fprintf(stderr, "mount_point_arg:\n");
162    fprintf(stderr, "       required for Mount and Force Mount \n");
163    fprintf(stderr, "Flags:\n");
164    fprintf(stderr, "       required for Mount, Force Mount and Probe\n");
165    fprintf(stderr, "       indicates removable or fixed (for example 'fixed')\n");
166    fprintf(stderr, "       indicates readonly or writable (for example 'readonly')\n");
167    fprintf(stderr, "Examples:\n");
168    fprintf(stderr, "		%s -p disk0s2 fixed writable\n", progname);
169    fprintf(stderr, "		%s -m disk0s2 /my/hfs removable readonly\n", progname);
170        exit(FSUR_INVAL);
171}
172
173int main(int argc, char **argv)
174{
175    char		rawdevpath[MAXPATHLEN];
176    char		blockdevpath[MAXPATHLEN];
177    char		opt;
178    struct stat	sb;
179    int			ret = FSUR_INVAL;
180
181
182    /* save & strip off program name */
183    progname = argv[0];
184    argc--;
185    argv++;
186
187    /* secret debug flag - must be 1st flag */
188    debug = (argc > 0 && !strcmp(argv[0], "-D"));
189    if (debug) { /* strip off debug flag argument */
190        argc--;
191        argv++;
192    }
193
194    if (argc < 2 || argv[0][0] != '-')
195        usage();
196    opt = argv[0][1];
197    if (opt != FSUC_PROBE && opt != FSUC_MOUNT && opt != FSUC_UNMOUNT && opt != FSUC_LABEL)
198        usage(); /* Not supported action */
199    if ((opt == FSUC_MOUNT || opt == FSUC_UNMOUNT || opt == FSUC_LABEL) && argc < 3)
200        usage(); /* mountpoint arg missing! */
201
202    if (snprintf(rawdevpath, sizeof(rawdevpath), "%s%s", RAWDEV_PREFIX, argv[1]) >= sizeof(rawdevpath))
203    	exit(FSUR_INVAL);
204    if (stat(rawdevpath, &sb) != 0) {
205        fprintf(stderr, "%s: stat %s failed, %s\n", progname, rawdevpath,
206                strerror(errno));
207        exit(FSUR_INVAL);
208    }
209
210    if (snprintf(blockdevpath, sizeof(blockdevpath), "%s%s", BLOCKDEV_PREFIX, argv[1]) >= sizeof(blockdevpath))
211    	exit(FSUR_INVAL);
212    if (stat(blockdevpath, &sb) != 0) {
213        fprintf(stderr, "%s: stat %s failed, %s\n", progname, blockdevpath,
214                strerror(errno));
215        exit(FSUR_INVAL);
216    }
217
218    switch (opt) {
219        case FSUC_PROBE: {
220            if (argc != 4)
221                usage();
222            ret = fs_probe(rawdevpath,
223                        strcmp(argv[2], DEVICE_FIXED),
224                        strcmp(argv[3], DEVICE_READONLY));
225            break;
226        }
227
228        case FSUC_MOUNT:
229        case FSUC_MOUNT_FORCE:
230            if (argc != 7)
231                usage();
232            if (strcmp(argv[3], DEVICE_FIXED) && strcmp(argv[3], DEVICE_REMOVABLE)) {
233                printf("msdosfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",3,argv[3]);
234                usage();
235            }
236                if (strcmp(argv[4], DEVICE_READONLY) && strcmp(argv[4], DEVICE_WRITABLE)) {
237                    printf("msdosfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",4,argv[4]);
238                    usage();
239                }
240                if (strcmp(argv[5], DEVICE_SUID) && strcmp(argv[5], DEVICE_NOSUID)) {
241                    printf("msdosfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",5,argv[5]);
242                    usage();
243                }
244                if (strcmp(argv[6], DEVICE_DEV) && strcmp(argv[6], DEVICE_NODEV)) {
245                    printf("msdosfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",6,argv[6]);
246                    usage();
247                }
248                ret = fs_mount(blockdevpath,
249                            argv[2],
250                            strcmp(argv[3], DEVICE_FIXED),
251                            strcmp(argv[4], DEVICE_READONLY),
252                            strcmp(argv[5], DEVICE_NOSUID),
253                            strcmp(argv[6], DEVICE_NODEV));
254            break;
255        case FSUC_UNMOUNT:
256            ret = fs_unmount(rawdevpath);
257            break;
258        case FSUC_LABEL:
259            ret = fs_label(rawdevpath, argv[2]);
260            break;
261        default:
262            usage();
263    }
264
265    exit(ret);
266
267    return(ret);
268}
269
270/*
271 * Begin Filesystem-specific code
272 */
273static int fs_probe(char *devpath, int removable, int writable)
274{
275    int fd;
276    struct dosdirentry *dirp;
277    union bootsector *bsp;
278    struct byte_bpb33 *b33;
279    struct byte_bpb50 *b50;
280    struct byte_bpb710 *b710;
281    u_int32_t	dev_block_size;
282    u_int16_t	bps;
283    u_int8_t	spc;
284    unsigned	rootDirSectors;
285    unsigned	i,j, finished;
286    char diskLabel[LABEL_LENGTH];
287    char buf[MAX_DOS_BLOCKSIZE];
288
289    fd = safe_open(devpath, O_RDONLY, 0);
290
291    if (ioctl(fd, DKIOCGETBLOCKSIZE, &dev_block_size) < 0)
292    {
293        fprintf(stderr, "%s: ioctl(DKIOCGETBLOCKSIZE) for %s failed, %s\n",
294            progname, devpath, strerror(errno));
295        return FSUR_IO_FAIL;
296    }
297    if (dev_block_size > MAX_DOS_BLOCKSIZE)
298    {
299        fprintf(stderr, "%s: block size of %s is too big (%lu)\n",
300            progname, devpath, (unsigned long) dev_block_size);
301        return FSUR_UNRECOGNIZED;
302    }
303
304    /*
305     * Read the boot sector of the filesystem, and then check the
306     * boot signature.  If not a dos boot sector then error out.
307     *
308     * NOTE: 4096 is a maximum sector size in current...
309     */
310    safe_read(fd, buf, MAX_DOS_BLOCKSIZE, 0);
311
312    bsp = (union bootsector *)buf;
313    b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
314    b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
315    b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
316
317    /* [2699033]
318     *
319     * The first three bytes are an Intel x86 jump instruction.  It should be one
320     * of the following forms:
321     *    0xE9 0x?? 0x??
322     *    0xEB 0x?? 0x90
323     * where 0x?? means any byte value is OK.
324     *
325     * [5016947]
326     *
327     * Windows doesn't actually check the third byte if the first byte is 0xEB,
328     * so we don't either
329     */
330    if (bsp->bs50.bsJump[0] != 0xE9
331        && bsp->bs50.bsJump[0] != 0xEB)
332    {
333        return FSUR_UNRECOGNIZED;
334    }
335
336    /* It is possible that the above check could match a partition table, or some */
337    /* non-FAT disk meant to boot a PC.  Check some more fields for sensible values. */
338
339    /* We only work with 512, 1024, 2048, and 4096 byte sectors */
340    bps = getuint16(b33->bpbBytesPerSec);
341    if ((bps < 0x200) || (bps & (bps - 1)) || (bps > MAX_DOS_BLOCKSIZE))
342	{
343        return(FSUR_UNRECOGNIZED);
344	}
345
346    /* Check to make sure valid sectors per cluster */
347    spc = b33->bpbSecPerClust;
348    if ((spc == 0 ) || (spc & (spc - 1)))
349	{
350        return(FSUR_UNRECOGNIZED);
351	}
352
353	/* Make sure the number of FATs is OK; on NTFS, this will be zero */
354	if (b33->bpbFATs == 0)
355	{
356		return(FSUR_UNRECOGNIZED);
357	}
358
359	/* Make sure the total sectors is non-zero */
360	if (getuint16(b33->bpbSectors) == 0 && getuint32(b50->bpbHugeSectors) == 0)
361	{
362		return(FSUR_UNRECOGNIZED);
363	}
364
365	/* Make sure there is a root directory */
366	if (getuint16(b33->bpbRootDirEnts) == 0 && getuint32(b710->bpbRootClust) == 0)
367	{
368		return(FSUR_UNRECOGNIZED);
369	}
370
371    /* we know this disk, find the volume label */
372    /* First, find the root directory */
373    diskLabel[0] = 0;
374    finished = false;
375    rootDirSectors = ((getuint16(b50->bpbRootDirEnts) * sizeof(struct dosdirentry)) +
376                      (bps-1)) / bps;
377    if (rootDirSectors) {			/* FAT12 or FAT16 */
378    	unsigned firstRootDirSecNum;
379    	char rootdirbuf[MAX_DOS_BLOCKSIZE];
380
381        firstRootDirSecNum = getuint16(b33->bpbResSectors) + (b33->bpbFATs * getuint16(b33->bpbFATsecs));
382        for (i=0; i< rootDirSectors; i++) {
383            safe_read(fd, rootdirbuf, bps, (firstRootDirSecNum+i)*bps);
384            dirp = (struct dosdirentry *)rootdirbuf;
385            for (j=0; j<bps; j+=sizeof(struct dosdirentry), dirp++) {
386                if (dirp->deName[0] == SLOT_EMPTY) {
387                    finished = true;
388                    break;
389                }
390                else if (dirp->deName[0] == SLOT_DELETED)
391                    continue;
392                else if (dirp->deAttributes == ATTR_WIN95)
393                    continue;
394                else if (dirp->deAttributes & ATTR_VOLUME) {
395                    strncpy(diskLabel, (char*)dirp->deName, LABEL_LENGTH);
396                    finished = true;
397                    break;
398                }
399            }	/* j */
400		if (finished == true)
401            break;
402        }	/* i */
403    }
404    else {	/* FAT32 */
405        u_int32_t cluster;
406        u_int32_t bytesPerCluster;
407        u_int8_t *rootDirBuffer;
408        off_t readOffset;
409
410        bytesPerCluster = (u_int32_t) bps * (u_int32_t) spc;
411        rootDirBuffer = malloc(bytesPerCluster);
412        cluster = getuint32(b710->bpbRootClust);
413
414        finished = false;
415        while (!finished && cluster >= CLUST_FIRST && cluster < CLUST_RSRVD)
416        {
417            /* Find sector where clusters start */
418            readOffset = getuint16(b710->bpbResSectors) + (b710->bpbFATs * getuint32(b710->bpbBigFATsecs));
419            /* Find sector where "cluster" starts */
420            readOffset += ((off_t) cluster - CLUST_FIRST) * (off_t) spc;
421            /* Convert to byte offset */
422            readOffset *= (off_t) bps;
423
424            /* Read in "cluster" */
425            safe_read(fd, rootDirBuffer, bytesPerCluster, readOffset);
426            dirp = (struct dosdirentry *) rootDirBuffer;
427
428            /* Examine each directory entry in this cluster */
429            for (i=0; i < bytesPerCluster; i += sizeof(struct dosdirentry), dirp++)
430            {
431                if (dirp->deName[0] == SLOT_EMPTY) {
432                    finished = true;	// Reached end of directory (never used entry)
433                    break;
434                }
435                else if (dirp->deName[0] == SLOT_DELETED)
436                    continue;
437                else if (dirp->deAttributes == ATTR_WIN95)
438                    continue;
439                else if (dirp->deAttributes & ATTR_VOLUME) {
440                    strncpy(diskLabel, (char *)dirp->deName, LABEL_LENGTH);
441                    finished = true;
442                    break;
443                }
444            }
445            if (finished)
446                break;
447
448            /* Find next cluster in the chain by reading the FAT */
449
450            /* Find first sector of FAT */
451            readOffset = getuint16(b710->bpbResSectors);
452            /* Find sector containing "cluster" entry in FAT */
453            readOffset += (cluster * 4) / bps;
454            /* Convert to byte offset */
455            readOffset *= bps;
456
457            /* Read one sector of the FAT */
458            safe_read(fd, rootDirBuffer, bps, readOffset);
459
460            cluster = getuint32(rootDirBuffer + ((cluster * 4) % bps));
461            cluster &= 0x0FFFFFFF;	// ignore reserved upper bits
462        }
463        free(rootDirBuffer);
464    }	/* rootDirSectors */
465
466	/* else look in the boot blocks */
467    if (diskLabel[0] == 0) {
468        if (getuint16(b50->bpbRootDirEnts) == 0) { /* Its a FAT32 */
469            if (((struct extboot *)bsp->bs710.bsExt)->exBootSignature == EXBOOTSIG) {
470            	strncpy(diskLabel, (char *)((struct extboot *)bsp->bs710.bsExt)->exVolumeLabel, LABEL_LENGTH);
471			}
472        }
473        else if (((struct extboot *)bsp->bs50.bsExt)->exBootSignature == EXBOOTSIG) {
474            strncpy(diskLabel, (char *)((struct extboot *)bsp->bs50.bsExt)->exVolumeLabel, LABEL_LENGTH);
475        }
476    }
477
478    fs_set_label_file(diskLabel);
479
480    safe_close(fd);
481
482    return(FSUR_RECOGNIZED);
483}
484
485
486static int fs_mount(char *devpath, char *mount_point, int removable, int writable, int suid, int dev) {
487    const char *kextargs[] = {KEXTLOAD_COMMAND, FS_KEXT_DIR, NULL};
488    const char *mountargs[] = {MOUNT_COMMAND, READWRITE_OPT, "-o", SUID_OPT, "-o",
489        DEV_OPT, "-t", FS_TYPE, devpath, mount_point, NULL};
490
491    if (! writable)
492        mountargs[1] = READONLY_OPT;
493
494    if (! suid)
495        mountargs[3] = NOSUID_OPT;
496
497    if (! dev)
498        mountargs[5] = NODEV_OPT;
499
500    if (checkLoadable())
501        safe_execv(kextargs); /* better here than in mount_udf */
502    safe_execv(mountargs);
503    ret = FSUR_IO_SUCCESS;
504
505    return ret;
506}
507
508static int fs_unmount(char *devpath) {
509        const char *umountargs[] = {UMOUNT_COMMAND, devpath, NULL};
510
511        safe_execv(umountargs);
512        return(FSUR_IO_SUCCESS);
513}
514
515
516
517/*
518 * Begin Filesystem-specific code
519 */
520static int fs_label(char *devpath, char *volName)
521{
522        int fd;
523        union bootsector *bsp;
524        struct byte_bpb33 *b33;
525        struct byte_bpb50 *b50;
526        u_int16_t	bps;
527        u_int8_t	spc;
528        char		tmplabel[LABEL_LENGTH], label[LABEL_LENGTH];
529        char 		buf[MAX_DOS_BLOCKSIZE];
530        CFStringRef 	cfstr;
531
532
533        /* First normalize the label */
534        if (volName == NULL)
535                errx(EX_USAGE, "No label was given");
536
537        /* Convert it from UTF-8 */
538        cfstr = CFStringCreateWithCString(kCFAllocatorDefault, volName, kCFStringEncodingUTF8);
539        if (cfstr == NULL)
540                errx(EX_DATAERR, "Bad UTF8 Name");
541        if (! CFStringGetCString(cfstr, tmplabel, LABEL_LENGTH, kCFStringEncodingWindowsLatin1))
542               errx(EX_DATAERR, "Could not convert to DOS Latin1");
543        CFRelease(cfstr);
544
545        if (! oklabel(tmplabel))
546                errx(EX_DATAERR, "Label has illegal characters");
547
548        /* Finall format it */
549        mklabel(label, tmplabel);
550
551
552        fd = safe_open(devpath, O_RDWR, 0);
553
554        /*
555         * Read the boot sector of the filesystem, and then check the
556         * boot signature.  If not a dos boot sector then error out.
557         *
558         * NOTE: 4096 is a maximum sector size in current...
559         */
560        safe_read(fd, buf, MAX_DOS_BLOCKSIZE, 0);
561
562        bsp = (union bootsector *)buf;
563        b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
564        b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
565
566        if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
567            || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
568                return(FSUR_UNRECOGNIZED);
569        }
570
571        /* Both partitions tables and boot sectors pass the above test, do do some more */
572
573        /* We only work with 512, 1024, 2048 and 4096 byte sectors */
574        bps = getuint16(b33->bpbBytesPerSec);
575        if ((bps < 0x200) || (bps & (bps - 1)) || (bps > MAX_DOS_BLOCKSIZE))
576                return(FSUR_UNRECOGNIZED);
577
578        /* Check to make sure valid sectors per cluster */
579        spc = b33->bpbSecPerClust;
580        if ((spc == 0 ) || (spc & (spc - 1)))
581                return(FSUR_UNRECOGNIZED);
582
583        /* we know this disk, find the volume label */
584        if (getuint16(b50->bpbRootDirEnts) == 0) {
585                /* Its a FAT32 */
586                strncpy((char *)((struct extboot *)bsp->bs710.bsExt)->exVolumeLabel, label, LABEL_LENGTH);
587        }
588        else if (((struct extboot *)bsp->bs50.bsExt)->exBootSignature == EXBOOTSIG) {
589                strncpy((char *)((struct extboot *)bsp->bs50.bsExt)->exVolumeLabel, label, LABEL_LENGTH);
590        }
591
592
593        safe_write(fd, buf, MAX_DOS_BLOCKSIZE, 0);
594
595        safe_close(fd);
596
597        return(FSUR_IO_SUCCESS);
598}
599
600
601static CFStringEncoding GetDefaultDOSEncoding(void)
602{
603	CFStringEncoding encoding;
604    struct passwd *passwdp;
605	int fd;
606	ssize_t size;
607	char buffer[MAXPATHLEN + 1];
608
609	/*
610	 * Get a default (Mac) encoding.  We use the CFUserTextEncoding
611	 * file since CFStringGetSystemEncoding() always seems to
612	 * return 0 when msdos.util is executed via disk arbitration.
613	 */
614	encoding = kCFStringEncodingMacRoman;	/* Default to Roman/Latin */
615	if ((passwdp = getpwuid(getuid()))) {
616		strlcpy(buffer, passwdp->pw_dir, sizeof(buffer));
617		strlcat(buffer, "/.CFUserTextEncoding", sizeof(buffer));
618
619		if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
620			size = read(fd, buffer, MAXPATHLEN);
621			buffer[(size < 0 ? 0 : size)] = '\0';
622			close(fd);
623			encoding = (CFStringEncoding)strtol(buffer, NULL, 0);
624		}
625	}
626
627	/* Convert the Mac encoding to a DOS/Windows encoding. */
628	switch (encoding) {
629		case kCFStringEncodingMacRoman:
630			encoding = kCFStringEncodingDOSLatin1;
631			break;
632		case kCFStringEncodingMacJapanese:
633			encoding = kCFStringEncodingDOSJapanese;
634			break;
635		case kCFStringEncodingMacChineseTrad:
636			encoding = kCFStringEncodingDOSChineseTrad;
637			break;
638		case kCFStringEncodingMacKorean:
639			encoding = kCFStringEncodingDOSKorean;
640			break;
641		case kCFStringEncodingMacArabic:
642			encoding = kCFStringEncodingDOSArabic;
643			break;
644		case kCFStringEncodingMacHebrew:
645			encoding = kCFStringEncodingDOSHebrew;
646			break;
647		case kCFStringEncodingMacGreek:
648			encoding = kCFStringEncodingDOSGreek;
649			break;
650		case kCFStringEncodingMacCyrillic:
651		case kCFStringEncodingMacUkrainian:
652			encoding = kCFStringEncodingDOSCyrillic;
653			break;
654		case kCFStringEncodingMacThai:
655			encoding = kCFStringEncodingDOSThai;
656			break;
657		case kCFStringEncodingMacChineseSimp:
658			encoding = kCFStringEncodingDOSChineseSimplif;
659			break;
660		case kCFStringEncodingMacCentralEurRoman:
661		case kCFStringEncodingMacCroatian:
662		case kCFStringEncodingMacRomanian:
663			encoding = kCFStringEncodingDOSLatin2;
664			break;
665		case kCFStringEncodingMacTurkish:
666			encoding = kCFStringEncodingDOSTurkish;
667			break;
668		case kCFStringEncodingMacIcelandic:
669			encoding = kCFStringEncodingDOSIcelandic;
670			break;
671		case kCFStringEncodingMacFarsi:
672			encoding = kCFStringEncodingDOSArabic;
673			break;
674		default:
675			encoding = kCFStringEncodingInvalidId;	/* Error: no corresponding Windows encoding */
676			break;
677	}
678
679	return encoding;
680}
681
682/* Set the name of this file system */
683static void fs_set_label_file(char *labelPtr)
684{
685    int				i;
686    CFStringEncoding encoding;
687    char			label[LABEL_LENGTH+1];
688    char			labelUTF8[LABEL_LENGTH*3];
689    CFStringRef 	cfstr;
690
691	/* Make a local copy of the label */
692	strncpy(label, labelPtr, LABEL_LENGTH);
693	label[LABEL_LENGTH] = 0;
694
695	/* Convert leading 0x05 to 0xE5 for multibyte languages like Japanese */
696	if (label[0] == 0x05)
697		label[0] = 0xE5;
698
699	/* Check for illegal characters */
700	if (!oklabel(label))
701		label[0] = 0;
702
703	/* Remove any trailing spaces */
704	for (i=LABEL_LENGTH-1; i>=0; --i) {
705		if (label[i] == ' ')
706			label[i] = 0;
707		else
708			break;
709	}
710
711    /* Convert it to UTF-8 */
712    encoding = GetDefaultDOSEncoding();
713    cfstr = CFStringCreateWithCString(NULL, label, encoding);
714    if (cfstr == NULL && encoding != kCFStringEncodingDOSLatin1)
715        cfstr = CFStringCreateWithCString(NULL, label, kCFStringEncodingDOSLatin1);
716	if (cfstr == NULL)
717		labelUTF8[0] = 0;
718	else {
719		CFMutableStringRef mutable;
720
721		mutable = CFStringCreateMutableCopy(NULL, 0, cfstr);
722		if (mutable != NULL) {
723			CFStringNormalize(mutable, kCFStringNormalizationFormD);
724			CFStringGetCString(mutable, labelUTF8, sizeof(labelUTF8), kCFStringEncodingUTF8);
725			CFRelease(mutable);
726		}
727
728		CFRelease(cfstr);
729	}
730
731    /* At this point, labelUTF8 should contain a correctly formatted name (possibly empty) */
732    write(1, labelUTF8, strlen(labelUTF8));
733}
734
735/*
736 * Based from newfs_msdos....to support the same 'functionality'...thanks
737 */
738
739/*
740 * Check a volume label.
741 */
742static int
743oklabel(const char *src)
744{
745    int c, i;
746
747    for (i = 0, c = 0; i <= 11; i++) {
748        c = (u_char)*src++;
749        if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
750            break;
751    }
752    return i && !c;
753}
754
755/*
756 * Make a volume label.
757 */
758static void
759mklabel(char *dest, const char *src)
760{
761    int c, i;
762
763    for (i = 0; i < 11; i++) {
764	c = *src ? toupper(*src++) : ' ';
765	*dest++ = !i && c == '\xe5' ? 5 : c;
766    }
767}
768
769static int
770safe_open(char *path, int flags, mode_t mode)
771{
772	int fd = open(path, flags, mode);
773
774	if (fd < 0) {
775		fprintf(stderr, "%s: open %s failed, %s\n", progname, path,
776			strerror(errno));
777		exit(FSUR_IO_FAIL);
778	}
779	return(fd);
780}
781
782
783static void
784safe_close(int fd)
785{
786	if (close(fd)) {
787		fprintf(stderr, "%s: safe_close failed, %s\n", progname,
788			strerror(errno));
789		exit(FSUR_IO_FAIL);
790	}
791}
792
793void
794safe_execv(const char *args[])
795{
796	int		pid;
797	union wait	status;
798
799	pid = fork();
800	if (pid == 0) {
801		(void)execv(args[0], (char *const *) args);
802		fprintf(stderr, "%s: execv %s failed, %s\n", progname, args[0],
803			strerror(errno));
804		exit(FSUR_IO_FAIL);
805	}
806	if (pid == -1) {
807		fprintf(stderr, "%s: fork failed, %s\n", progname,
808			strerror(errno));
809		exit(FSUR_IO_FAIL);
810	}
811	if (wait4(pid, (int *)&status, 0, NULL) != pid) {
812		fprintf(stderr, "%s: BUG executing %s command\n", progname,
813			args[0]);
814		exit(FSUR_IO_FAIL);
815	} else if (!WIFEXITED(status)) {
816		fprintf(stderr, "%s: %s command aborted by signal %d\n",
817			progname, args[0], WTERMSIG(status));
818		exit(FSUR_IO_FAIL);
819	} else if (WEXITSTATUS(status)) {
820		fprintf(stderr, "%s: %s command failed, exit status %d: %s\n",
821			progname, args[0], WEXITSTATUS(status),
822			strerror(WEXITSTATUS(status)));
823		exit(FSUR_IO_FAIL);
824	}
825}
826
827
828static void
829safe_read(int fd, void *buf, int nbytes, off_t off)
830{
831	if (lseek(fd, off, SEEK_SET) == -1) {
832		fprintf(stderr, "%s: device seek error @ %qu, %s\n", progname,
833			off, strerror(errno));
834		exit(FSUR_IO_FAIL);
835	}
836	if (read(fd, buf, nbytes) != nbytes) {
837		fprintf(stderr, "%s: device safe_read error @ %qu, %s\n", progname,
838			off, strerror(errno));
839		exit(FSUR_IO_FAIL);
840	}
841}
842
843
844void
845safe_write(int fd, char *buf, int nbytes, off_t off)
846{
847        if (lseek(fd, off, SEEK_SET) == -1) {
848                fprintf(stderr, "%s: device seek error @ %qu, %s\n", progname,
849                        off, strerror(errno));
850                exit(FSUR_IO_FAIL);
851        }
852        if (write(fd, buf, nbytes) != nbytes) {
853                fprintf(stderr, "%s: write failed, %s\n", progname,
854                        strerror(errno));
855                exit(FSUR_IO_FAIL);
856        }
857}
858
859
860/* Return non-zero if the file system is not yet loaded. */
861static int checkLoadable(void)
862{
863	int error;
864	struct vfsconf vfc;
865
866	error = getvfsbyname(FS_TYPE, &vfc);
867
868	return error;
869}
870
871/* end of DOS.util.c */
872