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/loadable_fs.h> 22#ifndef FSUC_GETUUID 23#define FSUC_GETUUID 'k' 24#endif 25 26#include <sys/disk.h> 27#include <sys/ioctl.h> 28#include <sys/mount.h> 29#include <sys/param.h> 30#include <sys/stat.h> 31#include <sys/types.h> 32#include <sys/wait.h> 33 34#include <errno.h> 35#include <fcntl.h> 36#include <stdlib.h> 37#include <stdio.h> 38#include <string.h> 39#include <strings.h> 40#include <unistd.h> 41 42#include <CoreFoundation/CFString.h> 43 44/* Define this if you want debug output to go to syslog. */ 45//#define NTFS_UTIL_DEBUG 46 47#ifdef NTFS_UTIL_DEBUG 48#include <syslog.h> 49#endif /* NTFS_UTIL_DEBUG */ 50 51#include "ntfs.h" 52#include "ntfs_types.h" 53#include "ntfs_endian.h" 54#include "ntfs_layout.h" 55 56/* 57 * The NTFS in-memory mount point structure. 58 * 59 * Adapted from ../kext/ntfs_volume.h. 60 */ 61typedef struct { 62 /* NTFS bootsector provided information. */ 63 u32 sector_size; /* in bytes */ 64 u32 cluster_size; /* in bytes */ 65 u32 mft_record_size; /* in bytes */ 66 u32 index_block_size; /* in bytes */ 67 LCN mft_lcn; /* Cluster location of mft data. */ 68 LCN mftmirr_lcn; /* Cluster location of copy of mft. */ 69 int mftmirr_size; /* Size of mft mirror in mft records. */ 70 LCN nr_clusters; /* Volume size in clusters == number of 71 bits in lcn bitmap. */ 72} ntfs_volume; 73 74typedef struct { 75 MFT_RECORD *m; 76 ATTR_RECORD *a; 77} ntfs_attr_search_ctx; 78 79typedef struct { /* In memory vcn to lcn mapping structure element. */ 80 VCN vcn; /* vcn = Starting virtual cluster number. */ 81 LCN lcn; /* lcn = Starting logical cluster number. */ 82 s64 length; /* Run length in clusters. */ 83} ntfs_rl_element; 84 85/* Runlist allocations happen in multiples of this value in bytes. */ 86#define NTFS_RL_ALLOC_BLOCK 1024 87 88typedef enum { 89 LCN_HOLE = -1, /* Keep this as highest value or die! */ 90 LCN_RL_NOT_MAPPED = -2, 91 LCN_ENOENT = -3, 92 LCN_ENOMEM = -4, 93 LCN_EIO = -5, 94} LCN_SPECIAL_VALUES; 95 96static void usage(const char *progname) __attribute__((noreturn)); 97static void usage(const char *progname) 98{ 99 fprintf(stderr, "usage: %s action_arg device_arg [mount_point_arg] [Flags]\n", progname); 100 fprintf(stderr, "action_arg:\n"); 101 fprintf(stderr, " -%c (Get UUID Key)\n", FSUC_GETUUID); 102 fprintf(stderr, " -%c (Mount)\n", FSUC_MOUNT); 103 fprintf(stderr, " -%c (Probe)\n", FSUC_PROBE); 104 fprintf(stderr, " -%c (Unmount)\n", FSUC_UNMOUNT); 105 fprintf(stderr, "device_arg:\n"); 106 fprintf(stderr, " device we are acting upon (for example, 'disk0s2')\n"); 107 fprintf(stderr, "mount_point_arg:\n"); 108 fprintf(stderr, " required for Mount and Unmount\n"); 109 fprintf(stderr, "Flags:\n"); 110 fprintf(stderr, " required for Mount and Probe\n"); 111 fprintf(stderr, " indicates removable or fixed (for example 'fixed')\n"); 112 fprintf(stderr, " indicates readonly or writable (for example 'readonly')\n"); 113 fprintf(stderr, "Flags (Mount only):\n"); 114 fprintf(stderr, " indicates suid or nosuid (for example 'nosuid')\n"); 115 fprintf(stderr, " indicates dev or nodev (for example 'nodev')\n"); 116 fprintf(stderr, "Examples:\n"); 117 fprintf(stderr, " %s -p disk0s2 fixed writable\n", progname); 118 fprintf(stderr, " %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", progname); 119 exit(FSUR_INVAL); 120} 121 122/** 123 * ntfs_boot_sector_is_valid - check if @b contains a valid ntfs boot sector 124 * @b: Boot sector of device @mp to check. 125 * 126 * Check whether the boot sector @b is a valid ntfs boot sector. 127 * 128 * Return TRUE if it is valid and FALSE if not. 129 * 130 * Copied from ../kext/ntfs_vfsops.c. 131 */ 132static BOOL ntfs_boot_sector_is_valid(const NTFS_BOOT_SECTOR *b) 133{ 134 /* Check OEMidentifier is "NTFS " */ 135 if (b->oem_id != magicNTFS) 136 goto not_ntfs; 137 /* 138 * Check bytes per sector value is between 256 and 139 * NTFS_MAX_SECTOR_SIZE. 140 */ 141 if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 || 142 le16_to_cpu(b->bpb.bytes_per_sector) > 143 NTFS_MAX_SECTOR_SIZE) 144 goto not_ntfs; 145 /* Check sectors per cluster value is valid. */ 146 switch (b->bpb.sectors_per_cluster) { 147 case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: 148 break; 149 default: 150 goto not_ntfs; 151 } 152 /* Check the cluster size is not above the maximum (64kiB). */ 153 if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) * 154 b->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) 155 goto not_ntfs; 156 /* Check reserved/unused fields are really zero. */ 157 if (le16_to_cpu(b->bpb.reserved_sectors) || 158 le16_to_cpu(b->bpb.root_entries) || 159 le16_to_cpu(b->bpb.sectors) || 160 le16_to_cpu(b->bpb.sectors_per_fat) || 161 le32_to_cpu(b->bpb.large_sectors) || b->bpb.fats) 162 goto not_ntfs; 163 /* 164 * Check clusters per file mft record value is valid. It can be either 165 * between -31 and -9 (in which case the actual mft record size is 166 * -log2() of the absolute value) or a positive power of two. 167 */ 168 if ((u8)b->clusters_per_mft_record < 0xe1 || 169 (u8)b->clusters_per_mft_record > 0xf7) 170 switch (b->clusters_per_mft_record) { 171 case 1: case 2: case 4: case 8: case 16: case 32: case 64: 172 break; 173 default: 174 goto not_ntfs; 175 } 176 /* Check clusters per index block value is valid. */ 177 if ((u8)b->clusters_per_index_block < 0xe1 || 178 (u8)b->clusters_per_index_block > 0xf7) 179 switch (b->clusters_per_index_block) { 180 case 1: case 2: case 4: case 8: case 16: case 32: case 64: 181 break; 182 default: 183 goto not_ntfs; 184 } 185 /* All checks passed, this boot sector is an NTFS boot sector. */ 186 return TRUE; 187not_ntfs: 188 return FALSE; 189} 190 191/** 192 * ntfs_boot_sector_parse - parse the boot sector and store the data in @vol 193 * @vol: volume structure to initialise with data from boot sector 194 * @b: boot sector to parse 195 * 196 * Parse the ntfs boot sector @b and store all imporant information therein in 197 * the ntfs_volume @vol. 198 * 199 * Return 0 on success and error code on error. The following error codes are 200 * defined: 201 * FSUR_UNRECOGNIZED - Boot sector is invalid/volume is not supported. 202 * 203 * Adapted from ../kext/ntfs_vfsops.c. 204 */ 205static int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) 206{ 207 s64 ll; 208 int clusters_per_mft_record; 209 210 vol->sector_size = le16_to_cpu(b->bpb.bytes_per_sector); 211 vol->cluster_size = vol->sector_size * b->bpb.sectors_per_cluster; 212 if (vol->cluster_size < vol->sector_size) 213 return FSUR_UNRECOGNIZED; 214 clusters_per_mft_record = b->clusters_per_mft_record; 215 if (clusters_per_mft_record > 0) 216 vol->mft_record_size = vol->cluster_size * 217 clusters_per_mft_record; 218 else 219 /* 220 * When mft_record_size < cluster_size, clusters_per_mft_record 221 * = -log2(mft_record_size) bytes. mft_record_size normaly is 222 * 1024 bytes, which is encoded as 0xF6 (-10 in decimal). 223 */ 224 vol->mft_record_size = 1 << -clusters_per_mft_record; 225 /* 226 * Get the size of the volume in clusters and check for 64-bit-ness. 227 * Windows currently only uses 32 bits to save the clusters so we do 228 * the same as we do not really want to break compatibility. We could 229 * perhaps add a mount option to allow this one day but it would render 230 * such volumes incompatible with Windows. 231 */ 232 ll = sle64_to_cpu(b->number_of_sectors) / b->bpb.sectors_per_cluster; 233 if ((u64)ll >= (u64)1 << 32) 234 return FSUR_UNRECOGNIZED; 235 vol->nr_clusters = ll; 236 ll = sle64_to_cpu(b->mft_lcn); 237 if (ll >= vol->nr_clusters) 238 return FSUR_UNRECOGNIZED; 239 vol->mft_lcn = ll; 240 ll = sle64_to_cpu(b->mftmirr_lcn); 241 if (ll >= vol->nr_clusters) 242 return FSUR_UNRECOGNIZED; 243 vol->mftmirr_lcn = ll; 244 return 0; 245} 246 247/** 248 * ntfs_mst_fixup_post_read - deprotect multi sector transfer protected data 249 * @b: pointer to the data to deprotect 250 * @size: size in bytes of @b 251 * 252 * Perform the necessary post read multi sector transfer fixup and detect the 253 * presence of incomplete multi sector transfers. 254 * 255 * In the case of an incomplete transfer being detected, overwrite the magic of 256 * the ntfs record header being processed with "BAAD" and abort processing. 257 * 258 * Return 0 on success and FSUR_IO_FAIL on error ("BAAD" magic will be 259 * present). 260 * 261 * NOTE: We consider the absence / invalidity of an update sequence array to 262 * mean that the structure is not protected at all and hence does not need to 263 * be fixed up. Thus, we return success and not failure in this case. This is 264 * in contrast to ntfs_mst_fixup_pre_write(), see below. 265 * 266 * Adapted from ../kext/ntfs_mst.c. 267 */ 268static int ntfs_mst_fixup_post_read(NTFS_RECORD *b, const u32 size) 269{ 270 u16 *usa_pos, *data_pos; 271 u16 usa_ofs, usa_count, usn; 272 273 /* Setup the variables. */ 274 usa_ofs = le16_to_cpu(b->usa_ofs); 275 /* Decrement usa_count to get number of fixups. */ 276 usa_count = le16_to_cpu(b->usa_count) - 1; 277 /* Size and alignment checks. */ 278 if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 || 279 (u32)usa_ofs + ((u32)usa_count * 2) > size || 280 (size >> NTFS_BLOCK_SIZE_SHIFT) != usa_count) 281 return 0; 282 /* Position of usn in update sequence array. */ 283 usa_pos = (u16*)b + usa_ofs/sizeof(u16); 284 /* 285 * The update sequence number which has to be equal to each of the u16 286 * values before they are fixed up. Note no need to care for 287 * endianness since we are comparing and moving data for on disk 288 * structures which means the data is consistent. If it is consistenty 289 * the wrong endianness it does not make any difference. 290 */ 291 usn = *usa_pos; 292 /* Position in protected data of first u16 that needs fixing up. */ 293 data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 294 /* Check for incomplete multi sector transfer(s). */ 295 while (usa_count--) { 296 if (*data_pos != usn) { 297 /* 298 * Incomplete multi sector transfer detected! )-: 299 * Set the magic to "BAAD" and return failure. 300 * Note that magic_BAAD is already little endian. 301 */ 302 b->magic = magic_BAAD; 303 return FSUR_IO_FAIL; 304 } 305 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 306 } 307 /* Re-setup the variables. */ 308 usa_count = le16_to_cpu(b->usa_count) - 1; 309 data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1; 310 /* Fixup all sectors. */ 311 while (usa_count--) { 312 /* 313 * Increment position in usa and restore original data from 314 * the usa into the data buffer. 315 */ 316 *data_pos = *(++usa_pos); 317 /* Increment position in data as well. */ 318 data_pos += NTFS_BLOCK_SIZE/sizeof(u16); 319 } 320 return 0; 321} 322 323/** 324 * ntfs_attr_search_ctx_init - initialize an attribute search context 325 * @ctx: attribute search context to initialize 326 * @m: mft record with which to initialize the search context 327 * 328 * Initialize the attribute search context @ctx with @m. 329 * 330 * Adapted from ../kext/ntfs_attr.c. 331 */ 332static void ntfs_attr_search_ctx_init(ntfs_attr_search_ctx *ctx, MFT_RECORD *m) 333{ 334 *ctx = (ntfs_attr_search_ctx) { 335 .m = m, 336 .a = (ATTR_RECORD*)((u8*)m + le16_to_cpu(m->attrs_offset)), 337 }; 338} 339 340/** 341 * ntfs_attr_find_in_mft_record - find attribute in mft record 342 * @type: attribute type to find 343 * @ctx: search context with mft record and attribute to search from 344 * 345 * ntfs_attr_find_in_mft_record() takes a search context @ctx as parameter and 346 * searches the mft record specified by @ctx->m, beginning at @ctx->a, for an 347 * attribute of @type, the attribute must be unnamed. 348 * 349 * If the attribute is found, ntfs_attr_find_in_mft_record() returns 0 and 350 * @ctx->a is set to point to the found attribute. 351 * 352 * If the attribute is not found, FSUR_UNRECOGNIZED is returned and @ctx->a is 353 * set to point to the attribute before which the attribute being searched for 354 * would need to be inserted if such an action were to be desired. 355 * 356 * On actual error, ntfs_attr_find_in_mft_record() returns FSUR_IO_FAIL. In 357 * this case @ctx->a is undefined and in particular do not rely on it not 358 * having changed. 359 * 360 * Adapted from ../kext/ntfs_attr.c. 361 */ 362static int ntfs_attr_find_in_mft_record(const ATTR_TYPE type, 363 ntfs_attr_search_ctx *ctx) 364{ 365 ATTR_RECORD *a; 366 367 a = ctx->a; 368 for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) { 369 if ((u8*)a < (u8*)ctx->m || (u8*)a > (u8*)ctx->m + 370 le32_to_cpu(ctx->m->bytes_allocated)) 371 break; 372 ctx->a = a; 373 if (le32_to_cpu(a->type) > le32_to_cpu(type) || 374 a->type == AT_END) 375 return FSUR_UNRECOGNIZED; 376 if (!a->length) 377 break; 378 if (a->type != type) 379 continue; 380 /* The search failed if the found attribute is named. */ 381 if (a->name_length) 382 return FSUR_UNRECOGNIZED; 383 return 0; 384 } 385 return FSUR_IO_FAIL; 386} 387 388/** 389 * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist 390 * @vol: ntfs volume on which the attribute resides 391 * @a: attribute record whose mapping pairs array to decompress 392 * @runlist: runlist in which to insert @a's runlist 393 * 394 * It is up to the caller to serialize access to the runlist @runlist. 395 * 396 * Decompress the attribute @a's mapping pairs array into a runlist. On 397 * success, return the decompressed runlist in @runlist. 398 * 399 * If @runlist already contains a runlist, the decompressed runlist is inserted 400 * into the appropriate place in @runlist and the resultant, combined runlist 401 * is returned in @runlist. 402 * 403 * On error, return error code (FSUR_UNRECOGNIZED, FSUR_IO_FAIL, or FSUR_INVAL). 404 * 405 * Adapted from ../kext/ntfs_runlist.c. 406 */ 407static int ntfs_mapping_pairs_decompress(ntfs_volume *vol, 408 const ATTR_RECORD *a, ntfs_rl_element **runlist) 409{ 410 VCN vcn; /* Current vcn. */ 411 LCN lcn; /* Current lcn. */ 412 s64 deltaxcn; /* Change in [vl]cn. */ 413 u8 *buf; /* Current position in mapping pairs array. */ 414 u8 *a_end; /* End of attribute. */ 415 ntfs_rl_element *rl; 416 unsigned rlsize; /* Size of runlist buffer. */ 417 int err; 418 u16 rlpos; /* Current runlist position in units of 419 runlist_elements. */ 420 u8 b; /* Current byte offset in buf. */ 421 422 /* Make sure @a exists and is non-resident. */ 423 if (!a || !a->non_resident || sle64_to_cpu(a->lowest_vcn) < (VCN)0) 424 return FSUR_INVAL; 425 /* Start at vcn = lowest_vcn and lcn 0. */ 426 vcn = sle64_to_cpu(a->lowest_vcn); 427 lcn = 0; 428 /* Get start of the mapping pairs array. */ 429 buf = (u8*)a + le16_to_cpu(a->mapping_pairs_offset); 430 a_end = (u8*)a + le32_to_cpu(a->length); 431 if (buf < (u8*)a || buf > a_end) 432 return FSUR_IO_FAIL; 433 /* If the mapping pairs array is valid but empty, nothing to do. */ 434 if (!vcn && !*buf) 435 return FSUR_UNRECOGNIZED; 436 /* Current position in runlist array. */ 437 rlpos = 0; 438 rlsize = NTFS_RL_ALLOC_BLOCK; 439 /* Allocate NTFS_RL_ALLOC_BLOCK bytes for the runlist. */ 440 rl = malloc(rlsize); 441 if (!rl) 442 return FSUR_INVAL; 443 /* Insert unmapped starting element if necessary. */ 444 if (vcn) { 445 rl->vcn = 0; 446 rl->lcn = LCN_RL_NOT_MAPPED; 447 rl->length = vcn; 448 rlpos++; 449 } 450 while (buf < a_end && *buf) { 451 /* 452 * Allocate more memory if needed, including space for the 453 * not-mapped and terminator elements. 454 */ 455 if (((rlpos + 3) * sizeof(*rl)) > rlsize) { 456 ntfs_rl_element *rl2; 457 458 rl2 = malloc(rlsize + NTFS_RL_ALLOC_BLOCK); 459 if (!rl2) { 460 err = FSUR_INVAL; 461 goto err; 462 } 463 memcpy(rl2, rl, rlsize); 464 free(rl); 465 rl = rl2; 466 rlsize += NTFS_RL_ALLOC_BLOCK; 467 } 468 /* Enter the current vcn into the current runlist element. */ 469 rl[rlpos].vcn = vcn; 470 /* 471 * Get the change in vcn, i.e. the run length in clusters. 472 * Doing it this way ensures that we sign-extend negative 473 * values. A negative run length does not make any sense, but 474 * hey, I did not design NTFS... 475 */ 476 b = *buf & 0xf; 477 if (b) { 478 if (buf + b >= a_end) { 479 err = FSUR_IO_FAIL; 480 goto err; 481 } 482 for (deltaxcn = (s8)buf[b--]; b; b--) 483 deltaxcn = (deltaxcn << 8) + buf[b]; 484 } else /* The length entry is compulsory. */ 485 deltaxcn = (s64)-1; 486 /* 487 * Assume a negative length to indicate data corruption and 488 * hence clean-up and return NULL. 489 */ 490 if (deltaxcn < 0) { 491 err = FSUR_IO_FAIL; 492 goto err; 493 } 494 /* 495 * Enter the current run length into the current runlist 496 * element. 497 */ 498 rl[rlpos].length = deltaxcn; 499 /* Increment the current vcn by the current run length. */ 500 vcn += deltaxcn; 501 /* 502 * There might be no lcn change at all, as is the case for 503 * sparse clusters on NTFS 3.0+, in which case we set the lcn 504 * to LCN_HOLE. 505 */ 506 if (!(*buf & 0xf0)) 507 rl[rlpos].lcn = LCN_HOLE; 508 else { 509 /* Get the lcn change which really can be negative. */ 510 u8 b2 = *buf & 0xf; 511 b = b2 + ((*buf >> 4) & 0xf); 512 if (buf + b >= a_end) { 513 err = FSUR_IO_FAIL; 514 goto err; 515 } 516 for (deltaxcn = (s8)buf[b--]; b > b2; b--) 517 deltaxcn = (deltaxcn << 8) + buf[b]; 518 /* Change the current lcn to its new value. */ 519 lcn += deltaxcn; 520 /* Check lcn is not below -1. */ 521 if (lcn < (LCN)-1) { 522 err = FSUR_IO_FAIL; 523 goto err; 524 } 525 /* Enter the current lcn into the runlist element. */ 526 rl[rlpos].lcn = lcn; 527 } 528 /* Get to the next runlist element. */ 529 rlpos++; 530 /* Increment the buffer position to the next mapping pair. */ 531 buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; 532 } 533 if (buf >= a_end) { 534 err = FSUR_IO_FAIL; 535 goto err; 536 } 537 /* 538 * If there is a highest_vcn specified, it must be equal to the final 539 * vcn in the runlist - 1, or something has gone badly wrong. 540 */ 541 deltaxcn = sle64_to_cpu(a->highest_vcn); 542 if (deltaxcn && vcn - 1 != deltaxcn) { 543 err = FSUR_IO_FAIL; 544 goto err; 545 } 546 /* Setup not mapped runlist element if this is the base extent. */ 547 if (!a->lowest_vcn) { 548 VCN max_cluster; 549 550 max_cluster = ((sle64_to_cpu(a->allocated_size) + 551 vol->cluster_size - 1) / vol->cluster_size) - 1; 552 /* 553 * A highest_vcn of zero means this is a single extent 554 * attribute so simply terminate the runlist with LCN_ENOENT). 555 */ 556 if (deltaxcn) { 557 /* 558 * If there is a difference between the highest_vcn and 559 * the highest cluster, the runlist is either corrupt 560 * or, more likely, there are more extents following 561 * this one. 562 */ 563 if (deltaxcn < max_cluster) { 564 rl[rlpos].vcn = vcn; 565 vcn += rl[rlpos].length = max_cluster - 566 deltaxcn; 567 rl[rlpos].lcn = LCN_RL_NOT_MAPPED; 568 rlpos++; 569 } else if (deltaxcn > max_cluster) { 570 err = FSUR_IO_FAIL; 571 goto err; 572 } 573 } 574 rl[rlpos].lcn = LCN_ENOENT; 575 } else /* Not the base extent. There may be more extents to follow. */ 576 rl[rlpos].lcn = LCN_RL_NOT_MAPPED; 577 /* Setup terminating runlist element. */ 578 rl[rlpos].vcn = vcn; 579 rl[rlpos].length = (s64)0; 580 *runlist = rl; 581 return 0; 582err: 583 free(rl); 584 return err; 585} 586 587/** 588 * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist 589 * @rl: runlist to use for conversion 590 * @vcn: vcn to convert 591 * @clusters: optional return pointer for the number of contiguous clusters 592 * 593 * Convert the virtual cluster number @vcn of an attribute into a logical 594 * cluster number (lcn) of a device using the runlist @rl to map vcns to their 595 * corresponding lcns. 596 * 597 * If @clusters is not NULL, on success (i.e. we return >= LCN_HOLE) we return 598 * the number of contiguous clusters after the returned lcn in *@clusters. 599 * 600 * Since lcns must be >= 0, we use negative return codes with special meaning: 601 * 602 * Return code Meaning / Description 603 * ================================================== 604 * LCN_HOLE Hole / not allocated on disk. 605 * LCN_RL_NOT_MAPPED This is part of the runlist which has not been 606 * inserted into the runlist yet. 607 * LCN_ENOENT There is no such vcn in the attribute. 608 * 609 * Locking: - The caller must have locked the runlist (for reading or writing). 610 * - This function does not touch the lock. 611 * - The runlist is not modified. 612 * 613 * Adapted from ../kext/ntfs_runlist.c. 614 */ 615static LCN ntfs_rl_vcn_to_lcn(const ntfs_rl_element *rl, const VCN vcn, 616 s64 *clusters) 617{ 618 unsigned i; 619 620 /* 621 * If @rl is NULL, assume that we have found an unmapped runlist. The 622 * caller can then attempt to map it and fail appropriately if 623 * necessary. 624 */ 625 if (!rl) 626 return LCN_RL_NOT_MAPPED; 627 /* Catch out of lower bounds vcn. */ 628 if (vcn < rl[0].vcn) 629 return LCN_ENOENT; 630 for (i = 0; rl[i].length; i++) { 631 if (vcn < rl[i + 1].vcn) { 632 const s64 ofs = vcn - rl[i].vcn; 633 if (clusters) 634 *clusters = rl[i].length - ofs; 635 if (rl[i].lcn >= (LCN)0) 636 return rl[i].lcn + ofs; 637 return rl[i].lcn; 638 } 639 } 640 /* 641 * Set *@clusters just in case rl[i].lcn is LCN_HOLE. That should 642 * never happen since the terminator element should never be of type 643 * LCN_HOLE but better be safe than sorry. 644 */ 645 if (clusters) 646 *clusters = 0; 647 /* 648 * The terminator element is setup to the correct value, i.e. one of 649 * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. 650 */ 651 if (rl[i].lcn < (LCN)0) 652 return rl[i].lcn; 653 /* Just in case... We could replace this with panic() some day. */ 654 return LCN_ENOENT; 655} 656 657/** 658 * ntfs_pread - Read from a raw device file descriptor. 659 * @f: file descriptor to read from 660 * @buf: temporary buffer of size sector_size bytes 661 * @sector_size: sector size in bytes 662 * @dst: destination buffer to read into 663 * @to_read: how many bytes to read into the destination buffer 664 * @ofs: offset into the raw device at which to read 665 * 666 * We are working with the raw device thus we need to do sector aligned i/o. 667 */ 668static ssize_t ntfs_pread(int f, u8 *buf, long sector_size, void *dst, 669 ssize_t to_read, off_t ofs) 670{ 671 off_t io_pos; 672 ssize_t buf_ofs, to_copy, copied; 673 674 copied = 0; 675 while (to_read > 0) { 676 io_pos = ofs & ~((off_t)sector_size - 1); 677 buf_ofs = ofs & ((off_t)sector_size - 1); 678 to_copy = sector_size - buf_ofs; 679 if (to_copy > to_read) 680 to_copy = to_read; 681 /* 682 * Accept partial reads as long as they contain the data we 683 * want. 684 */ 685 if (pread(f, buf, sector_size, io_pos) < buf_ofs + to_copy) { 686 if (!copied) 687 copied = -1; 688 break; 689 } 690 memcpy((u8*)dst + copied, buf + buf_ofs, to_copy); 691 to_read -= to_copy; 692 copied += to_copy; 693 } 694 return copied; 695} 696 697/** 698 * get_volume_mft_record - Get the mft record for $Volume system file. 699 * @rdev: raw device containing NTFS volume 700 * @vol: ntfs_volume structure to set up describing the NTFS volume 701 * @mrec: pointer in which to return the mft record of $Volume 702 * 703 * Check whether the volume is an NTFS volume and if so load and return the 704 * initialized ntfs_volume structure in @vol as well as the mft record for the 705 * system file $Volume in @mrec and return FSUR_RECOGNIZED. The caller is 706 * responsible for freeing the returned mft record @mrec using free(). 707 * 708 * If not an NTFS volume return FSUR_UNRECOGNIZED. 709 * 710 * On error return FSUR_INVAL or FSUR_IO_FAIL. 711 */ 712static int get_volume_mft_record(char *rdev, ntfs_volume *vol, 713 MFT_RECORD **mrec) 714{ 715 VCN vcn; 716 LCN lcn; 717 s64 clusters, io_size; 718 void *buf; 719 NTFS_BOOT_SECTOR *bs; 720 MFT_RECORD *m; 721 ntfs_rl_element *rl; 722 long sector_size; 723 unsigned vcn_ofs; 724 int f, err, to_read; 725 u32 dev_block_size; 726 ntfs_attr_search_ctx ctx; 727 728 /* 729 * Read the boot sector. We are working with the raw device thus we 730 * need to do sector aligned i/o. 731 * 732 * The maximum supported sector size for the NTFS driver is the system 733 * page size thus query the system for the page size and use that for 734 * the sector size. If the querying fails then use 32768 which is the 735 * maximum value that can be set in the NTFS boot sector (it is stored 736 * in a 16-bit unsigned variable). 737 * 738 * The minumum sector size is the ntfs block size (512 bytes). 739 */ 740 sector_size = sysconf(_SC_PAGE_SIZE); 741 if (sector_size < 0) 742 sector_size = 32768; 743 if (sector_size < NTFS_BLOCK_SIZE) 744 sector_size = NTFS_BLOCK_SIZE; 745 f = open(rdev, O_RDONLY); 746 if (f == -1) { 747 return FSUR_IO_FAIL; 748 } 749 /* 750 * Get the native block size of the device. If it is bigger than 751 * @sector_size we need to do i/o in multiples of the native block 752 * size. 753 */ 754 if (ioctl(f, DKIOCGETBLOCKSIZE, &dev_block_size) < 0) { 755 err = FSUR_IO_FAIL; 756 buf = NULL; 757 goto err; 758 } 759 if (dev_block_size > (u32)sector_size) 760 sector_size = dev_block_size; 761 buf = malloc(sector_size); 762 if (!buf) { 763 err = FSUR_IO_FAIL; 764 goto err; 765 } 766 /* 767 * We can cope with partial reads as long as we have at least the boot 768 * sector which fits inside the first NTFS_BLOCK_SIZE bytes. 769 */ 770 if (read(f, buf, sector_size) < NTFS_BLOCK_SIZE) { 771 err = FSUR_IO_FAIL; 772 goto err; 773 } 774 /* Check if the boot sector is a valid NTFS boot sector. */ 775 bs = buf; 776 if (!ntfs_boot_sector_is_valid(bs)) { 777 err = FSUR_UNRECOGNIZED; 778 goto err; 779 } 780 /* Parse the boot sector and initialize @vol with its information. */ 781 err = ntfs_boot_sector_parse(vol, bs); 782 if (err) 783 goto err; 784 m = malloc(vol->mft_record_size); 785 if (!m) { 786 err = FSUR_INVAL; 787 goto err; 788 } 789 /* Load the mft record for $MFT. */ 790 if (ntfs_pread(f, buf, sector_size, m, vol->mft_record_size, 791 vol->mft_lcn * vol->cluster_size) != 792 (ssize_t)vol->mft_record_size) { 793 err = FSUR_IO_FAIL; 794 goto free_err; 795 } 796 /* Apply the multi sector transfer protection fixups. */ 797 err = ntfs_mst_fixup_post_read((NTFS_RECORD*)m, vol->mft_record_size); 798 if (err) { 799 err = FSUR_IO_FAIL; 800 goto free_err; 801 } 802 /* Lookup $DATA attribute. */ 803 ntfs_attr_search_ctx_init(&ctx, m); 804 err = ntfs_attr_find_in_mft_record(AT_DATA, &ctx); 805 if (err) 806 goto free_err; 807 /* Decompress the mapping pairs array of the attribute. */ 808 rl = NULL; 809 err = ntfs_mapping_pairs_decompress(vol, ctx.a, &rl); 810 if (err) 811 goto free_err; 812 vcn = FILE_Volume * vol->mft_record_size; 813 vcn_ofs = vcn & (vol->cluster_size - 1); 814 vcn /= vol->cluster_size; 815 to_read = vol->mft_record_size; 816 /* 817 * Determine location of $Volume mft record from mft data runlist and 818 * read it into memory. We can safely assume that the first fragment 819 * of $DATA must specify $Volume's location or the volume would not be 820 * boot strappable. 821 */ 822 do { 823 lcn = ntfs_rl_vcn_to_lcn(rl, vcn, &clusters); 824 if (lcn < 0) { 825 err = FSUR_IO_FAIL; 826 goto rl_free_err; 827 } 828 io_size = (clusters * vol->cluster_size) - vcn_ofs; 829 if (io_size > to_read) 830 io_size = to_read; 831 if (ntfs_pread(f, buf, sector_size, m, io_size, (lcn * 832 vol->cluster_size) + vcn_ofs) != io_size) { 833 err = FSUR_IO_FAIL; 834 goto rl_free_err; 835 } 836 to_read -= io_size; 837 vcn += clusters; 838 vcn_ofs = 0; 839 } while (to_read > 0); 840 free(rl); 841 /* Apply the multi sector transfer protection fixups. */ 842 err = ntfs_mst_fixup_post_read((NTFS_RECORD*)m, vol->mft_record_size); 843 if (err) { 844 err = FSUR_IO_FAIL; 845 goto free_err; 846 } 847 (void)close(f); 848 free(buf); 849 /* Finally got the mft record for $Volume. */ 850 *mrec = m; 851 return FSUR_RECOGNIZED; 852rl_free_err: 853 free(rl); 854free_err: 855 free(m); 856err: 857 if (buf) 858 free(buf); 859 (void)close(f); 860 return err; 861} 862 863/** 864 * do_getuuid - Get the UUID key of an NTFS volume. 865 * 866 * If the volume is recognized as an NTFS volume look up its UUID key if it 867 * exists and output it to stdout then return FSUR_RECOGNIZED. 868 * 869 * If there is no UUID then return FSUR_INVAL and do not output anything to 870 * stdout. 871 * 872 * If the volume is not an NTFS volume return FSUR_UNRECOGNIZED. 873 * 874 * On error return FSUR_INVAL or FSUR_IO_FAIL. 875 */ 876static int do_getuuid(char *rdev) 877{ 878 MFT_RECORD *m; 879 ATTR_RECORD *a; 880 OBJECT_ID_ATTR *obj_id; 881 GUID *guid; 882 unsigned obj_id_len; 883 int err; 884 ntfs_volume vol; 885 ntfs_attr_search_ctx ctx; 886 char uuid[37]; 887 888 /* Obtain the mft record for $Volume. */ 889 err = get_volume_mft_record(rdev, &vol, &m); 890 if (err != FSUR_RECOGNIZED) 891 goto err; 892 /* Lookup $OBJECT_ID attribute. */ 893 ntfs_attr_search_ctx_init(&ctx, m); 894 err = ntfs_attr_find_in_mft_record(AT_OBJECT_ID, &ctx); 895 if (err) { 896 if (err != FSUR_UNRECOGNIZED) 897 goto free_err; 898 /* There is no volume UUID key which is fine. */ 899#ifdef NTFS_UTIL_DEBUG 900 /* Log to syslog. */ 901 openlog("ntfs.util", LOG_PID, LOG_DAEMON); 902 syslog(LOG_NOTICE, "Volume does not have a UUID key.\n"); 903 closelog(); 904#endif /* NTFS_UTIL_DEBUG */ 905 err = FSUR_INVAL; 906 goto free_err; 907 } 908 a = ctx.a; 909 if (a->non_resident) { 910 err = FSUR_IO_FAIL; 911 goto free_err; 912 } 913 obj_id = (OBJECT_ID_ATTR*)((u8*)a + le16_to_cpu(a->value_offset)); 914 obj_id_len = le32_to_cpu(a->value_length); 915 if ((u8*)obj_id + obj_id_len > (u8*)m + vol.mft_record_size || 916 (u8*)obj_id + obj_id_len > (u8*)a + 917 le32_to_cpu(a->length)) { 918 err = FSUR_IO_FAIL; 919 goto free_err; 920 } 921 guid = &obj_id->object_id; 922 /* Convert guid to utf8 uuid. */ 923 if (snprintf(uuid, 37, "%08x-%04x-%04x-%02x%02x-" 924 "%02x%02x%02x%02x%02x%02x", le32_to_cpu(guid->data1), 925 le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), 926 guid->data4[0], guid->data4[1], guid->data4[2], 927 guid->data4[3], guid->data4[4], guid->data4[5], 928 guid->data4[6], guid->data4[7]) != 36) { 929 err = FSUR_IO_FAIL; 930 goto free_err; 931 } 932 /* Output utf8 uuid to stdout. */ 933#ifdef NTFS_UTIL_DEBUG 934 /* Log to syslog. */ 935 openlog("ntfs.util", LOG_PID, LOG_DAEMON); 936 syslog(LOG_NOTICE, "Volume UUID Key: %s\n", uuid); 937 closelog(); 938#endif /* NTFS_UTIL_DEBUG */ 939 /* 940 * If this fails it is not a problem and there is nothing wrong with 941 * the volume so ignore the return value. 942 */ 943 (void)write(STDOUT_FILENO, uuid, 36); 944 /* Finally done! */ 945 err = FSUR_IO_SUCCESS; 946free_err: 947 free(m); 948err: 949 return err; 950} 951 952/* 953 * Invalid NTFS filename characters are encodeded using the 954 * SFM (Services for Macintosh) private use Unicode characters. 955 * 956 * These should only be used for SMB, MSDOS or NTFS. 957 * 958 * Illegal NTFS Char SFM Unicode Char 959 * ---------------------------------------- 960 * 0x01-0x1f 0xf001-0xf01f 961 * '"' 0xf020 962 * '*' 0xf021 963 * '/' 0xf022 964 * '<' 0xf023 965 * '>' 0xf024 966 * '?' 0xf025 967 * '\' 0xf026 968 * '|' 0xf027 969 * ' ' 0xf028 (Only if last char of the name) 970 * '.' 0xf029 (Only if last char of the name) 971 * ---------------------------------------- 972 * 973 * Reference: http://support.microsoft.com/kb/q117258/ 974 */ 975 976/* 977 * In the Mac OS 9 days the colon was illegal in a file name. For that reason 978 * SFM had no conversion for the colon. There is a conversion for the 979 * slash. In Mac OS X the slash is illegal in a file name. So for us the colon 980 * is a slash and a slash is a colon. So we can just replace the slash with the 981 * colon in our tables and everything will just work. 982 * 983 * SFM conversion code adapted from xnu/bsd/vfs/vfs_utfconf.c. 984 */ 985static u8 sfm2mac[0x30] = { 986 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00 - 07 */ 987 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08 - 0F */ 988 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10 - 17 */ 989 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18 - 1F */ 990 0x22, 0x2a, 0x3a, 0x3c, 0x3e, 0x3f, 0x5c, 0x7c, /* 20 - 27 */ 991 0x20, 0x2e /* 28 - 29 */ 992}; 993 994/** 995 * do_probe - Examine a volume to see if we recognize it as an NTFS volume. 996 * 997 * If the volume is recognized as an NTFS volume look up its volume label and 998 * output it to stdout then return FSUR_RECOGNIZED. 999 * 1000 * If the volume is not an NTFS volume return FSUR_UNRECOGNIZED. 1001 * 1002 * On error return FSUR_INVAL or FSUR_IO_FAIL. 1003 */ 1004static int do_probe(char *rdev, const BOOL removable __attribute__((unused)), 1005 const BOOL writable __attribute__((unused))) 1006{ 1007 MFT_RECORD *m; 1008 ATTR_RECORD *a; 1009 ntfschar *uname; 1010 CFStringRef s; 1011 char *label; 1012 size_t label_len; 1013 unsigned uname_len, i; 1014 int err; 1015 CFIndex label_size; 1016 ntfs_volume vol; 1017 ntfs_attr_search_ctx ctx; 1018 1019 /* Obtain the mft record for $Volume. */ 1020 err = get_volume_mft_record(rdev, &vol, &m); 1021 if (err != FSUR_RECOGNIZED) 1022 goto err; 1023 /* Lookup $VOLUME_NAME attribute. */ 1024 ntfs_attr_search_ctx_init(&ctx, m); 1025 err = ntfs_attr_find_in_mft_record(AT_VOLUME_NAME, &ctx); 1026 if (err) { 1027 if (err != FSUR_UNRECOGNIZED) 1028 goto free_err; 1029 /* There is no volume label which is fine. */ 1030#ifdef NTFS_UTIL_DEBUG 1031 /* Log to syslog. */ 1032 openlog("ntfs.util", LOG_PID, LOG_DAEMON); 1033 syslog(LOG_NOTICE, "Volume is not labelled.\n"); 1034 closelog(); 1035#endif /* NTFS_UTIL_DEBUG */ 1036 err = FSUR_RECOGNIZED; 1037 goto free_err; 1038 } 1039 a = ctx.a; 1040 if (a->non_resident) { 1041 err = FSUR_IO_FAIL; 1042 goto free_err; 1043 } 1044 uname = (ntfschar*)((u8*)a + le16_to_cpu(a->value_offset)); 1045 uname_len = le32_to_cpu(a->value_length); 1046 if ((u8*)uname + uname_len > (u8*)m + vol.mft_record_size || 1047 (u8*)uname + uname_len > (u8*)a + 1048 le32_to_cpu(a->length)) { 1049 err = FSUR_IO_FAIL; 1050 goto free_err; 1051 } 1052 /* Convert length to number of Unicode characters. */ 1053 uname_len /= sizeof(ntfschar); 1054 /* 1055 * Scan through the name looking for any Services For Macintosh encoded 1056 * characters and if any are found replace them with the decoded ones. 1057 * We need to do this as the Core Foundation string handling routines 1058 * are not aware of the SFM encodings. 1059 */ 1060 for (i = 0; i < uname_len; i++) { 1061 ntfschar c; 1062 1063 c = le16_to_cpu(uname[i]); 1064 if ((c & 0xffc0) == 0xf000) { 1065 c &= 0x003f; 1066 if (c <= 0x29) 1067 uname[i] = cpu_to_le16(sfm2mac[c]); 1068 } 1069 } 1070 /* Convert volume name to utf8. */ 1071 s = CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)uname, 1072 uname_len * sizeof(ntfschar), kCFStringEncodingUTF16LE, 1073 true); 1074 if (!s) { 1075 err = FSUR_IO_FAIL; 1076 goto free_err; 1077 } 1078 label_size = CFStringGetMaximumSizeOfFileSystemRepresentation(s); 1079 label = malloc(label_size); 1080 if (!label) { 1081 err = FSUR_IO_FAIL; 1082 goto s_free_err; 1083 } 1084 if (!CFStringGetFileSystemRepresentation(s, label, label_size)) { 1085 err = FSUR_IO_FAIL; 1086 goto label_free_err; 1087 } 1088 /* Output volume label (now in utf8) to stdout. */ 1089 label_len = strlen(label); 1090 if (label_len > (size_t)label_size) { 1091 err = FSUR_IO_FAIL; 1092 goto label_free_err; 1093 } 1094#ifdef NTFS_UTIL_DEBUG 1095 /* Log to syslog. */ 1096 openlog("ntfs.util", LOG_PID, LOG_DAEMON); 1097 syslog(LOG_NOTICE, "Volume label: %s\n", label); 1098 closelog(); 1099#endif /* NTFS_UTIL_DEBUG */ 1100 /* 1101 * If this fails it is not a problem and there is nothing wrong with 1102 * the volume so ignore the return value. 1103 */ 1104 (void)write(STDOUT_FILENO, label, label_len); 1105 /* Finally done! */ 1106 err = FSUR_RECOGNIZED; 1107label_free_err: 1108 free(label); 1109s_free_err: 1110 CFRelease(s); 1111free_err: 1112 free(m); 1113err: 1114 return err; 1115} 1116 1117/** 1118 * do_exec - Execute an external command. 1119 */ 1120static int do_exec(const char *progname, char *const args[]) 1121{ 1122 pid_t pid; 1123 union wait status; 1124 int err; 1125 1126 pid = fork(); 1127 if (pid == -1) { 1128 fprintf(stderr, "%s: fork failed: %s\n", progname, 1129 strerror(errno)); 1130 return FSUR_INVAL; 1131 } 1132 if (!pid) { 1133 /* In child process, execute external command. */ 1134 (void)execv(args[0], args); 1135 /* We only get here if the execv() failed. */ 1136 err = errno; 1137 fprintf(stderr, "%s: execv %s failed: %s\n", progname, args[0], 1138 strerror(err)); 1139 exit(err); 1140 } 1141 /* In parent process, wait for exernal command to finish. */ 1142 if (wait4(pid, (int*)&status, 0, NULL) != pid) { 1143 fprintf(stderr, "%s: BUG executing %s command.\n", progname, 1144 args[0]); 1145 return FSUR_INVAL; 1146 } 1147 if (!WIFEXITED(status)) { 1148 fprintf(stderr, "%s: %s command aborted by signal %d.\n", 1149 progname, args[0], WTERMSIG(status)); 1150 return FSUR_INVAL; 1151 } 1152 err = WEXITSTATUS(status); 1153 if (err) { 1154 fprintf(stderr, "%s: %s command failed: %s\n", progname, 1155 args[0], strerror(err)); 1156 return FSUR_IO_FAIL; 1157 } 1158 return FSUR_IO_SUCCESS; 1159} 1160 1161/** 1162 * do_mount - Mount a file system. 1163 */ 1164static int do_mount(const char *progname, char *dev, char *mp, 1165 const BOOL removable __attribute__((unused)), 1166 const BOOL readonly, const BOOL nosuid, const BOOL nodev) 1167{ 1168 char *const kextargs[] = { "/sbin/kextload", 1169 "/System/Library/Extensions/ntfs.kext", NULL }; 1170 char *mountargs[] = { "/sbin/mount", "-w", "-o", 1171 "suid", "-o", "dev", "-t", "ntfs", dev, mp, NULL }; 1172 struct vfsconf vfc; 1173 1174 if (!mp || !strlen(mp)) 1175 return FSUR_INVAL; 1176 if (readonly) 1177 mountargs[1] = "-r"; 1178 if (nosuid) 1179 mountargs[3] = "nosuid"; 1180 if (nodev) 1181 mountargs[5] = "nodev"; 1182 /* 1183 * If the kext is not loaded, load it now. Ignore any errors as the 1184 * mount will fail appropriately if the kext is not loaded. 1185 */ 1186 if (getvfsbyname("ntfs", &vfc)) 1187 (void)do_exec(progname, kextargs); 1188 return do_exec(progname, mountargs); 1189} 1190 1191/** 1192 * do_unmount - Unmount a volume. 1193 */ 1194static int do_unmount(const char *progname, char *mp) 1195{ 1196 char *const umountargs[] = { "/sbin/umount", mp, NULL }; 1197 1198 if (!mp || !strlen(mp)) 1199 return FSUR_INVAL; 1200 return do_exec(progname, umountargs); 1201} 1202 1203/** 1204 * main - Main function, parse arguments and cause required action to be taken. 1205 */ 1206int main(int argc, char **argv) 1207{ 1208 char *progname, *dev, *mp = NULL; 1209 int err; 1210 char opt; 1211 BOOL removable, readonly, nosuid, nodev; 1212 char rawdev[MAXPATHLEN]; 1213 char blockdev[MAXPATHLEN]; 1214 struct stat sb; 1215 1216 nodev = nosuid = readonly = removable = FALSE; 1217 /* Save & strip off program name. */ 1218 progname = argv[0]; 1219 argc--; 1220 argv++; 1221#ifdef NTFS_UTIL_DEBUG 1222 /* Log to syslog. */ 1223 openlog("ntfs.util", LOG_PID, LOG_DAEMON); 1224 if (argc == 0) 1225 syslog(LOG_NOTICE, "Called without arguments!"); 1226 else if (argc == 1) 1227 syslog(LOG_NOTICE, "Called with %d arguments: %s", argc, 1228 argv[0]); 1229 else if (argc == 2) 1230 syslog(LOG_NOTICE, "Called with %d arguments: %s %s", argc, 1231 argv[0], argv[1]); 1232 else if (argc == 3) 1233 syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s", argc, 1234 argv[0], argv[1], argv[2]); 1235 else if (argc == 4) 1236 syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s", 1237 argc, argv[0], argv[1], argv[2], argv[3]); 1238 else if (argc == 5) 1239 syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s", 1240 argc, argv[0], argv[1], argv[2], argv[3], 1241 argv[4]); 1242 else if (argc == 6) 1243 syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s " 1244 "%s", argc, argv[0], argv[1], argv[2], argv[3], 1245 argv[4], argv[5]); 1246 else 1247 syslog(LOG_NOTICE, "Called with %d arguments: %s %s %s %s %s " 1248 "%s %s", argc, argv[0], argv[1], argv[2], 1249 argv[3], argv[4], argv[5], argv[6]); 1250 closelog(); 1251#endif /* NTFS_UTIL_DEBUG */ 1252 /* 1253 * We support probe, mount, and unmount all of which need the command 1254 * option and the device. 1255 */ 1256 if (argc < 2 || argv[0][0] != '-') 1257 usage(progname); 1258 opt = argv[0][1]; 1259 dev = argv[1]; 1260 argc -= 2; 1261 argv += 2; 1262 /* Check we have the right number of arguments. */ 1263 switch (opt) { 1264 case FSUC_GETUUID: 1265 /* For get UUID key do not need any more arguments. */ 1266 if (argc) 1267 usage(progname); 1268 break; 1269 case FSUC_PROBE: 1270 /* For probe need the two mountflags also. */ 1271 if (argc != 2) 1272 usage(progname); 1273 break; 1274 case FSUC_MOUNT: 1275 /* For mount need the mount point and four mountflags also. */ 1276 if (argc != 5) 1277 usage(progname); 1278 break; 1279 case FSUC_UNMOUNT: 1280 /* For unmount need the mount point also. */ 1281 if (argc != 1) 1282 usage(progname); 1283 break; 1284 default: 1285 /* Unsupported command. */ 1286 usage(progname); 1287 break; 1288 } 1289 /* Check the raw and block device special files exist. */ 1290 err = snprintf(rawdev, sizeof(rawdev), "/dev/r%s", dev); 1291 if (err >= (int)sizeof(rawdev)) { 1292 fprintf(stderr, "%s: Specified device name is too long.\n", 1293 progname); 1294 exit(FSUR_INVAL); 1295 } 1296 if (stat(rawdev, &sb)) { 1297 fprintf(stderr, "%s: stat %s failed, %s\n", progname, rawdev, 1298 strerror(errno)); 1299 exit(FSUR_INVAL); 1300 } 1301 err = snprintf(blockdev, sizeof(blockdev), "/dev/%s", dev); 1302 if (err >= (int)sizeof(blockdev)) { 1303 fprintf(stderr, "%s: Specified device name is too long.\n", 1304 progname); 1305 exit(FSUR_INVAL); 1306 } 1307 if (stat(blockdev, &sb)) { 1308 fprintf(stderr, "%s: stat %s failed, %s\n", progname, blockdev, 1309 strerror(errno)); 1310 exit(FSUR_INVAL); 1311 } 1312 /* Get the mount point for the mount and unmount cases. */ 1313 if (opt == FSUC_MOUNT || opt == FSUC_UNMOUNT) { 1314 mp = argv[0]; 1315 argc--; 1316 argv++; 1317 } 1318 /* Get the mount flags for the probe and mount cases. */ 1319 if (opt == FSUC_PROBE || opt == FSUC_MOUNT) { 1320 /* mountflag1: Removable or fixed. */ 1321 if (!strcmp(argv[0], DEVICE_REMOVABLE)) 1322 removable = TRUE; 1323 else if (strcmp(argv[0], DEVICE_FIXED)) 1324 usage(progname); 1325 /* mountflag2: Readonly or writable. */ 1326 if (!strcmp(argv[1], DEVICE_READONLY)) 1327 readonly = TRUE; 1328 else if (strcmp(argv[1], DEVICE_WRITABLE)) 1329 usage(progname); 1330 /* Only the mount command supports the third and fourth flag. */ 1331 if (opt == FSUC_MOUNT) { 1332 /* mountflag3: Nosuid or suid. */ 1333 if (!strcmp(argv[2], "nosuid")) 1334 nosuid = TRUE; 1335 else if (strcmp(argv[2], "suid")) 1336 usage(progname); 1337 /* mountflag4: Nodev or dev. */ 1338 if (!strcmp(argv[3], "nodev")) 1339 nodev = TRUE; 1340 else if (strcmp(argv[3], "dev")) 1341 usage(progname); 1342 } 1343 } 1344 /* Finally execute the required command. */ 1345 switch (opt) { 1346 case FSUC_GETUUID: 1347 err = do_getuuid(rawdev); 1348 break; 1349 case FSUC_PROBE: 1350 err = do_probe(rawdev, removable, readonly); 1351 break; 1352 case FSUC_MOUNT: 1353 err = do_mount(progname, blockdev, mp, removable, readonly, 1354 nosuid, nodev); 1355 break; 1356 case FSUC_UNMOUNT: 1357 err = do_unmount(progname, mp); 1358 break; 1359 default: 1360 /* Cannot happen... */ 1361 usage(progname); 1362 break; 1363 } 1364 return err; 1365} 1366