diff options
Diffstat (limited to 'usr/src/cmd/ntfsprogs/mkntfs.c')
-rw-r--r-- | usr/src/cmd/ntfsprogs/mkntfs.c | 4748 |
1 files changed, 0 insertions, 4748 deletions
diff --git a/usr/src/cmd/ntfsprogs/mkntfs.c b/usr/src/cmd/ntfsprogs/mkntfs.c deleted file mode 100644 index e23f6a7848..0000000000 --- a/usr/src/cmd/ntfsprogs/mkntfs.c +++ /dev/null @@ -1,4748 +0,0 @@ -/** - * mkntfs - Part of the Linux-NTFS project. - * - * Copyright (c) 2000-2007 Anton Altaparmakov - * Copyright (c) 2001-2005 Richard Russon - * Copyright (c) 2002-2006 Szabolcs Szakacsits - * Copyright (c) 2005 Erik Sornes - * Copyright (c) 2007 Yura Pakhuchiy - * - * This utility will create an NTFS 1.2 or 3.1 volume on a user - * specified (block) device. - * - * Some things (option handling and determination of mount status) have been - * adapted from e2fsprogs-1.19 and lib/ext2fs/ismounted.c and misc/mke2fs.c in - * particular. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program (in the main directory of the Linux-NTFS source - * in the file COPYING); if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif -#ifdef HAVE_STDIO_H -#include <stdio.h> -#endif -#ifdef HAVE_STDARG_H -#include <stdarg.h> -#endif -#ifdef HAVE_STRING_H -#include <string.h> -#endif -#ifdef HAVE_ERRNO_H -#include <errno.h> -#endif -#ifdef HAVE_TIME_H -#include <time.h> -#endif -#ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif -#ifdef HAVE_LIBGEN_H -#include <libgen.h> -#endif -#ifdef ENABLE_UUID -#include <uuid/uuid.h> -#endif - - -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#else - extern char *optarg; - extern int optind; -#endif - -#ifdef HAVE_LINUX_MAJOR_H -# include <linux/major.h> -# ifndef MAJOR -# define MAJOR(dev) ((dev) >> 8) -# define MINOR(dev) ((dev) & 0xff) -# endif -# ifndef IDE_DISK_MAJOR -# ifndef IDE0_MAJOR -# define IDE0_MAJOR 3 -# define IDE1_MAJOR 22 -# define IDE2_MAJOR 33 -# define IDE3_MAJOR 34 -# define IDE4_MAJOR 56 -# define IDE5_MAJOR 57 -# define IDE6_MAJOR 88 -# define IDE7_MAJOR 89 -# define IDE8_MAJOR 90 -# define IDE9_MAJOR 91 -# endif -# define IDE_DISK_MAJOR(M) \ - ((M) == IDE0_MAJOR || (M) == IDE1_MAJOR || \ - (M) == IDE2_MAJOR || (M) == IDE3_MAJOR || \ - (M) == IDE4_MAJOR || (M) == IDE5_MAJOR || \ - (M) == IDE6_MAJOR || (M) == IDE7_MAJOR || \ - (M) == IDE8_MAJOR || (M) == IDE9_MAJOR) -# endif -# ifndef SCSI_DISK_MAJOR -# ifndef SCSI_DISK0_MAJOR -# define SCSI_DISK0_MAJOR 8 -# define SCSI_DISK1_MAJOR 65 -# define SCSI_DISK7_MAJOR 71 -# endif -# define SCSI_DISK_MAJOR(M) \ - ((M) == SCSI_DISK0_MAJOR || \ - ((M) >= SCSI_DISK1_MAJOR && \ - (M) <= SCSI_DISK7_MAJOR)) -# endif -#endif - -#include "compat.h" -#include "security.h" -#include "types.h" -#include "attrib.h" -#include "bitmap.h" -#include "bootsect.h" -#include "device.h" -#include "dir.h" -#include "mft.h" -#include "mst.h" -#include "runlist.h" -#include "utils.h" -#include "ntfstime.h" -#include "sd.h" -#include "boot.h" -#include "attrdef.h" -#include "version.h" -#include "logging.h" -#include "support.h" -#include "unistr.h" - -#ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS -#error "No default device io operations! Cannot build mkntfs. \ -You need to run ./configure without the --disable-default-device-io-ops \ -switch if you want to be able to build the NTFS utilities." -#endif - -/* Page size on ia32. Can change to 8192 on Alpha. */ -#define NTFS_PAGE_SIZE 4096 - -static char EXEC_NAME[] = "mkntfs"; - -/** - * global variables - */ -static u8 *g_buf = NULL; -static int g_mft_bitmap_byte_size = 0; -static u8 *g_mft_bitmap = NULL; -static int g_lcn_bitmap_byte_size = 0; -static u8 *g_lcn_bitmap = NULL; -static runlist *g_rl_mft = NULL; -static runlist *g_rl_mft_bmp = NULL; -static runlist *g_rl_mftmirr = NULL; -static runlist *g_rl_logfile = NULL; -static runlist *g_rl_boot = NULL; -static runlist *g_rl_bad = NULL; -static INDEX_ALLOCATION *g_index_block = NULL; -static ntfs_volume *g_vol = NULL; -static int g_mft_size = 0; -static long long g_mft_lcn = 0; /* lcn of $MFT, $DATA attribute */ -static long long g_mftmirr_lcn = 0; /* lcn of $MFTMirr, $DATA */ -static long long g_logfile_lcn = 0; /* lcn of $LogFile, $DATA */ -static int g_logfile_size = 0; /* in bytes, determined from volume_size */ -static long long g_mft_zone_end = 0; /* Determined from volume_size and mft_zone_multiplier, in clusters */ -static long long g_num_bad_blocks = 0; /* Number of bad clusters */ -static long long *g_bad_blocks = NULL; /* Array of bad clusters */ - -/** - * struct mkntfs_options - */ -static struct mkntfs_options { - char *dev_name; /* Name of the device, or file, to use */ - BOOL enable_compression; /* -C, enables compression of all files on the volume by default. */ - BOOL quick_format; /* -f or -Q, fast format, don't zero the volume first. */ - BOOL force; /* -F, force fs creation. */ - long heads; /* -H, number of heads on device */ - BOOL disable_indexing; /* -I, disables indexing of file contents on the volume by default. */ - BOOL no_action; /* -n, do not write to device, only display what would be done. */ - long long part_start_sect; /* -p, start sector of partition on parent device */ - long sector_size; /* -s, in bytes, power of 2, default is 512 bytes. */ - long sectors_per_track; /* -S, number of sectors per track on device */ - BOOL use_epoch_time; /* -T, fake the time to be 00:00:00 UTC, Jan 1, 1970. */ - long mft_zone_multiplier; /* -z, value from 1 to 4. Default is 1. */ - long long num_sectors; /* size of device in sectors */ - long cluster_size; /* -c, format with this cluster-size */ - char *label; /* -L, volume label */ -} opts; - - -/** - * mkntfs_license - */ -static void mkntfs_license(void) -{ - ntfs_log_info("%s", ntfs_gpl); -} - -/** - * mkntfs_usage - */ -static void mkntfs_usage(void) -{ - ntfs_log_info("\nUsage: %s [options] device [number-of-sectors]\n" -"\n" -"Basic options:\n" -" -f, --fast Perform a quick format\n" -" -Q, --quick Perform a quick format\n" -" -L, --label STRING Set the volume label\n" -" -C, --enable-compression Enable compression on the volume\n" -" -I, --no-indexing Disable indexing on the volume\n" -" -n, --no-action Do not write to disk\n" -"\n" -"Advanced options:\n" -" -c, --cluster-size BYTES Specify the cluster size for the volume\n" -" -s, --sector-size BYTES Specify the sector size for the device\n" -" -p, --partition-start SECTOR Specify the partition start sector\n" -" -H, --heads NUM Specify the number of heads\n" -" -S, --sectors-per-track NUM Specify the number of sectors per track\n" -" -z, --mft-zone-multiplier NUM Set the MFT zone multiplier\n" -" -T, --zero-time Fake the time to be 00:00 UTC, Jan 1, 1970\n" -" -F, --force Force execution despite errors\n" -"\n" -"Output options:\n" -" -q, --quiet Quiet execution\n" -" -v, --verbose Verbose execution\n" -" --debug Very verbose execution\n" -"\n" -"Help options:\n" -" -V, --version Display version\n" -" -l, --license Display licensing information\n" -" -h, --help Display this help\n" -"\n", basename(EXEC_NAME)); - ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); -} - -/** - * mkntfs_version - */ -static void mkntfs_version(void) -{ - ntfs_log_info("\n%s v%s (libntfs %s)\n\n", EXEC_NAME, VERSION, - ntfs_libntfs_version()); - ntfs_log_info("Create an NTFS volume on a user specified (block) " - "device.\n\n"); - ntfs_log_info("Copyright (c) 2000-2007 Anton Altaparmakov\n"); - ntfs_log_info("Copyright (c) 2001-2005 Richard Russon\n"); - ntfs_log_info("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); - ntfs_log_info("Copyright (c) 2005 Erik Sornes\n"); - ntfs_log_info("Copyright (c) 2007 Yura Pakhuchiy\n"); - ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); -} - - -/** - * mkntfs_parse_long - */ -static BOOL mkntfs_parse_long(const char *string, const char *name, long *num) -{ - char *end = NULL; - long tmp; - - if (!string || !name || !num) - return FALSE; - - if (*num >= 0) { - ntfs_log_error("You may only specify the %s once.\n", name); - return FALSE; - } - - tmp = strtol(string, &end, 0); - if (end && *end) { - ntfs_log_error("Cannot understand the %s '%s'.\n", name, string); - return FALSE; - } else { - *num = tmp; - return TRUE; - } -} - -/** - * mkntfs_parse_llong - */ -static BOOL mkntfs_parse_llong(const char *string, const char *name, - long long *num) -{ - char *end = NULL; - long long tmp; - - if (!string || !name || !num) - return FALSE; - - if (*num >= 0) { - ntfs_log_error("You may only specify the %s once.\n", name); - return FALSE; - } - - tmp = strtoll(string, &end, 0); - if (end && *end) { - ntfs_log_error("Cannot understand the %s '%s'.\n", name, - string); - return FALSE; - } else { - *num = tmp; - return TRUE; - } -} - -/** - * mkntfs_init_options - */ -static void mkntfs_init_options(struct mkntfs_options *opts2) -{ - if (!opts2) - return; - - memset(opts2, 0, sizeof(*opts2)); - - /* Mark all the numeric options as "unset". */ - opts2->cluster_size = -1; - opts2->heads = -1; - opts2->mft_zone_multiplier = -1; - opts2->num_sectors = -1; - opts2->part_start_sect = -1; - opts2->sector_size = -1; - opts2->sectors_per_track = -1; -} - -/** - * mkntfs_parse_options - */ -static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options *opts2) -{ - static const char *sopt = "-c:CfFhH:IlL:np:qQs:S:TvVz:"; - static const struct option lopt[] = { - { "cluster-size", required_argument, NULL, 'c' }, - { "debug", no_argument, NULL, 'Z' }, - { "enable-compression", no_argument, NULL, 'C' }, - { "fast", no_argument, NULL, 'f' }, - { "force", no_argument, NULL, 'F' }, - { "heads", required_argument, NULL, 'H' }, - { "help", no_argument, NULL, 'h' }, - { "label", required_argument, NULL, 'L' }, - { "license", no_argument, NULL, 'l' }, - { "mft-zone-multiplier",required_argument, NULL, 'z' }, - { "no-action", no_argument, NULL, 'n' }, - { "no-indexing", no_argument, NULL, 'I' }, - { "partition-start", required_argument, NULL, 'p' }, - { "quick", no_argument, NULL, 'Q' }, - { "quiet", no_argument, NULL, 'q' }, - { "sector-size", required_argument, NULL, 's' }, - { "sectors-per-track", required_argument, NULL, 'S' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { "zero-time", no_argument, NULL, 'T' }, - { NULL, 0, NULL, 0 } - }; - - int c = -1; - int lic = 0; - int err = 0; - int ver = 0; - - if (!argv || !opts2) { - ntfs_log_error("Internal error: invalid parameters to " - "mkntfs_options.\n"); - return FALSE; - } - - opterr = 0; /* We'll handle the errors, thank you. */ - - while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { - switch (c) { - case 1: /* A device, or a number of sectors */ - if (!opts2->dev_name) - opts2->dev_name = argv[optind - 1]; - else if (!mkntfs_parse_llong(optarg, - "number of sectors", - &opts2->num_sectors)) - err++; - break; - case 'C': - opts2->enable_compression = TRUE; - break; - case 'c': - if (!mkntfs_parse_long(optarg, "cluster size", - &opts2->cluster_size)) - err++; - break; - case 'F': - opts2->force = TRUE; - break; - case 'f': /* fast */ - case 'Q': /* quick */ - opts2->quick_format = TRUE; - break; - case 'H': - if (!mkntfs_parse_long(optarg, "heads", &opts2->heads)) - err++; - break; - case 'h': - err++; /* display help */ - break; - case 'I': - opts2->disable_indexing = TRUE; - break; - case 'L': - if (!opts2->label) { - opts2->label = argv[optind-1]; - } else { - ntfs_log_error("You may only specify the label " - "once.\n"); - err++; - } - break; - case 'l': - lic++; /* display the license */ - break; - case 'n': - opts2->no_action = TRUE; - break; - case 'p': - if (!mkntfs_parse_llong(optarg, "partition start", - &opts2->part_start_sect)) - err++; - break; - case 'q': - ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET | - NTFS_LOG_LEVEL_VERBOSE | - NTFS_LOG_LEVEL_PROGRESS); - break; - case 's': - if (!mkntfs_parse_long(optarg, "sector size", - &opts2->sector_size)) - err++; - break; - case 'S': - if (!mkntfs_parse_long(optarg, "sectors per track", - &opts2->sectors_per_track)) - err++; - break; - case 'T': - opts2->use_epoch_time = TRUE; - break; - case 'v': - ntfs_log_set_levels(NTFS_LOG_LEVEL_QUIET | - NTFS_LOG_LEVEL_VERBOSE | - NTFS_LOG_LEVEL_PROGRESS); - break; - case 'V': - ver++; /* display version info */ - break; - case 'Z': /* debug - turn on everything */ - ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | - NTFS_LOG_LEVEL_TRACE | - NTFS_LOG_LEVEL_VERBOSE | - NTFS_LOG_LEVEL_QUIET); - break; - case 'z': - if (!mkntfs_parse_long(optarg, "mft zone multiplier", - &opts2->mft_zone_multiplier)) - err++; - break; - default: - if (ntfs_log_parse_option (argv[optind-1])) - break; - if (((optopt == 'c') || (optopt == 'H') || - (optopt == 'L') || (optopt == 'p') || - (optopt == 's') || (optopt == 'S') || - (optopt == 'N') || (optopt == 'z')) && - (!optarg)) { - ntfs_log_error("Option '%s' requires an " - "argument.\n", argv[optind-1]); - } else if (optopt != '?') { - ntfs_log_error("Unknown option '%s'.\n", - argv[optind - 1]); - } - err++; - break; - } - } - - if (!err && !ver && !lic) { - if (opts2->dev_name == NULL) { - if (argc > 1) - ntfs_log_error("You must specify a device.\n"); - err++; - } - } - - if (ver) - mkntfs_version(); - if (lic) - mkntfs_license(); - if (err) - mkntfs_usage(); - - return (!err && !ver && !lic); -} - - -/** - * mkntfs_time - */ -static time_t mkntfs_time(void) -{ - if (!opts.use_epoch_time) - return time(NULL); - return 0; -} - -/** - * append_to_bad_blocks - */ -static BOOL append_to_bad_blocks(unsigned long long block) -{ - long long *new_buf; - - if (!(g_num_bad_blocks & 15)) { - new_buf = realloc(g_bad_blocks, (g_num_bad_blocks + 16) * - sizeof(long long)); - if (!new_buf) { - ntfs_log_perror("Reallocating memory for bad blocks " - "list failed"); - return FALSE; - } - g_bad_blocks = new_buf; - } - g_bad_blocks[g_num_bad_blocks++] = block; - return TRUE; -} - -/** - * mkntfs_write - */ -static long long mkntfs_write(struct ntfs_device *dev, - const void *b, long long count) -{ - long long bytes_written, total; - int retry; - - if (opts.no_action) - return count; - total = 0LL; - retry = 0; - do { - bytes_written = dev->d_ops->write(dev, b, count); - if (bytes_written == -1LL) { - retry = errno; - ntfs_log_perror("Error writing to %s", dev->d_name); - errno = retry; - return bytes_written; - } else if (!bytes_written) { - retry++; - } else { - count -= bytes_written; - total += bytes_written; - } - } while (count && retry < 3); - if (count) - ntfs_log_error("Failed to complete writing to %s after three retries." - "\n", dev->d_name); - return total; -} - -/** - * ntfs_rlwrite - Write data to disk on clusters found in a runlist. - * - * Write to disk the clusters contained in the runlist @rl taking the data - * from @val. Take @val_len bytes from @val and pad the rest with zeroes. - * - * If the @rl specifies a completely sparse file, @val is allowed to be NULL. - * - * @inited_size if not NULL points to an output variable which will contain - * the actual number of bytes written to disk. I.e. this will not include - * sparse bytes for example. - * - * Return the number of bytes written (minus padding) or -1 on error. Errno - * will be set to the error code. - */ -static s64 ntfs_rlwrite(struct ntfs_device *dev, const runlist *rl, - const u8 *val, const s64 val_len, s64 *inited_size) -{ - s64 bytes_written, total, length, delta; - int retry, i; - - if (inited_size) - *inited_size = 0LL; - if (opts.no_action) - return val_len; - total = 0LL; - delta = 0LL; - for (i = 0; rl[i].length; i++) { - length = rl[i].length * g_vol->cluster_size; - /* Don't write sparse runs. */ - if (rl[i].lcn == -1) { - total += length; - if (!val) - continue; - /* TODO: Check that *val is really zero at pos and len. */ - continue; - } - /* - * Break up the write into the real data write and then a write - * of zeroes between the end of the real data and the end of - * the (last) run. - */ - if (total + length > val_len) { - delta = length; - length = val_len - total; - delta -= length; - } - if (dev->d_ops->seek(dev, rl[i].lcn * g_vol->cluster_size, - SEEK_SET) == (off_t)-1) - return -1LL; - retry = 0; - do { - bytes_written = dev->d_ops->write(dev, val + total, - length); - if (bytes_written == -1LL) { - retry = errno; - ntfs_log_perror("Error writing to %s", - dev->d_name); - errno = retry; - return bytes_written; - } - if (bytes_written) { - length -= bytes_written; - total += bytes_written; - if (inited_size) - *inited_size += bytes_written; - } else { - retry++; - } - } while (length && retry < 3); - if (length) { - ntfs_log_error("Failed to complete writing to %s after three " - "retries.\n", dev->d_name); - return total; - } - } - if (delta) { - int eo; - char *b = ntfs_calloc(delta); - if (!b) - return -1; - bytes_written = mkntfs_write(dev, b, delta); - eo = errno; - free(b); - errno = eo; - if (bytes_written == -1LL) - return bytes_written; - } - return total; -} - -/** - * make_room_for_attribute - make room for an attribute inside an mft record - * @m: mft record - * @pos: position at which to make space - * @size: byte size to make available at this position - * - * @pos points to the attribute in front of which we want to make space. - * - * Return 0 on success or -errno on error. Possible error codes are: - * - * -ENOSPC There is not enough space available to complete - * operation. The caller has to make space before calling - * this. - * -EINVAL Can only occur if mkntfs was compiled with -DDEBUG. Means - * the input parameters were faulty. - */ -static int make_room_for_attribute(MFT_RECORD *m, char *pos, const u32 size) -{ - u32 biu; - - if (!size) - return 0; -#ifdef DEBUG - /* - * Rigorous consistency checks. Always return -EINVAL even if more - * appropriate codes exist for simplicity of parsing the return value. - */ - if (size != ((size + 7) & ~7)) { - ntfs_log_error("make_room_for_attribute() received non 8-byte aligned " - "size.\n"); - return -EINVAL; - } - if (!m || !pos) - return -EINVAL; - if (pos < (char*)m || pos + size < (char*)m || - pos > (char*)m + le32_to_cpu(m->bytes_allocated) || - pos + size > (char*)m + le32_to_cpu(m->bytes_allocated)) - return -EINVAL; - /* The -8 is for the attribute terminator. */ - if (pos - (char*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) - return -EINVAL; -#endif - biu = le32_to_cpu(m->bytes_in_use); - /* Do we have enough space? */ - if (biu + size > le32_to_cpu(m->bytes_allocated)) - return -ENOSPC; - /* Move everything after pos to pos + size. */ - memmove(pos + size, pos, biu - (pos - (char*)m)); - /* Update mft record. */ - m->bytes_in_use = cpu_to_le32(biu + size); - return 0; -} - -/** - * deallocate_scattered_clusters - */ -static void deallocate_scattered_clusters(const runlist *rl) -{ - LCN j; - int i; - - if (!rl) - return; - /* Iterate over all runs in the runlist @rl. */ - for (i = 0; rl[i].length; i++) { - /* Skip sparse runs. */ - if (rl[i].lcn == -1LL) - continue; - /* Deallocate the current run. */ - for (j = rl[i].lcn; j < rl[i].lcn + rl[i].length; j++) - ntfs_bit_set(g_lcn_bitmap, j, 0); - } -} - -/** - * allocate_scattered_clusters - * @clusters: Amount of clusters to allocate. - * - * Allocate @clusters and create a runlist of the allocated clusters. - * - * Return the allocated runlist. Caller has to free the runlist when finished - * with it. - * - * On error return NULL and errno is set to the error code. - * - * TODO: We should be returning the size as well, but for mkntfs this is not - * necessary. - */ -static runlist * allocate_scattered_clusters(s64 clusters) -{ - runlist *rl = NULL, *rlt; - VCN vcn = 0LL; - LCN lcn, end, prev_lcn = 0LL; - int rlpos = 0; - int rlsize = 0; - s64 prev_run_len = 0LL; - char bit; - - end = g_vol->nr_clusters; - /* Loop until all clusters are allocated. */ - while (clusters) { - /* Loop in current zone until we run out of free clusters. */ - for (lcn = g_mft_zone_end; lcn < end; lcn++) { - bit = ntfs_bit_get_and_set(g_lcn_bitmap, lcn, 1); - if (bit) - continue; - /* - * Reallocate memory if necessary. Make sure we have - * enough for the terminator entry as well. - */ - if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) { - rlsize += 4096; /* PAGE_SIZE */ - rlt = realloc(rl, rlsize); - if (!rlt) - goto err_end; - rl = rlt; - } - /* Coalesce with previous run if adjacent LCNs. */ - if (prev_lcn == lcn - prev_run_len) { - rl[rlpos - 1].length = ++prev_run_len; - vcn++; - } else { - rl[rlpos].vcn = vcn++; - rl[rlpos].lcn = lcn; - prev_lcn = lcn; - rl[rlpos].length = 1LL; - prev_run_len = 1LL; - rlpos++; - } - /* Done? */ - if (!--clusters) { - /* Add terminator element and return. */ - rl[rlpos].vcn = vcn; - rl[rlpos].lcn = 0LL; - rl[rlpos].length = 0LL; - return rl; - } - - } - /* Switch to next zone, decreasing mft zone by factor 2. */ - end = g_mft_zone_end; - g_mft_zone_end >>= 1; - /* Have we run out of space on the volume? */ - if (g_mft_zone_end <= 0) - goto err_end; - } - return rl; -err_end: - if (rl) { - /* Add terminator element. */ - rl[rlpos].vcn = vcn; - rl[rlpos].lcn = -1LL; - rl[rlpos].length = 0LL; - /* Deallocate all allocated clusters. */ - deallocate_scattered_clusters(rl); - /* Free the runlist. */ - free(rl); - } - return NULL; -} - -/** - * ntfs_attr_find - find (next) attribute in mft record - * @type: attribute type to find - * @name: attribute name to find (optional, i.e. NULL means don't care) - * @name_len: attribute name length (only needed if @name present) - * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * You shouldn't need to call this function directly. Use lookup_attr() instead. - * - * ntfs_attr_find() takes a search context @ctx as parameter and searches the - * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an - * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() - * returns 0 and @ctx->attr will point to the found attribute. - * - * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and - * @ctx->attr will point to the attribute before which the attribute being - * searched for would need to be inserted if such an action were to be desired. - * - * On actual error, ntfs_attr_find() returns -1 with errno set to the error - * code but not to ENOENT. In this case @ctx->attr is undefined and in - * particular do not rely on it not changing. - * - * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it - * is FALSE, the search begins after @ctx->attr. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to - * indicate that there are no more entries. During the enumeration, each - * successful call of ntfs_attr_find() will return the next attribute in the - * mft record @ctx->mrec. - * - * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. - * AT_END is not a valid attribute, its length is zero for example, thus it is - * safer to return error instead of success in this case. This also allows us - * to interoperate cleanly with ntfs_external_attr_find(). - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and - * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record - * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at - * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case - * sensitive. When @name is present, @name_len is the @name length in Unicode - * characters. - * - * If @name is not present (NULL), we assume that the unnamed attribute is - * being searched for. - * - * Finally, the resident attribute value @val is looked for, if present. - * If @val is not present (NULL), @val_len is ignored. - * - * ntfs_attr_find() only searches the specified mft record and it ignores the - * presence of an attribute list attribute (unless it is the one being searched - * for, obviously). If you need to take attribute lists into consideration, use - * ntfs_attr_lookup() instead (see below). This also means that you cannot use - * ntfs_attr_find() to search for extent records of non-resident attributes, as - * extents with lowest_vcn != 0 are usually described by the attribute list - * attribute only. - Note that it is possible that the first extent is only in - * the attribute list while the last extent is in the base mft record, so don't - * rely on being able to find the first extent in the base mft record. - * - * Warning: Never use @val when looking for attribute types which can be - * non-resident as this most likely will result in a crash! - */ -static int mkntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) -{ - ATTR_RECORD *a; - ntfschar *upcase = g_vol->upcase; - u32 upcase_len = g_vol->upcase_len; - - /* - * Iterate over attributes in mft record starting at @ctx->attr, or the - * attribute following that, if @ctx->is_first is TRUE. - */ - if (ctx->is_first) { - a = ctx->attr; - ctx->is_first = FALSE; - } else { - a = (ATTR_RECORD*)((char*)ctx->attr + - le32_to_cpu(ctx->attr->length)); - } - for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { - if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) - break; - ctx->attr = a; - if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > - le32_to_cpu(type))) || - (a->type == AT_END)) { - errno = ENOENT; - return -1; - } - if (!a->length) - break; - /* If this is an enumeration return this attribute. */ - if (type == AT_UNUSED) - return 0; - if (a->type != type) - continue; - /* - * If @name is AT_UNNAMED we want an unnamed attribute. - * If @name is present, compare the two names. - * Otherwise, match any attribute. - */ - if (name == AT_UNNAMED) { - /* The search failed if the found attribute is named. */ - if (a->name_length) { - errno = ENOENT; - return -1; - } - } else if (name && !ntfs_names_are_equal(name, name_len, - (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, ic, upcase, upcase_len)) { - int rc; - - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, IGNORE_CASE, - upcase, upcase_len); - /* - * If @name collates before a->name, there is no - * matching attribute. - */ - if (rc == -1) { - errno = ENOENT; - return -1; - } - /* If the strings are not equal, continue search. */ - if (rc) - continue; - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, CASE_SENSITIVE, - upcase, upcase_len); - if (rc == -1) { - errno = ENOENT; - return -1; - } - if (rc) - continue; - } - /* - * The names match or @name not present and attribute is - * unnamed. If no @val specified, we have found the attribute - * and are done. - */ - if (!val) { - return 0; - /* @val is present; compare values. */ - } else { - int rc; - - rc = memcmp(val, (char*)a +le16_to_cpu(a->u.res.value_offset), - min(val_len, - le32_to_cpu(a->u.res.value_length))); - /* - * If @val collates before the current attribute's - * value, there is no matching attribute. - */ - if (!rc) { - u32 avl; - avl = le32_to_cpu(a->u.res.value_length); - if (val_len == avl) - return 0; - if (val_len < avl) { - errno = ENOENT; - return -1; - } - } else if (rc < 0) { - errno = ENOENT; - return -1; - } - } - } - ntfs_log_trace("File is corrupt. Run chkdsk.\n"); - errno = EIO; - return -1; -} - -/** - * ntfs_attr_lookup - find an attribute in an ntfs inode - * @type: attribute type to find - * @name: attribute name to find (optional, i.e. NULL means don't care) - * @name_len: attribute name length (only needed if @name present) - * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) - * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must - * be the base mft record and @ctx must have been obtained from a call to - * ntfs_attr_get_search_ctx(). - * - * This function transparently handles attribute lists and @ctx is used to - * continue searches where they were left off at. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT - * to indicate that there are no more entries. During the enumeration, each - * successful call of ntfs_attr_lookup() will return the next attribute, with - * the current attribute being described by the search context @ctx. - * - * If @type is AT_END, seek to the end of the base mft record ignoring the - * attribute list completely and return -1 with errno set to ENOENT. AT_END is - * not a valid attribute, its length is zero for example, thus it is safer to - * return error instead of success in this case. It should never be needed to - * do this, but we implement the functionality because it allows for simpler - * code inside ntfs_external_attr_find(). - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * After finishing with the attribute/mft record you need to call - * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any - * mapped extent inodes, etc). - * - * Return 0 if the search was successful and -1 if not, with errno set to the - * error code. - * - * On success, @ctx->attr is the found attribute, it is in mft record - * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this - * attribute with @ctx->base_* being the base mft record to which @ctx->attr - * belongs. If no attribute list attribute is present @ctx->al_entry and - * @ctx->base_* are NULL. - * - * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the - * attribute which collates just after the attribute being searched for in the - * base ntfs inode, i.e. if one wants to add the attribute to the mft record - * this is the correct place to insert it into, and if there is not enough - * space, the attribute should be placed in an extent mft record. - * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list - * at which the new attribute's attribute list entry should be inserted. The - * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. - * The only exception to this is when @type is AT_END, in which case - * @ctx->al_entry is set to NULL also (see above). - * - * The following error codes are defined: - * ENOENT Attribute not found, not an error as such. - * EINVAL Invalid arguments. - * EIO I/O error or corrupt data structures found. - * ENOMEM Not enough memory to allocate necessary buffers. - */ -static int mkntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const VCN lowest_vcn __attribute__((unused)), const u8 *val, - const u32 val_len, ntfs_attr_search_ctx *ctx) -{ - ntfs_inode *base_ni; - - if (!ctx || !ctx->mrec || !ctx->attr) { - errno = EINVAL; - return -1; - } - if (ctx->base_ntfs_ino) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) - return mkntfs_attr_find(type, name, name_len, ic, val, val_len, - ctx); - errno = EOPNOTSUPP; - return -1; -} - -/** - * insert_positioned_attr_in_mft_record - * - * Create a non-resident attribute with a predefined on disk location - * specified by the runlist @rl. The clusters specified by @rl are assumed to - * be allocated already. - * - * Return 0 on success and -errno on error. - */ -static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, - const ATTR_TYPES type, const char *name, u32 name_len, - const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, - const runlist *rl, const u8 *val, const s64 val_len) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - u16 hdr_size; - int asize, mpa_size, err, i; - s64 bw = 0, inited_size; - VCN highest_vcn; - ntfschar *uname = NULL; - int uname_len = 0; - /* - if (base record) - attr_lookup(); - else - */ - - uname = ntfs_str2ucs(name, &uname_len); - if (!uname) - return -errno; - - /* Check if the attribute is already there. */ - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_error("Failed to allocate attribute search context.\n"); - err = -ENOMEM; - goto err_out; - } - if (ic == IGNORE_CASE) { - ntfs_log_error("FIXME: Hit unimplemented code path #1.\n"); - err = -EOPNOTSUPP; - goto err_out; - } - if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { - err = -EEXIST; - goto err_out; - } - if (errno != ENOENT) { - ntfs_log_error("Corrupt inode.\n"); - err = -errno; - goto err_out; - } - a = ctx->attr; - if (flags & ATTR_COMPRESSION_MASK) { - ntfs_log_error("Compressed attributes not supported yet.\n"); - /* FIXME: Compress attribute into a temporary buffer, set */ - /* val accordingly and save the compressed size. */ - err = -EOPNOTSUPP; - goto err_out; - } - if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { - ntfs_log_error("Encrypted/sparse attributes not supported.\n"); - err = -EOPNOTSUPP; - goto err_out; - } - if (flags & ATTR_COMPRESSION_MASK) { - hdr_size = 72; - /* FIXME: This compression stuff is all wrong. Never mind for */ - /* now. (AIA) */ - if (val_len) - mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ - else - mpa_size = 0; - } else { - hdr_size = 64; - if (val_len) { - mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0); - if (mpa_size < 0) { - err = -errno; - ntfs_log_error("Failed to get size for mapping " - "pairs.\n"); - goto err_out; - } - } else { - mpa_size = 0; - } - } - /* Mapping pairs array and next attribute must be 8-byte aligned. */ - asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; - /* Get the highest vcn. */ - for (i = 0, highest_vcn = 0LL; rl[i].length; i++) - highest_vcn += rl[i].length; - /* Does the value fit inside the allocated size? */ - if (highest_vcn * g_vol->cluster_size < val_len) { - ntfs_log_error("BUG: Allocated size is smaller than data size!\n"); - err = -EINVAL; - goto err_out; - } - err = make_room_for_attribute(m, (char*)a, asize); - if (err == -ENOSPC) { - /* - * FIXME: Make space! (AIA) - * can we make it non-resident? if yes, do that. - * does it fit now? yes -> do it. - * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? - * yes -> make non-resident - * does it fit now? yes -> do it. - * make all attributes non-resident - * does it fit now? yes -> do it. - * m is a base record? yes -> allocate extension record - * does the new attribute fit in there? yes -> do it. - * split up runlist into extents and place each in an extension - * record. - * FIXME: the check for needing extension records should be - * earlier on as it is very quick: asize > m->bytes_allocated? - */ - err = -EOPNOTSUPP; - goto err_out; -#ifdef DEBUG - } else if (err == -EINVAL) { - ntfs_log_error("BUG(): in insert_positioned_attribute_in_mft_" - "record(): make_room_for_attribute() returned " - "error: EINVAL!\n"); - goto err_out; -#endif - } - a->type = type; - a->length = cpu_to_le32(asize); - a->non_resident = 1; - a->name_length = name_len; - a->name_offset = cpu_to_le16(hdr_size); - a->flags = flags; - a->instance = m->next_attr_instance; - m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) - + 1) & 0xffff); - a->u.nonres.lowest_vcn = 0; - a->u.nonres.highest_vcn = cpu_to_sle64(highest_vcn - 1LL); - a->u.nonres.mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); - memset(a->u.nonres.reserved1, 0, sizeof(a->u.nonres.reserved1)); - /* FIXME: Allocated size depends on compression. */ - a->u.nonres.allocated_size = cpu_to_sle64(highest_vcn * g_vol->cluster_size); - a->u.nonres.data_size = cpu_to_sle64(val_len); - if (name_len) - memcpy((char*)a + hdr_size, uname, name_len << 1); - if (flags & ATTR_COMPRESSION_MASK) { - if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { - ntfs_log_error("Unknown compression format. Reverting " - "to standard compression.\n"); - a->flags &= ~ATTR_COMPRESSION_MASK; - a->flags |= ATTR_IS_COMPRESSED; - } - a->u.nonres.compression_unit = 4; - inited_size = val_len; - /* FIXME: Set the compressed size. */ - a->u.nonres.compressed_size = 0; - /* FIXME: Write out the compressed data. */ - /* FIXME: err = build_mapping_pairs_compressed(); */ - err = -EOPNOTSUPP; - } else { - a->u.nonres.compression_unit = 0; - bw = ntfs_rlwrite(g_vol->u.dev, rl, val, val_len, &inited_size); - if (bw != val_len) { - ntfs_log_error("Error writing non-resident attribute " - "value.\n"); - return -errno; - } - err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + - ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); - } - a->u.nonres.initialized_size = cpu_to_sle64(inited_size); - if (err < 0 || bw != val_len) { - /* FIXME: Handle error. */ - /* deallocate clusters */ - /* remove attribute */ - if (err >= 0) - err = -EIO; - ntfs_log_error("insert_positioned_attr_in_mft_record failed " - "with error %i.\n", err < 0 ? err : (int)bw); - } -err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - ntfs_ucsfree(uname); - return err; -} - -/** - * insert_non_resident_attr_in_mft_record - * - * Return 0 on success and -errno on error. - */ -static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, - const ATTR_TYPES type, const char *name, u32 name_len, - const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, - const u8 *val, const s64 val_len) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - u16 hdr_size; - int asize, mpa_size, err, i; - runlist *rl = NULL; - s64 bw = 0; - ntfschar *uname = NULL; - int uname_len = 0; - /* - if (base record) - attr_lookup(); - else - */ - - uname = ntfs_str2ucs(name, &uname_len); - if (!uname) - return -errno; - - /* Check if the attribute is already there. */ - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_error("Failed to allocate attribute search context.\n"); - err = -ENOMEM; - goto err_out; - } - if (ic == IGNORE_CASE) { - ntfs_log_error("FIXME: Hit unimplemented code path #2.\n"); - err = -EOPNOTSUPP; - goto err_out; - } - if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, NULL, 0, ctx)) { - err = -EEXIST; - goto err_out; - } - if (errno != ENOENT) { - ntfs_log_error("Corrupt inode.\n"); - err = -errno; - goto err_out; - } - a = ctx->attr; - if (flags & ATTR_COMPRESSION_MASK) { - ntfs_log_error("Compressed attributes not supported yet.\n"); - /* FIXME: Compress attribute into a temporary buffer, set */ - /* val accordingly and save the compressed size. */ - err = -EOPNOTSUPP; - goto err_out; - } - if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { - ntfs_log_error("Encrypted/sparse attributes not supported.\n"); - err = -EOPNOTSUPP; - goto err_out; - } - if (val_len) { - rl = allocate_scattered_clusters((val_len + - g_vol->cluster_size - 1) / g_vol->cluster_size); - if (!rl) { - err = -errno; - ntfs_log_perror("Failed to allocate scattered clusters"); - goto err_out; - } - } else { - rl = NULL; - } - if (flags & ATTR_COMPRESSION_MASK) { - hdr_size = 72; - /* FIXME: This compression stuff is all wrong. Never mind for */ - /* now. (AIA) */ - if (val_len) - mpa_size = 0; /* get_size_for_compressed_mapping_pairs(rl); */ - else - mpa_size = 0; - } else { - hdr_size = 64; - if (val_len) { - mpa_size = ntfs_get_size_for_mapping_pairs(g_vol, rl, 0); - if (mpa_size < 0) { - err = -errno; - ntfs_log_error("Failed to get size for mapping " - "pairs.\n"); - goto err_out; - } - } else { - mpa_size = 0; - } - } - /* Mapping pairs array and next attribute must be 8-byte aligned. */ - asize = (((int)hdr_size + ((name_len + 7) & ~7) + mpa_size) + 7) & ~7; - err = make_room_for_attribute(m, (char*)a, asize); - if (err == -ENOSPC) { - /* - * FIXME: Make space! (AIA) - * can we make it non-resident? if yes, do that. - * does it fit now? yes -> do it. - * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? - * yes -> make non-resident - * does it fit now? yes -> do it. - * make all attributes non-resident - * does it fit now? yes -> do it. - * m is a base record? yes -> allocate extension record - * does the new attribute fit in there? yes -> do it. - * split up runlist into extents and place each in an extension - * record. - * FIXME: the check for needing extension records should be - * earlier on as it is very quick: asize > m->bytes_allocated? - */ - err = -EOPNOTSUPP; - goto err_out; -#ifdef DEBUG - } else if (err == -EINVAL) { - ntfs_log_error("BUG(): in insert_non_resident_attribute_in_" - "mft_record(): make_room_for_attribute() " - "returned error: EINVAL!\n"); - goto err_out; -#endif - } - a->type = type; - a->length = cpu_to_le32(asize); - a->non_resident = 1; - a->name_length = name_len; - a->name_offset = cpu_to_le16(hdr_size); - a->flags = flags; - a->instance = m->next_attr_instance; - m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) - + 1) & 0xffff); - a->u.nonres.lowest_vcn = 0; - for (i = 0; rl[i].length; i++) - ; - a->u.nonres.highest_vcn = cpu_to_sle64(rl[i].vcn - 1); - a->u.nonres.mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); - memset(a->u.nonres.reserved1, 0, sizeof(a->u.nonres.reserved1)); - /* FIXME: Allocated size depends on compression. */ - a->u.nonres.allocated_size = cpu_to_sle64((val_len + (g_vol->cluster_size - 1)) & - ~(g_vol->cluster_size - 1)); - a->u.nonres.data_size = cpu_to_sle64(val_len); - a->u.nonres.initialized_size = cpu_to_sle64(val_len); - if (name_len) - memcpy((char*)a + hdr_size, uname, name_len << 1); - if (flags & ATTR_COMPRESSION_MASK) { - if (flags & ATTR_COMPRESSION_MASK & ~ATTR_IS_COMPRESSED) { - ntfs_log_error("Unknown compression format. Reverting " - "to standard compression.\n"); - a->flags &= ~ATTR_COMPRESSION_MASK; - a->flags |= ATTR_IS_COMPRESSED; - } - a->u.nonres.compression_unit = 4; - /* FIXME: Set the compressed size. */ - a->u.nonres.compressed_size = 0; - /* FIXME: Write out the compressed data. */ - /* FIXME: err = build_mapping_pairs_compressed(); */ - err = -EOPNOTSUPP; - } else { - a->u.nonres.compression_unit = 0; - bw = ntfs_rlwrite(g_vol->u.dev, rl, val, val_len, NULL); - if (bw != val_len) { - ntfs_log_error("Error writing non-resident attribute " - "value.\n"); - return -errno; - } - err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + - ((name_len + 7) & ~7), mpa_size, rl, 0, NULL); - } - if (err < 0 || bw != val_len) { - /* FIXME: Handle error. */ - /* deallocate clusters */ - /* remove attribute */ - if (err >= 0) - err = -EIO; - ntfs_log_error("insert_non_resident_attr_in_mft_record failed with " - "error %lld.\n", (long long) (err < 0 ? err : bw)); - } -err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - ntfs_ucsfree(uname); - free(rl); - return err; -} - -/** - * insert_resident_attr_in_mft_record - * - * Return 0 on success and -errno on error. - */ -static int insert_resident_attr_in_mft_record(MFT_RECORD *m, - const ATTR_TYPES type, const char *name, u32 name_len, - const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, - const RESIDENT_ATTR_FLAGS res_flags, - const u8 *val, const u32 val_len) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - int asize, err; - ntfschar *uname = NULL; - int uname_len = 0; - /* - if (base record) - mkntfs_attr_lookup(); - else - */ - - uname = ntfs_str2ucs(name, &uname_len); - if (!uname) - return -errno; - - /* Check if the attribute is already there. */ - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_error("Failed to allocate attribute search context.\n"); - err = -ENOMEM; - goto err_out; - } - if (ic == IGNORE_CASE) { - ntfs_log_error("FIXME: Hit unimplemented code path #3.\n"); - err = -EOPNOTSUPP; - goto err_out; - } - if (!mkntfs_attr_lookup(type, uname, uname_len, ic, 0, val, val_len, - ctx)) { - err = -EEXIST; - goto err_out; - } - if (errno != ENOENT) { - ntfs_log_error("Corrupt inode.\n"); - err = -errno; - goto err_out; - } - a = ctx->attr; - /* sizeof(resident attribute record header) == 24 */ - asize = ((24 + ((name_len + 7) & ~7) + val_len) + 7) & ~7; - err = make_room_for_attribute(m, (char*)a, asize); - if (err == -ENOSPC) { - /* - * FIXME: Make space! (AIA) - * can we make it non-resident? if yes, do that. - * does it fit now? yes -> do it. - * m's $DATA or $BITMAP+$INDEX_ALLOCATION resident? - * yes -> make non-resident - * does it fit now? yes -> do it. - * make all attributes non-resident - * does it fit now? yes -> do it. - * m is a base record? yes -> allocate extension record - * does the new attribute fit in there? yes -> do it. - * split up runlist into extents and place each in an extension - * record. - * FIXME: the check for needing extension records should be - * earlier on as it is very quick: asize > m->bytes_allocated? - */ - err = -EOPNOTSUPP; - goto err_out; - } -#ifdef DEBUG - if (err == -EINVAL) { - ntfs_log_error("BUG(): in insert_resident_attribute_in_mft_" - "record(): make_room_for_attribute() returned " - "error: EINVAL!\n"); - goto err_out; - } -#endif - a->type = type; - a->length = cpu_to_le32(asize); - a->non_resident = 0; - a->name_length = name_len; - if (type == AT_OBJECT_ID) - a->name_offset = const_cpu_to_le16(0); - else - a->name_offset = const_cpu_to_le16(24); - a->flags = flags; - a->instance = m->next_attr_instance; - m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) - + 1) & 0xffff); - a->u.res.value_length = cpu_to_le32(val_len); - a->u.res.value_offset = cpu_to_le16(24 + ((name_len + 7) & ~7)); - a->u.res.resident_flags = res_flags; - a->u.res.reservedR = 0; - if (name_len) - memcpy((char*)a + 24, uname, name_len << 1); - if (val_len) - memcpy((char*)a + le16_to_cpu(a->u.res.value_offset), val, val_len); -err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - ntfs_ucsfree(uname); - return err; -} - - -/** - * add_attr_std_info - * - * Return 0 on success or -errno on error. - */ -static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, - le32 security_id) -{ - STANDARD_INFORMATION si; - int err, sd_size; - - sd_size = 48; - - si.creation_time = utc2ntfs(mkntfs_time()); - si.last_data_change_time = si.creation_time; - si.last_mft_change_time = si.creation_time; - si.last_access_time = si.creation_time; - si.file_attributes = flags; /* already LE */ - si.u.v30.maximum_versions = cpu_to_le32(0); - si.u.v30.version_number = cpu_to_le32(0); - si.u.v30.class_id = cpu_to_le32(0); - si.u.v30.security_id = security_id; - if (si.u.v30.security_id != 0) - sd_size = 72; - /* FIXME: $Quota support... */ - si.u.v30.owner_id = cpu_to_le32(0); - si.u.v30.quota_charged = cpu_to_le64(0ULL); - /* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */ - si.u.v30.usn = cpu_to_le64(0ULL); - /* NTFS 1.2: size of si = 48, NTFS 3.[01]: size of si = 72 */ - err = insert_resident_attr_in_mft_record(m, AT_STANDARD_INFORMATION, - NULL, 0, 0, 0, 0, (u8*)&si, sd_size); - if (err < 0) - ntfs_log_perror("add_attr_std_info failed"); - return err; -} - -/** - * add_attr_file_name - * - * Return 0 on success or -errno on error. - */ -static int add_attr_file_name(MFT_RECORD *m, const leMFT_REF parent_dir, - const s64 allocated_size, const s64 data_size, - const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, - const u32 reparse_point_tag, const char *file_name, - const FILE_NAME_TYPE_FLAGS file_name_type) -{ - ntfs_attr_search_ctx *ctx; - STANDARD_INFORMATION *si; - FILE_NAME_ATTR *fn; - int i, fn_size; - ntfschar *uname; - - /* Check if the attribute is already there. */ - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_error("Failed to get attribute search context.\n"); - return -ENOMEM; - } - if (mkntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, 0, 0, - NULL, 0, ctx)) { - int eo = errno; - ntfs_log_error("BUG: Standard information attribute not " - "present in file record.\n"); - ntfs_attr_put_search_ctx(ctx); - return -eo; - } - si = (STANDARD_INFORMATION*)((char*)ctx->attr + - le16_to_cpu(ctx->attr->u.res.value_offset)); - i = (strlen(file_name) + 1) * sizeof(ntfschar); - fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = ntfs_malloc(fn_size); - if (!fn) { - ntfs_attr_put_search_ctx(ctx); - return -errno; - } - fn->parent_directory = parent_dir; - - fn->creation_time = si->creation_time; - fn->last_data_change_time = si->last_data_change_time; - fn->last_mft_change_time = si->last_mft_change_time; - fn->last_access_time = si->last_access_time; - ntfs_attr_put_search_ctx(ctx); - - fn->allocated_size = cpu_to_sle64(allocated_size); - fn->data_size = cpu_to_sle64(data_size); - fn->file_attributes = flags; - /* These are in a union so can't have both. */ - if (packed_ea_size && reparse_point_tag) { - free(fn); - return -EINVAL; - } - if (packed_ea_size) { - fn->u.s.packed_ea_size = cpu_to_le16(packed_ea_size); - fn->u.s.reserved = cpu_to_le16(0); - } else { - fn->u.reparse_point_tag = cpu_to_le32(reparse_point_tag); - } - fn->file_name_type = file_name_type; - uname = fn->file_name; - i = ntfs_mbstoucs(file_name, &uname, i); - if (i < 1) { - free(fn); - return -EINVAL; - } - if (i > 0xff) { - free(fn); - return -ENAMETOOLONG; - } - /* No terminating null in file names. */ - fn->file_name_length = i; - fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); - i = insert_resident_attr_in_mft_record(m, AT_FILE_NAME, NULL, 0, 0, - 0, RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); - free(fn); - if (i < 0) - ntfs_log_error("add_attr_file_name failed: %s\n", strerror(-i)); - return i; -} - -#ifdef ENABLE_UUID - -/** - * add_attr_object_id - - * - * Note we insert only a basic object id which only has the GUID and none of - * the extended fields. This is because we currently only use this function - * when creating the object id for the volume. - * - * Return 0 on success or -errno on error. - */ -static int add_attr_object_id(MFT_RECORD *m, const GUID *object_id) -{ - OBJECT_ID_ATTR oi; - int err; - - oi = (OBJECT_ID_ATTR) { - .object_id = *object_id, - }; - err = insert_resident_attr_in_mft_record(m, AT_OBJECT_ID, NULL, - 0, 0, 0, 0, (u8*)&oi, sizeof(oi.object_id)); - if (err < 0) - ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); - return err; -} - -#endif - -/** - * add_attr_sd - * - * Create the security descriptor attribute adding the security descriptor @sd - * of length @sd_len to the mft record @m. - * - * Return 0 on success or -errno on error. - */ -static int add_attr_sd(MFT_RECORD *m, const u8 *sd, const s64 sd_len) -{ - int err; - - /* Does it fit? NO: create non-resident. YES: create resident. */ - if (le32_to_cpu(m->bytes_in_use) + 24 + sd_len > - le32_to_cpu(m->bytes_allocated)) - err = insert_non_resident_attr_in_mft_record(m, - AT_SECURITY_DESCRIPTOR, NULL, 0, 0, 0, sd, - sd_len); - else - err = insert_resident_attr_in_mft_record(m, - AT_SECURITY_DESCRIPTOR, NULL, 0, 0, 0, 0, sd, - sd_len); - if (err < 0) - ntfs_log_error("add_attr_sd failed: %s\n", strerror(-err)); - return err; -} - -/** - * add_attr_data - * - * Return 0 on success or -errno on error. - */ -static int add_attr_data(MFT_RECORD *m, const char *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const ATTR_FLAGS flags, - const u8 *val, const s64 val_len) -{ - int err; - - /* - * Does it fit? NO: create non-resident. YES: create resident. - * - * FIXME: Introduced arbitrary limit of mft record allocated size - 512. - * This is to get around the problem that if $Bitmap/$DATA becomes too - * big, but is just small enough to be resident, we would make it - * resident, and later run out of space when creating the other - * attributes and this would cause us to abort as making resident - * attributes non-resident is not supported yet. - * The proper fix is to support making resident attribute non-resident. - */ - if (le32_to_cpu(m->bytes_in_use) + 24 + val_len > - min(le32_to_cpu(m->bytes_allocated), - le32_to_cpu(m->bytes_allocated) - 512)) - err = insert_non_resident_attr_in_mft_record(m, AT_DATA, name, - name_len, ic, flags, val, val_len); - else - err = insert_resident_attr_in_mft_record(m, AT_DATA, name, - name_len, ic, flags, 0, val, val_len); - - if (err < 0) - ntfs_log_error("add_attr_data failed: %s\n", strerror(-err)); - return err; -} - -/** - * add_attr_data_positioned - * - * Create a non-resident data attribute with a predefined on disk location - * specified by the runlist @rl. The clusters specified by @rl are assumed to - * be allocated already. - * - * Return 0 on success or -errno on error. - */ -static int add_attr_data_positioned(MFT_RECORD *m, const char *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const ATTR_FLAGS flags, const runlist *rl, - const u8 *val, const s64 val_len) -{ - int err; - - err = insert_positioned_attr_in_mft_record(m, AT_DATA, name, name_len, - ic, flags, rl, val, val_len); - if (err < 0) - ntfs_log_error("add_attr_data_positioned failed: %s\n", - strerror(-err)); - return err; -} - -/** - * add_attr_vol_name - * - * Create volume name attribute specifying the volume name @vol_name as a null - * terminated char string of length @vol_name_len (number of characters not - * including the terminating null), which is converted internally to a little - * endian ntfschar string. The name is at least 1 character long and at most - * 0xff characters long (not counting the terminating null). - * - * Return 0 on success or -errno on error. - */ -static int add_attr_vol_name(MFT_RECORD *m, const char *vol_name, - const int vol_name_len __attribute__((unused))) -{ - ntfschar *uname = NULL; - int uname_len = 0; - int i; - - if (vol_name) { - uname_len = ntfs_mbstoucs(vol_name, &uname, 0); - if (uname_len < 0) - return -errno; - if (uname_len > 0xff) { - free(uname); - return -ENAMETOOLONG; - } - } - i = insert_resident_attr_in_mft_record(m, AT_VOLUME_NAME, NULL, 0, 0, - 0, 0, (u8*)uname, uname_len*sizeof(ntfschar)); - free(uname); - if (i < 0) - ntfs_log_error("add_attr_vol_name failed: %s\n", strerror(-i)); - return i; -} - -/** - * add_attr_vol_info - * - * Return 0 on success or -errno on error. - */ -static int add_attr_vol_info(MFT_RECORD *m, const VOLUME_FLAGS flags, - const u8 major_ver, const u8 minor_ver) -{ - VOLUME_INFORMATION vi; - int err; - - memset(&vi, 0, sizeof(vi)); - vi.major_ver = major_ver; - vi.minor_ver = minor_ver; - vi.flags = flags & VOLUME_FLAGS_MASK; - err = insert_resident_attr_in_mft_record(m, AT_VOLUME_INFORMATION, NULL, - 0, 0, 0, 0, (u8*)&vi, sizeof(vi)); - if (err < 0) - ntfs_log_error("add_attr_vol_info failed: %s\n", strerror(-err)); - return err; -} - -/** - * add_attr_index_root - * - * Return 0 on success or -errno on error. - */ -static int add_attr_index_root(MFT_RECORD *m, const char *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const ATTR_TYPES indexed_attr_type, - const COLLATION_RULES collation_rule, - const u32 index_block_size) -{ - INDEX_ROOT *r; - INDEX_ENTRY_HEADER *e; - int err, val_len; - - val_len = sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER); - r = ntfs_malloc(val_len); - if (!r) - return -errno; - r->type = (indexed_attr_type == AT_FILE_NAME) ? AT_FILE_NAME : 0; - if (indexed_attr_type == AT_FILE_NAME && - collation_rule != COLLATION_FILE_NAME) { - free(r); - ntfs_log_error("add_attr_index_root: indexed attribute is $FILE_NAME " - "but collation rule is not COLLATION_FILE_NAME.\n"); - return -EINVAL; - } - r->collation_rule = collation_rule; - r->index_block_size = cpu_to_le32(index_block_size); - if (index_block_size >= g_vol->cluster_size) { - if (index_block_size % g_vol->cluster_size) { - ntfs_log_error("add_attr_index_root: index block size is not " - "a multiple of the cluster size.\n"); - free(r); - return -EINVAL; - } - r->clusters_per_index_block = index_block_size / - g_vol->cluster_size; - } else { /* if (g_vol->cluster_size > index_block_size) */ - if (index_block_size & (index_block_size - 1)) { - ntfs_log_error("add_attr_index_root: index block size is not " - "a power of 2.\n"); - free(r); - return -EINVAL; - } - if (index_block_size < (u32)opts.sector_size) { - ntfs_log_error("add_attr_index_root: index block size " - "is smaller than the sector size.\n"); - free(r); - return -EINVAL; - } - r->clusters_per_index_block = index_block_size / - opts.sector_size; - } - memset(&r->reserved, 0, sizeof(r->reserved)); - r->index.entries_offset = const_cpu_to_le32(sizeof(INDEX_HEADER)); - r->index.index_length = const_cpu_to_le32(sizeof(INDEX_HEADER) + - sizeof(INDEX_ENTRY_HEADER)); - r->index.allocated_size = r->index.index_length; - r->index.flags = SMALL_INDEX; - memset(&r->index.reserved, 0, sizeof(r->index.reserved)); - e = (INDEX_ENTRY_HEADER*)((u8*)&r->index + - le32_to_cpu(r->index.entries_offset)); - /* - * No matter whether this is a file index or a view as this is a - * termination entry, hence no key value / data is associated with it - * at all. Thus, we just need the union to be all zero. - */ - e->u.indexed_file = const_cpu_to_le64(0LL); - e->length = const_cpu_to_le16(sizeof(INDEX_ENTRY_HEADER)); - e->key_length = const_cpu_to_le16(0); - e->flags = INDEX_ENTRY_END; - e->reserved = const_cpu_to_le16(0); - err = insert_resident_attr_in_mft_record(m, AT_INDEX_ROOT, name, - name_len, ic, 0, 0, (u8*)r, val_len); - free(r); - if (err < 0) - ntfs_log_error("add_attr_index_root failed: %s\n", strerror(-err)); - return err; -} - -/** - * add_attr_index_alloc - * - * Return 0 on success or -errno on error. - */ -static int add_attr_index_alloc(MFT_RECORD *m, const char *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const u8 *index_alloc_val, const u32 index_alloc_val_len) -{ - int err; - - err = insert_non_resident_attr_in_mft_record(m, AT_INDEX_ALLOCATION, - name, name_len, ic, 0, index_alloc_val, - index_alloc_val_len); - if (err < 0) - ntfs_log_error("add_attr_index_alloc failed: %s\n", strerror(-err)); - return err; -} - -/** - * add_attr_bitmap - * - * Return 0 on success or -errno on error. - */ -static int add_attr_bitmap(MFT_RECORD *m, const char *name, const u32 name_len, - const IGNORE_CASE_BOOL ic, const u8 *bitmap, - const u32 bitmap_len) -{ - int err; - - /* Does it fit? NO: create non-resident. YES: create resident. */ - if (le32_to_cpu(m->bytes_in_use) + 24 + bitmap_len > - le32_to_cpu(m->bytes_allocated)) - err = insert_non_resident_attr_in_mft_record(m, AT_BITMAP, name, - name_len, ic, 0, bitmap, bitmap_len); - else - err = insert_resident_attr_in_mft_record(m, AT_BITMAP, name, - name_len, ic, 0, 0, bitmap, bitmap_len); - - if (err < 0) - ntfs_log_error("add_attr_bitmap failed: %s\n", strerror(-err)); - return err; -} - -/** - * add_attr_bitmap_positioned - * - * Create a non-resident bitmap attribute with a predefined on disk location - * specified by the runlist @rl. The clusters specified by @rl are assumed to - * be allocated already. - * - * Return 0 on success or -errno on error. - */ -static int add_attr_bitmap_positioned(MFT_RECORD *m, const char *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const runlist *rl, const u8 *bitmap, const u32 bitmap_len) -{ - int err; - - err = insert_positioned_attr_in_mft_record(m, AT_BITMAP, name, name_len, - ic, 0, rl, bitmap, bitmap_len); - if (err < 0) - ntfs_log_error("add_attr_bitmap_positioned failed: %s\n", - strerror(-err)); - return err; -} - - -/** - * upgrade_to_large_index - * - * Create bitmap and index allocation attributes, modify index root - * attribute accordingly and move all of the index entries from the index root - * into the index allocation. - * - * Return 0 on success or -errno on error. - */ -static int upgrade_to_large_index(MFT_RECORD *m, const char *name, - u32 name_len, const IGNORE_CASE_BOOL ic, - INDEX_ALLOCATION **idx) -{ - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - INDEX_ROOT *r; - INDEX_ENTRY *re; - INDEX_ALLOCATION *ia_val = NULL; - ntfschar *uname = NULL; - int uname_len = 0; - u8 bmp[8]; - char *re_start, *re_end; - int i, err, index_block_size; - - uname = ntfs_str2ucs(name, &uname_len); - if (!uname) - return -errno; - - /* Find the index root attribute. */ - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_error("Failed to allocate attribute search context.\n"); - ntfs_ucsfree(uname); - return -ENOMEM; - } - if (ic == IGNORE_CASE) { - ntfs_log_error("FIXME: Hit unimplemented code path #4.\n"); - err = -EOPNOTSUPP; - ntfs_ucsfree(uname); - goto err_out; - } - err = mkntfs_attr_lookup(AT_INDEX_ROOT, uname, uname_len, ic, 0, NULL, 0, - ctx); - ntfs_ucsfree(uname); - if (err) { - err = -ENOTDIR; - goto err_out; - } - a = ctx->attr; - if (a->non_resident || a->flags) { - err = -EINVAL; - goto err_out; - } - r = (INDEX_ROOT*)((char*)a + le16_to_cpu(a->u.res.value_offset)); - re_end = (char*)r + le32_to_cpu(a->u.res.value_length); - re_start = (char*)&r->index + le32_to_cpu(r->index.entries_offset); - re = (INDEX_ENTRY*)re_start; - index_block_size = le32_to_cpu(r->index_block_size); - memset(bmp, 0, sizeof(bmp)); - ntfs_bit_set(bmp, 0ULL, 1); - /* Bitmap has to be at least 8 bytes in size. */ - err = add_attr_bitmap(m, name, name_len, ic, bmp, sizeof(bmp)); - if (err) - goto err_out; - ia_val = ntfs_calloc(index_block_size); - if (!ia_val) { - err = -errno; - goto err_out; - } - /* Setup header. */ - ia_val->magic = magic_INDX; - ia_val->usa_ofs = cpu_to_le16(sizeof(INDEX_ALLOCATION)); - if (index_block_size >= NTFS_BLOCK_SIZE) { - ia_val->usa_count = cpu_to_le16(index_block_size / - NTFS_BLOCK_SIZE + 1); - } else { - ia_val->usa_count = cpu_to_le16(1); - ntfs_log_error("Sector size is bigger than index block size. " - "Setting usa_count to 1. If Windows chkdsk " - "reports this as corruption, please email %s " - "stating that you saw this message and that " - "the filesystem created was corrupt. " - "Thank you.", NTFS_DEV_LIST); - } - /* Set USN to 1. */ - *(le16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = - cpu_to_le16(1); - ia_val->lsn = 0; - ia_val->index_block_vcn = 0; - ia_val->index.flags = LEAF_NODE; - /* Align to 8-byte boundary. */ - ia_val->index.entries_offset = cpu_to_le32((sizeof(INDEX_HEADER) + - le16_to_cpu(ia_val->usa_count) * 2 + 7) & ~7); - ia_val->index.allocated_size = cpu_to_le32(index_block_size - - (sizeof(INDEX_ALLOCATION) - sizeof(INDEX_HEADER))); - /* Find the last entry in the index root and save it in re. */ - while ((char*)re < re_end && !(re->flags & INDEX_ENTRY_END)) { - /* Next entry in index root. */ - re = (INDEX_ENTRY*)((char*)re + le16_to_cpu(re->length)); - } - /* Copy all the entries including the termination entry. */ - i = (char*)re - re_start + le16_to_cpu(re->length); - memcpy((char*)&ia_val->index + - le32_to_cpu(ia_val->index.entries_offset), re_start, i); - /* Finish setting up index allocation. */ - ia_val->index.index_length = cpu_to_le32(i + - le32_to_cpu(ia_val->index.entries_offset)); - /* Move the termination entry forward to the beginning if necessary. */ - if ((char*)re > re_start) { - memmove(re_start, (char*)re, le16_to_cpu(re->length)); - re = (INDEX_ENTRY*)re_start; - } - /* Now fixup empty index root with pointer to index allocation VCN 0. */ - r->index.flags = LARGE_INDEX; - re->flags |= INDEX_ENTRY_NODE; - if (le16_to_cpu(re->length) < sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) - re->length = cpu_to_le16(le16_to_cpu(re->length) + sizeof(VCN)); - r->index.index_length = cpu_to_le32(le32_to_cpu(r->index.entries_offset) - + le16_to_cpu(re->length)); - r->index.allocated_size = r->index.index_length; - /* Resize index root attribute. */ - if (ntfs_resident_attr_value_resize(m, a, sizeof(INDEX_ROOT) - - sizeof(INDEX_HEADER) + - le32_to_cpu(r->index.allocated_size))) { - /* TODO: Remove the added bitmap! */ - /* Revert index root from index allocation. */ - err = -errno; - goto err_out; - } - /* Set VCN pointer to 0LL. */ - *(leVCN*)((char*)re + le16_to_cpu(re->length) - sizeof(VCN)) = 0; - err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)ia_val, index_block_size); - if (err) { - err = -errno; - ntfs_log_error("ntfs_mst_pre_write_fixup() failed in " - "upgrade_to_large_index.\n"); - goto err_out; - } - err = add_attr_index_alloc(m, name, name_len, ic, (u8*)ia_val, - index_block_size); - ntfs_mst_post_write_fixup((NTFS_RECORD*)ia_val); - if (err) { - /* TODO: Remove the added bitmap! */ - /* Revert index root from index allocation. */ - goto err_out; - } - *idx = ia_val; - ntfs_attr_put_search_ctx(ctx); - return 0; -err_out: - ntfs_attr_put_search_ctx(ctx); - free(ia_val); - return err; -} - -/** - * make_room_for_index_entry_in_index_block - * - * Create space of @size bytes at position @pos inside the index block @idx. - * - * Return 0 on success or -errno on error. - */ -static int make_room_for_index_entry_in_index_block(INDEX_BLOCK *idx, - INDEX_ENTRY *pos, u32 size) -{ - u32 biu; - - if (!size) - return 0; -#ifdef DEBUG - /* - * Rigorous consistency checks. Always return -EINVAL even if more - * appropriate codes exist for simplicity of parsing the return value. - */ - if (size != ((size + 7) & ~7)) { - ntfs_log_error("make_room_for_index_entry_in_index_block() received " - "non 8-byte aligned size.\n"); - return -EINVAL; - } - if (!idx || !pos) - return -EINVAL; - if ((char*)pos < (char*)idx || (char*)pos + size < (char*)idx || - (char*)pos > (char*)idx + sizeof(INDEX_BLOCK) - - sizeof(INDEX_HEADER) + - le32_to_cpu(idx->index.allocated_size) || - (char*)pos + size > (char*)idx + sizeof(INDEX_BLOCK) - - sizeof(INDEX_HEADER) + - le32_to_cpu(idx->index.allocated_size)) - return -EINVAL; - /* The - sizeof(INDEX_ENTRY_HEADER) is for the index terminator. */ - if ((char*)pos - (char*)&idx->index > - (int)le32_to_cpu(idx->index.index_length) - - (int)sizeof(INDEX_ENTRY_HEADER)) - return -EINVAL; -#endif - biu = le32_to_cpu(idx->index.index_length); - /* Do we have enough space? */ - if (biu + size > le32_to_cpu(idx->index.allocated_size)) - return -ENOSPC; - /* Move everything after pos to pos + size. */ - memmove((char*)pos + size, (char*)pos, biu - ((char*)pos - - (char*)&idx->index)); - /* Update index block. */ - idx->index.index_length = cpu_to_le32(biu + size); - return 0; -} - -/** - * ntfs_index_keys_compare - * - * not all types of COLLATION_RULES supported yet... - * added as needed.. (remove this comment when all are added) - */ -static int ntfs_index_keys_compare(u8 *key1, u8 *key2, int key1_length, - int key2_length, COLLATION_RULES collation_rule) -{ - u32 u1, u2; - int i; - - if (collation_rule == COLLATION_NTOFS_ULONG) { - /* i.e. $SII or $QUOTA-$Q */ - u1 = le32_to_cpup(key1); - u2 = le32_to_cpup(key2); - if (u1 < u2) - return -1; - if (u1 > u2) - return 1; - /* u1 == u2 */ - return 0; - } - if (collation_rule == COLLATION_NTOFS_ULONGS) { - /* i.e $OBJID-$O */ - i = 0; - while (i < min(key1_length, key2_length)) { - u1 = le32_to_cpup(key1 + i); - u2 = le32_to_cpup(key2 + i); - if (u1 < u2) - return -1; - if (u1 > u2) - return 1; - /* u1 == u2 */ - i += sizeof(u32); - } - if (key1_length < key2_length) - return -1; - if (key1_length > key2_length) - return 1; - return 0; - } - if (collation_rule == COLLATION_NTOFS_SECURITY_HASH) { - /* i.e. $SDH */ - u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->hash); - u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->hash); - if (u1 < u2) - return -1; - if (u1 > u2) - return 1; - /* u1 == u2 */ - u1 = le32_to_cpu(((SDH_INDEX_KEY*)key1)->security_id); - u2 = le32_to_cpu(((SDH_INDEX_KEY*)key2)->security_id); - if (u1 < u2) - return -1; - if (u1 > u2) - return 1; - return 0; - } - if (collation_rule == COLLATION_NTOFS_SID) { - /* i.e. $QUOTA-O */ - i = memcmp(key1, key2, min(key1_length, key2_length)); - if (!i) { - if (key1_length < key2_length) - return -1; - if (key1_length > key2_length) - return 1; - } - return i; - } - ntfs_log_critical("ntfs_index_keys_compare called without supported " - "collation rule.\n"); - return 0; /* Claim they're equal. What else can we do? */ -} - -/** - * insert_index_entry_in_res_dir_index - * - * i.e. insert an index_entry in some named index_root - * simplified search method, works for mkntfs - */ -static int insert_index_entry_in_res_dir_index(INDEX_ENTRY *idx, u32 idx_size, - MFT_RECORD *m, ntfschar *name, u32 name_size, ATTR_TYPES type) -{ - ntfs_attr_search_ctx *ctx; - INDEX_HEADER *idx_header; - INDEX_ENTRY *idx_entry, *idx_end; - ATTR_RECORD *a; - COLLATION_RULES collation_rule; - int err, i; - - err = 0; - /* does it fit ?*/ - if (g_vol->mft_record_size > idx_size + le32_to_cpu(m->bytes_allocated)) - return -ENOSPC; - /* find the INDEX_ROOT attribute:*/ - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_error("Failed to allocate attribute search " - "context.\n"); - err = -ENOMEM; - goto err_out; - } - if (mkntfs_attr_lookup(AT_INDEX_ROOT, name, name_size, 0, 0, NULL, 0, - ctx)) { - err = -EEXIST; - goto err_out; - } - /* found attribute */ - a = (ATTR_RECORD*)ctx->attr; - collation_rule = ((INDEX_ROOT*)((u8*)a + - le16_to_cpu(a->u.res.value_offset)))->collation_rule; - idx_header = (INDEX_HEADER*)((u8*)a + le16_to_cpu(a->u.res.value_offset) - + 0x10); - idx_entry = (INDEX_ENTRY*)((u8*)idx_header + - le32_to_cpu(idx_header->entries_offset)); - idx_end = (INDEX_ENTRY*)((u8*)idx_entry + - le32_to_cpu(idx_header->index_length)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - if (type == AT_FILE_NAME) { - while (((u8*)idx_entry < (u8*)idx_end) && - !(idx_entry->flags & INDEX_ENTRY_END)) { - i = ntfs_file_values_compare(&idx->key.file_name, - &idx_entry->key.file_name, 1, - IGNORE_CASE, g_vol->upcase, - g_vol->upcase_len); - /* - * If @file_name collates before ie->key.file_name, - * there is no matching index entry. - */ - if (i == -1) - break; - /* If file names are not equal, continue search. */ - if (i) - goto do_next; - if (idx->key.file_name.file_name_type != - FILE_NAME_POSIX || - idx_entry->key.file_name.file_name_type - != FILE_NAME_POSIX) - return -EEXIST; - i = ntfs_file_values_compare(&idx->key.file_name, - &idx_entry->key.file_name, 1, - CASE_SENSITIVE, g_vol->upcase, - g_vol->upcase_len); - if (!i) - return -EEXIST; - if (i == -1) - break; -do_next: - idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + - le16_to_cpu(idx_entry->length)); - } - } else if (type == AT_UNUSED) { /* case view */ - while (((u8*)idx_entry < (u8*)idx_end) && - !(idx_entry->flags & INDEX_ENTRY_END)) { - i = ntfs_index_keys_compare((u8*)idx + 0x10, - (u8*)idx_entry + 0x10, - le16_to_cpu(idx->key_length), - le16_to_cpu(idx_entry->key_length), - collation_rule); - if (!i) - return -EEXIST; - if (i == -1) - break; - idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + - le16_to_cpu(idx_entry->length)); - } - } else - return -EINVAL; - memmove((u8*)idx_entry + idx_size, (u8*)idx_entry, - le32_to_cpu(m->bytes_in_use) - - ((u8*)idx_entry - (u8*)m)); - memcpy((u8*)idx_entry, (u8*)idx, idx_size); - /* Adjust various offsets, etc... */ - m->bytes_in_use = cpu_to_le32(le32_to_cpu(m->bytes_in_use) + idx_size); - a->length = cpu_to_le32(le32_to_cpu(a->length) + idx_size); - a->u.res.value_length = cpu_to_le32(le32_to_cpu(a->u.res.value_length) + idx_size); - idx_header->index_length = cpu_to_le32( - le32_to_cpu(idx_header->index_length) + idx_size); - idx_header->allocated_size = cpu_to_le32( - le32_to_cpu(idx_header->allocated_size) + idx_size); -err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - return err; -} - -/** - * initialize_secure - * - * initializes $Secure's $SDH and $SII indexes from $SDS datastream - */ -static int initialize_secure(char *sds, u32 sds_size, MFT_RECORD *m) -{ - int err, sdh_size, sii_size; - SECURITY_DESCRIPTOR_HEADER *sds_header; - INDEX_ENTRY *idx_entry_sdh, *idx_entry_sii; - SDH_INDEX_DATA *sdh_data; - SII_INDEX_DATA *sii_data; - - sds_header = (SECURITY_DESCRIPTOR_HEADER*)sds; - sdh_size = sizeof(INDEX_ENTRY_HEADER); - sdh_size += sizeof(SDH_INDEX_KEY) + sizeof(SDH_INDEX_DATA); - sii_size = sizeof(INDEX_ENTRY_HEADER); - sii_size += sizeof(SII_INDEX_KEY) + sizeof(SII_INDEX_DATA); - idx_entry_sdh = ntfs_calloc(sizeof(INDEX_ENTRY)); - if (!idx_entry_sdh) - return -errno; - idx_entry_sii = ntfs_calloc(sizeof(INDEX_ENTRY)); - if (!idx_entry_sii) { - free(idx_entry_sdh); - return -errno; - } - err = 0; - - while ((char*)sds_header < (char*)sds + sds_size) { - if (!sds_header->length) - break; - /* SDH index entry */ - idx_entry_sdh->u.s.data_offset = const_cpu_to_le16(0x18); - idx_entry_sdh->u.s.data_length = const_cpu_to_le16(0x14); - idx_entry_sdh->u.s.reservedV = const_cpu_to_le32(0x00); - idx_entry_sdh->length = const_cpu_to_le16(0x30); - idx_entry_sdh->key_length = const_cpu_to_le16(0x08); - idx_entry_sdh->flags = const_cpu_to_le16(0x00); - idx_entry_sdh->reserved = const_cpu_to_le16(0x00); - idx_entry_sdh->key.sdh.hash = sds_header->hash; - idx_entry_sdh->key.sdh.security_id = sds_header->security_id; - sdh_data = (SDH_INDEX_DATA*)((u8*)idx_entry_sdh + - le16_to_cpu(idx_entry_sdh->u.s.data_offset)); - sdh_data->hash = sds_header->hash; - sdh_data->security_id = sds_header->security_id; - sdh_data->offset = sds_header->offset; - sdh_data->length = sds_header->length; - sdh_data->reserved_II = const_cpu_to_le32(0x00490049); - - /* SII index entry */ - idx_entry_sii->u.s.data_offset = const_cpu_to_le16(0x14); - idx_entry_sii->u.s.data_length = const_cpu_to_le16(0x14); - idx_entry_sii->u.s.reservedV = const_cpu_to_le32(0x00); - idx_entry_sii->length = const_cpu_to_le16(0x28); - idx_entry_sii->key_length = const_cpu_to_le16(0x04); - idx_entry_sii->flags = const_cpu_to_le16(0x00); - idx_entry_sii->reserved = const_cpu_to_le16(0x00); - idx_entry_sii->key.sii.security_id = sds_header->security_id; - sii_data = (SII_INDEX_DATA*)((u8*)idx_entry_sii + - le16_to_cpu(idx_entry_sii->u.s.data_offset)); - sii_data->hash = sds_header->hash; - sii_data->security_id = sds_header->security_id; - sii_data->offset = sds_header->offset; - sii_data->length = sds_header->length; - if ((err = insert_index_entry_in_res_dir_index(idx_entry_sdh, - sdh_size, m, NTFS_INDEX_SDH, 4, AT_UNUSED))) - break; - if ((err = insert_index_entry_in_res_dir_index(idx_entry_sii, - sii_size, m, NTFS_INDEX_SII, 4, AT_UNUSED))) - break; - sds_header = (SECURITY_DESCRIPTOR_HEADER*)((u8*)sds_header + - ((le32_to_cpu(sds_header->length) + 15) & ~15)); - } - free(idx_entry_sdh); - free(idx_entry_sii); - return err; -} - -/** - * initialize_quota - * - * initialize $Quota with the default quota index-entries. - */ -static int initialize_quota(MFT_RECORD *m) -{ - int o_size, q1_size, q2_size, err, i; - INDEX_ENTRY *idx_entry_o, *idx_entry_q1, *idx_entry_q2; - QUOTA_O_INDEX_DATA *idx_entry_o_data; - QUOTA_CONTROL_ENTRY *idx_entry_q1_data, *idx_entry_q2_data; - - err = 0; - /* q index entry num 1 */ - q1_size = 0x48; - idx_entry_q1 = ntfs_calloc(q1_size); - if (!idx_entry_q1) - return errno; - idx_entry_q1->u.s.data_offset = const_cpu_to_le16(0x14); - idx_entry_q1->u.s.data_length = const_cpu_to_le16(0x30); - idx_entry_q1->u.s.reservedV = const_cpu_to_le32(0x00); - idx_entry_q1->length = const_cpu_to_le16(0x48); - idx_entry_q1->key_length = const_cpu_to_le16(0x04); - idx_entry_q1->flags = const_cpu_to_le16(0x00); - idx_entry_q1->reserved = const_cpu_to_le16(0x00); - idx_entry_q1->key.owner_id = const_cpu_to_le32(0x01); - idx_entry_q1_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q1 - + le16_to_cpu(idx_entry_q1->u.s.data_offset)); - idx_entry_q1_data->version = const_cpu_to_le32(0x02); - idx_entry_q1_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; - idx_entry_q1_data->bytes_used = const_cpu_to_le64(0x00); - idx_entry_q1_data->change_time = utc2ntfs(mkntfs_time()); - idx_entry_q1_data->threshold = cpu_to_sle64(-1); - idx_entry_q1_data->limit = cpu_to_sle64(-1); - idx_entry_q1_data->exceeded_time = 0; - err = insert_index_entry_in_res_dir_index(idx_entry_q1, q1_size, m, - NTFS_INDEX_Q, 2, AT_UNUSED); - free(idx_entry_q1); - if (err) - return err; - /* q index entry num 2 */ - q2_size = 0x58; - idx_entry_q2 = ntfs_calloc(q2_size); - if (!idx_entry_q2) - return errno; - idx_entry_q2->u.s.data_offset = const_cpu_to_le16(0x14); - idx_entry_q2->u.s.data_length = const_cpu_to_le16(0x40); - idx_entry_q2->u.s.reservedV = const_cpu_to_le32(0x00); - idx_entry_q2->length = const_cpu_to_le16(0x58); - idx_entry_q2->key_length = const_cpu_to_le16(0x04); - idx_entry_q2->flags = const_cpu_to_le16(0x00); - idx_entry_q2->reserved = const_cpu_to_le16(0x00); - idx_entry_q2->key.owner_id = QUOTA_FIRST_USER_ID; - idx_entry_q2_data = (QUOTA_CONTROL_ENTRY*)((char*)idx_entry_q2 - + le16_to_cpu(idx_entry_q2->u.s.data_offset)); - idx_entry_q2_data->version = const_cpu_to_le32(0x02); - idx_entry_q2_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; - idx_entry_q2_data->bytes_used = const_cpu_to_le64(0x00); - idx_entry_q2_data->change_time = utc2ntfs(mkntfs_time());; - idx_entry_q2_data->threshold = cpu_to_sle64(-1); - idx_entry_q2_data->limit = cpu_to_sle64(-1); - idx_entry_q2_data->exceeded_time = 0; - idx_entry_q2_data->sid.revision = 1; - idx_entry_q2_data->sid.sub_authority_count = 2; - for (i = 0; i < 5; i++) - idx_entry_q2_data->sid.identifier_authority.value[i] = 0; - idx_entry_q2_data->sid.identifier_authority.value[5] = 0x05; - idx_entry_q2_data->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - idx_entry_q2_data->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - err = insert_index_entry_in_res_dir_index(idx_entry_q2, q2_size, m, - NTFS_INDEX_Q, 2, AT_UNUSED); - free(idx_entry_q2); - if (err) - return err; - o_size = 0x28; - idx_entry_o = ntfs_calloc(o_size); - if (!idx_entry_o) - return errno; - idx_entry_o->u.s.data_offset = const_cpu_to_le16(0x20); - idx_entry_o->u.s.data_length = const_cpu_to_le16(0x04); - idx_entry_o->u.s.reservedV = const_cpu_to_le32(0x00); - idx_entry_o->length = const_cpu_to_le16(0x28); - idx_entry_o->key_length = const_cpu_to_le16(0x10); - idx_entry_o->flags = const_cpu_to_le16(0x00); - idx_entry_o->reserved = const_cpu_to_le16(0x00); - idx_entry_o->key.sid.revision = 0x01; - idx_entry_o->key.sid.sub_authority_count = 0x02; - for (i = 0; i < 5; i++) - idx_entry_o->key.sid.identifier_authority.value[i] = 0; - idx_entry_o->key.sid.identifier_authority.value[5] = 0x05; - idx_entry_o->key.sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - idx_entry_o->key.sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - idx_entry_o_data = (QUOTA_O_INDEX_DATA*)((char*)idx_entry_o - + le16_to_cpu(idx_entry_o->u.s.data_offset)); - idx_entry_o_data->owner_id = QUOTA_FIRST_USER_ID; - /* 20 00 00 00 padding after here on ntfs 3.1. 3.0 is unchecked. */ - idx_entry_o_data->unknown = const_cpu_to_le32(32); - err = insert_index_entry_in_res_dir_index(idx_entry_o, o_size, m, - NTFS_INDEX_O, 2, AT_UNUSED); - free(idx_entry_o); - - return err; -} - -/** - * insert_file_link_in_dir_index - * - * Insert the fully completed FILE_NAME_ATTR @file_name which is inside - * the file with mft reference @file_ref into the index (allocation) block - * @idx (which belongs to @file_ref's parent directory). - * - * Return 0 on success or -errno on error. - */ -static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, leMFT_REF file_ref, - FILE_NAME_ATTR *file_name, u32 file_name_size) -{ - int err, i; - INDEX_ENTRY *ie; - char *index_end; - - /* - * Lookup dir entry @file_name in dir @idx to determine correct - * insertion location. FIXME: Using a very oversimplified lookup - * method which is sufficient for mkntfs but no good whatsoever in - * real world scenario. (AIA) - */ - - index_end = (char*)&idx->index + le32_to_cpu(idx->index.index_length); - ie = (INDEX_ENTRY*)((char*)&idx->index + - le32_to_cpu(idx->index.entries_offset)); - /* - * Loop until we exceed valid memory (corruption case) or until we - * reach the last entry. - */ - while ((char*)ie < index_end && !(ie->flags & INDEX_ENTRY_END)) { -#if 0 -#ifdef DEBUG - ntfs_log_debug("file_name_attr1->file_name_length = %i\n", - file_name->file_name_length); - if (file_name->file_name_length) { - char *__buf = NULL; - i = ntfs_ucstombs((ntfschar*)&file_name->file_name, - file_name->file_name_length, &__buf, 0); - if (i < 0) - ntfs_log_debug("Name contains non-displayable " - "Unicode characters.\n"); - ntfs_log_debug("file_name_attr1->file_name = %s\n", - __buf); - free(__buf); - } - ntfs_log_debug("file_name_attr2->file_name_length = %i\n", - ie->key.file_name.file_name_length); - if (ie->key.file_name.file_name_length) { - char *__buf = NULL; - i = ntfs_ucstombs(ie->key.file_name.file_name, - ie->key.file_name.file_name_length + 1, &__buf, - 0); - if (i < 0) - ntfs_log_debug("Name contains non-displayable " - "Unicode characters.\n"); - ntfs_log_debug("file_name_attr2->file_name = %s\n", - __buf); - free(__buf); - } -#endif -#endif - i = ntfs_file_values_compare(file_name, - (FILE_NAME_ATTR*)&ie->key.file_name, 1, - IGNORE_CASE, g_vol->upcase, g_vol->upcase_len); - /* - * If @file_name collates before ie->key.file_name, there is no - * matching index entry. - */ - if (i == -1) - break; - /* If file names are not equal, continue search. */ - if (i) - goto do_next; - /* File names are equal when compared ignoring case. */ - /* - * If BOTH file names are in the POSIX namespace, do a case - * sensitive comparison as well. Otherwise the names match so - * we return -EEXIST. FIXME: There are problems with this in a - * real world scenario, when one is POSIX and one isn't, but - * fine for mkntfs where we don't use POSIX namespace at all - * and hence this following code is luxury. (AIA) - */ - if (file_name->file_name_type != FILE_NAME_POSIX || - ie->key.file_name.file_name_type != FILE_NAME_POSIX) - return -EEXIST; - i = ntfs_file_values_compare(file_name, - (FILE_NAME_ATTR*)&ie->key.file_name, 1, - CASE_SENSITIVE, g_vol->upcase, - g_vol->upcase_len); - if (i == -1) - break; - /* Complete match. Bugger. Can't insert. */ - if (!i) - return -EEXIST; -do_next: -#ifdef DEBUG - /* Next entry. */ - if (!ie->length) { - ntfs_log_debug("BUG: ie->length is zero, breaking out " - "of loop.\n"); - break; - } -#endif - ie = (INDEX_ENTRY*)((char*)ie + le16_to_cpu(ie->length)); - }; - i = (sizeof(INDEX_ENTRY_HEADER) + file_name_size + 7) & ~7; - err = make_room_for_index_entry_in_index_block(idx, ie, i); - if (err) { - ntfs_log_error("make_room_for_index_entry_in_index_block " - "failed: %s\n", strerror(-err)); - return err; - } - /* Create entry in place and copy file name attribute value. */ - ie->u.indexed_file = file_ref; - ie->length = cpu_to_le16(i); - ie->key_length = cpu_to_le16(file_name_size); - ie->flags = cpu_to_le16(0); - ie->reserved = cpu_to_le16(0); - memcpy((char*)&ie->key.file_name, (char*)file_name, file_name_size); - return 0; -} - -/** - * create_hardlink_res - * - * Create a file_name_attribute in the mft record @m_file which points to the - * parent directory with mft reference @ref_parent. - * - * Then, insert an index entry with this file_name_attribute in the index - * root @idx of the index_root attribute of the parent directory. - * - * @ref_file is the mft reference of @m_file. - * - * Return 0 on success or -errno on error. - */ -static int create_hardlink_res(MFT_RECORD *m_parent, const leMFT_REF ref_parent, - MFT_RECORD *m_file, const leMFT_REF ref_file, - const s64 allocated_size, const s64 data_size, - const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, - const u32 reparse_point_tag, const char *file_name, - const FILE_NAME_TYPE_FLAGS file_name_type) -{ - FILE_NAME_ATTR *fn; - int i, fn_size, idx_size; - INDEX_ENTRY *idx_entry_new; - ntfschar *uname; - - /* Create the file_name attribute. */ - i = (strlen(file_name) + 1) * sizeof(ntfschar); - fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = ntfs_malloc(fn_size); - if (!fn) - return -errno; - fn->parent_directory = ref_parent; - /* FIXME: copy the creation_time from the std info */ - fn->creation_time = utc2ntfs(mkntfs_time()); - fn->last_data_change_time = fn->creation_time; - fn->last_mft_change_time = fn->creation_time; - fn->last_access_time = fn->creation_time; - fn->allocated_size = cpu_to_sle64(allocated_size); - fn->data_size = cpu_to_sle64(data_size); - fn->file_attributes = flags; - /* These are in a union so can't have both. */ - if (packed_ea_size && reparse_point_tag) { - free(fn); - return -EINVAL; - } - if (packed_ea_size) { - free(fn); - return -EINVAL; - } - if (packed_ea_size) { - fn->u.s.packed_ea_size = cpu_to_le16(packed_ea_size); - fn->u.s.reserved = cpu_to_le16(0); - } else { - fn->u.reparse_point_tag = cpu_to_le32(reparse_point_tag); - } - fn->file_name_type = file_name_type; - uname = fn->file_name; - i = ntfs_mbstoucs(file_name, &uname, i); - if (i < 1) { - free(fn); - return -EINVAL; - } - if (i > 0xff) { - free(fn); - return -ENAMETOOLONG; - } - /* No terminating null in file names. */ - fn->file_name_length = i; - fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); - /* Increment the link count of @m_file. */ - i = le16_to_cpu(m_file->link_count); - if (i == 0xffff) { - ntfs_log_error("Too many hardlinks present already.\n"); - free(fn); - return -EINVAL; - } - m_file->link_count = cpu_to_le16(i + 1); - /* Add the file_name to @m_file. */ - i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, 0, - 0, RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); - if (i < 0) { - ntfs_log_error("create_hardlink failed adding file name " - "attribute: %s\n", strerror(-i)); - free(fn); - /* Undo link count increment. */ - m_file->link_count = cpu_to_le16( - le16_to_cpu(m_file->link_count) - 1); - return i; - } - /* Insert the index entry for file_name in @idx. */ - idx_size = (fn_size + 7) & ~7; - idx_entry_new = ntfs_calloc(idx_size + 0x10); - if (!idx_entry_new) - return -errno; - idx_entry_new->u.indexed_file = ref_file; - idx_entry_new->length = cpu_to_le16(idx_size + 0x10); - idx_entry_new->key_length = cpu_to_le16(fn_size); - memcpy((u8*)idx_entry_new + 0x10, (u8*)fn, fn_size); - i = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size + 0x10, - m_parent, NTFS_INDEX_I30, 4, AT_FILE_NAME); - if (i < 0) { - ntfs_log_error("create_hardlink failed inserting index entry: " - "%s\n", strerror(-i)); - /* FIXME: Remove the file name attribute from @m_file. */ - free(idx_entry_new); - free(fn); - /* Undo link count increment. */ - m_file->link_count = cpu_to_le16( - le16_to_cpu(m_file->link_count) - 1); - return i; - } - free(idx_entry_new); - free(fn); - return 0; -} - -/** - * create_hardlink - * - * Create a file_name_attribute in the mft record @m_file which points to the - * parent directory with mft reference @ref_parent. - * - * Then, insert an index entry with this file_name_attribute in the index - * block @idx of the index allocation attribute of the parent directory. - * - * @ref_file is the mft reference of @m_file. - * - * Return 0 on success or -errno on error. - */ -static int create_hardlink(INDEX_BLOCK *idx, const leMFT_REF ref_parent, - MFT_RECORD *m_file, const leMFT_REF ref_file, - const s64 allocated_size, const s64 data_size, - const FILE_ATTR_FLAGS flags, const u16 packed_ea_size, - const u32 reparse_point_tag, const char *file_name, - const FILE_NAME_TYPE_FLAGS file_name_type) -{ - FILE_NAME_ATTR *fn; - int i, fn_size; - ntfschar *uname; - - /* Create the file_name attribute. */ - i = (strlen(file_name) + 1) * sizeof(ntfschar); - fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = ntfs_malloc(fn_size); - if (!fn) - return -errno; - fn->parent_directory = ref_parent; - /* FIXME: Is this correct? Or do we have to copy the creation_time */ - /* from the std info? */ - fn->creation_time = utc2ntfs(mkntfs_time()); - fn->last_data_change_time = fn->creation_time; - fn->last_mft_change_time = fn->creation_time; - fn->last_access_time = fn->creation_time; - fn->allocated_size = cpu_to_sle64(allocated_size); - fn->data_size = cpu_to_sle64(data_size); - fn->file_attributes = flags; - /* These are in a union so can't have both. */ - if (packed_ea_size && reparse_point_tag) { - free(fn); - return -EINVAL; - } - if (packed_ea_size) { - fn->u.s.packed_ea_size = cpu_to_le16(packed_ea_size); - fn->u.s.reserved = cpu_to_le16(0); - } else { - fn->u.reparse_point_tag = cpu_to_le32(reparse_point_tag); - } - fn->file_name_type = file_name_type; - uname = fn->file_name; - i = ntfs_mbstoucs(file_name, &uname, i); - if (i < 1) { - free(fn); - return -EINVAL; - } - if (i > 0xff) { - free(fn); - return -ENAMETOOLONG; - } - /* No terminating null in file names. */ - fn->file_name_length = i; - fn_size = sizeof(FILE_NAME_ATTR) + i * sizeof(ntfschar); - /* Increment the link count of @m_file. */ - i = le16_to_cpu(m_file->link_count); - if (i == 0xffff) { - ntfs_log_error("Too many hardlinks present already.\n"); - free(fn); - return -EINVAL; - } - m_file->link_count = cpu_to_le16(i + 1); - /* Add the file_name to @m_file. */ - i = insert_resident_attr_in_mft_record(m_file, AT_FILE_NAME, NULL, 0, 0, - 0, RESIDENT_ATTR_IS_INDEXED, (u8*)fn, fn_size); - if (i < 0) { - ntfs_log_error("create_hardlink failed adding file name attribute: " - "%s\n", strerror(-i)); - free(fn); - /* Undo link count increment. */ - m_file->link_count = cpu_to_le16( - le16_to_cpu(m_file->link_count) - 1); - return i; - } - /* Insert the index entry for file_name in @idx. */ - i = insert_file_link_in_dir_index(idx, ref_file, fn, fn_size); - if (i < 0) { - ntfs_log_error("create_hardlink failed inserting index entry: %s\n", - strerror(-i)); - /* FIXME: Remove the file name attribute from @m_file. */ - free(fn); - /* Undo link count increment. */ - m_file->link_count = cpu_to_le16( - le16_to_cpu(m_file->link_count) - 1); - return i; - } - free(fn); - return 0; -} - -#ifdef ENABLE_UUID - -/** - * index_obj_id_insert - * - * Insert an index entry with the key @guid and data pointing to the mft record - * @ref in the $O index root of the mft record @m (which must be the mft record - * for $ObjId). - * - * Return 0 on success or -errno on error. - */ -static int index_obj_id_insert(MFT_RECORD *m, const GUID *guid, - const leMFT_REF ref) -{ - INDEX_ENTRY *idx_entry_new; - int data_ofs, idx_size, err; - OBJ_ID_INDEX_DATA *oi; - - /* - * Insert the index entry for the object id in the index. - * - * First determine the size of the index entry to be inserted. This - * consists of the index entry header, followed by the index key, i.e. - * the GUID, followed by the index data, i.e. OBJ_ID_INDEX_DATA. - */ - data_ofs = (sizeof(INDEX_ENTRY_HEADER) + sizeof(GUID) + 7) & ~7; - idx_size = (data_ofs + sizeof(OBJ_ID_INDEX_DATA) + 7) & ~7; - idx_entry_new = ntfs_calloc(idx_size); - if (!idx_entry_new) - return -errno; - idx_entry_new->u.s.data_offset = cpu_to_le16(data_ofs); - idx_entry_new->u.s.data_length = cpu_to_le16(sizeof(OBJ_ID_INDEX_DATA)); - idx_entry_new->length = cpu_to_le16(idx_size); - idx_entry_new->key_length = cpu_to_le16(sizeof(GUID)); - idx_entry_new->key.object_id = *guid; - oi = (OBJ_ID_INDEX_DATA*)((u8*)idx_entry_new + data_ofs); - oi->mft_reference = ref; - err = insert_index_entry_in_res_dir_index(idx_entry_new, idx_size, m, - NTFS_INDEX_O, 2, AT_UNUSED); - free(idx_entry_new); - if (err < 0) { - ntfs_log_error("index_obj_id_insert failed inserting index " - "entry: %s\n", strerror(-err)); - return err; - } - return 0; -} - -#endif - -/** - * mkntfs_cleanup - */ -static void mkntfs_cleanup(void) -{ - /* Close the volume */ - if (g_vol) { - if (g_vol->u.dev) { - if (NDevOpen(g_vol->u.dev) && g_vol->u.dev->d_ops->close(g_vol->u.dev)) - ntfs_log_perror("Warning: Could not close %s", g_vol->u.dev->d_name); - ntfs_device_free(g_vol->u.dev); - } - free(g_vol->vol_name); - free(g_vol->attrdef); - free(g_vol->upcase); - free(g_vol); - g_vol = NULL; - } - - /* Free any memory we've used */ - free(g_bad_blocks); g_bad_blocks = NULL; - free(g_buf); g_buf = NULL; - free(g_index_block); g_index_block = NULL; - free(g_lcn_bitmap); g_lcn_bitmap = NULL; - free(g_mft_bitmap); g_mft_bitmap = NULL; - free(g_rl_bad); g_rl_bad = NULL; - free(g_rl_boot); g_rl_boot = NULL; - free(g_rl_logfile); g_rl_logfile = NULL; - free(g_rl_mft); g_rl_mft = NULL; - free(g_rl_mft_bmp); g_rl_mft_bmp = NULL; - free(g_rl_mftmirr); g_rl_mftmirr = NULL; -} - - -/** - * mkntfs_open_partition - - */ -static BOOL mkntfs_open_partition(ntfs_volume *vol) -{ - BOOL result = FALSE; - int i; - struct stat sbuf; - unsigned long mnt_flags; - - /* - * Allocate and initialize an ntfs device structure and attach it to - * the volume. - */ - vol->u.dev = ntfs_device_alloc(opts.dev_name, 0, &ntfs_device_default_io_ops, NULL); - if (!vol->u.dev) { - ntfs_log_perror("Could not create device"); - goto done; - } - - /* Open the device for reading or reading and writing. */ - if (opts.no_action) { - ntfs_log_quiet("Running in READ-ONLY mode!\n"); - i = O_RDONLY; - } else { - i = O_RDWR; - } - if (vol->u.dev->d_ops->open(vol->u.dev, i)) { - if (errno == ENOENT) - ntfs_log_error("The device doesn't exist; did you specify it correctly?\n"); - else - ntfs_log_perror("Could not open %s", vol->u.dev->d_name); - goto done; - } - /* Verify we are dealing with a block device. */ - if (vol->u.dev->d_ops->stat(vol->u.dev, &sbuf)) { - ntfs_log_perror("Error getting information about %s", vol->u.dev->d_name); - goto done; - } - - if (!S_ISBLK(sbuf.st_mode)) { - ntfs_log_error("%s is not a block device.\n", vol->u.dev->d_name); - if (!opts.force) { - ntfs_log_error("Refusing to make a filesystem here!\n"); - goto done; - } - if (!opts.num_sectors) { - if (!sbuf.st_size && !sbuf.st_blocks) { - ntfs_log_error("You must specify the number of sectors.\n"); - goto done; - } - if (opts.sector_size) { - if (sbuf.st_size) - opts.num_sectors = sbuf.st_size / opts.sector_size; - else - opts.num_sectors = ((s64)sbuf.st_blocks << 9) / opts.sector_size; - } else { - if (sbuf.st_size) - opts.num_sectors = sbuf.st_size / 512; - else - opts.num_sectors = sbuf.st_blocks; - opts.sector_size = 512; - } - } - ntfs_log_warning("mkntfs forced anyway.\n"); -#ifdef HAVE_LINUX_MAJOR_H - } else if ((IDE_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && - MINOR(sbuf.st_rdev) % 64 == 0) || - (SCSI_DISK_MAJOR(MAJOR(sbuf.st_rdev)) && - MINOR(sbuf.st_rdev) % 16 == 0)) { - ntfs_log_error("%s is entire device, not just one partition.\n", vol->u.dev->d_name); - if (!opts.force) { - ntfs_log_error("Refusing to make a filesystem here!\n"); - goto done; - } - ntfs_log_warning("mkntfs forced anyway.\n"); -#endif - } - /* Make sure the file system is not mounted. */ - if (ntfs_check_if_mounted(vol->u.dev->d_name, &mnt_flags)) { - ntfs_log_perror("Failed to determine whether %s is mounted", vol->u.dev->d_name); - } else if (mnt_flags & NTFS_MF_MOUNTED) { - ntfs_log_error("%s is mounted.\n", vol->u.dev->d_name); - if (!opts.force) { - ntfs_log_error("Refusing to make a filesystem here!\n"); - goto done; - } - ntfs_log_warning("mkntfs forced anyway. Hope /etc/mtab is incorrect.\n"); - } - result = TRUE; -done: - return result; -} - -/** - * mkntfs_get_page_size - detect the system's memory page size. - */ -static long mkntfs_get_page_size(void) -{ - long page_size; -#ifdef _SC_PAGESIZE - page_size = sysconf(_SC_PAGESIZE); - if (page_size < 0) -#endif -#ifdef _SC_PAGE_SIZE - page_size = sysconf(_SC_PAGE_SIZE); - if (page_size < 0) -#endif - { - ntfs_log_warning("Failed to determine system page size. " - "Assuming safe default of 4096 bytes.\n"); - return 4096; - } - ntfs_log_debug("System page size is %li bytes.\n", page_size); - return page_size; -} - -/** - * mkntfs_override_vol_params - - */ -static BOOL mkntfs_override_vol_params(ntfs_volume *vol) -{ - s64 volume_size; - long page_size; - int i; - BOOL winboot = TRUE; - - /* If user didn't specify the sector size, determine it now. */ - if (opts.sector_size < 0) { - opts.sector_size = ntfs_device_sector_size_get(vol->u.dev); - if (opts.sector_size < 0) { - ntfs_log_warning("The sector size was not specified " - "for %s and it could not be obtained " - "automatically. It has been set to 512 " - "bytes.\n", vol->u.dev->d_name); - opts.sector_size = 512; - } - } - /* Validate sector size. */ - if ((opts.sector_size - 1) & opts.sector_size) { - ntfs_log_error("The sector size is invalid. It must be a " - "power of two, e.g. 512, 1024.\n"); - return FALSE; - } - if (opts.sector_size < 256 || opts.sector_size > 4096) { - ntfs_log_error("The sector size is invalid. The minimum size " - "is 256 bytes and the maximum is 4096 bytes.\n"); - return FALSE; - } - ntfs_log_debug("sector size = %ld bytes\n", opts.sector_size); - /* Now set the device block size to the sector size. */ - if (ntfs_device_block_size_set(vol->u.dev, opts.sector_size)) - ntfs_log_debug("Failed to set the device block size to the " - "sector size. This may cause problems when " - "creating the backup boot sector and also may " - "affect performance but should be harmless " - "otherwise. Error: %s\n", strerror(errno)); - /* If user didn't specify the number of sectors, determine it now. */ - if (opts.num_sectors < 0) { - opts.num_sectors = ntfs_device_size_get(vol->u.dev, - opts.sector_size); - if (opts.num_sectors <= 0) { - ntfs_log_error("Couldn't determine the size of %s. " - "Please specify the number of sectors " - "manually.\n", vol->u.dev->d_name); - return FALSE; - } - } - ntfs_log_debug("number of sectors = %lld (0x%llx)\n", opts.num_sectors, - opts.num_sectors); - /* - * Reserve the last sector for the backup boot sector unless the - * sector size is less than 512 bytes in which case reserve 512 bytes - * worth of sectors. - */ - i = 1; - if (opts.sector_size < 512) - i = 512 / opts.sector_size; - opts.num_sectors -= i; - /* If user didn't specify the partition start sector, determine it. */ - if (opts.part_start_sect < 0) { - opts.part_start_sect = ntfs_device_partition_start_sector_get( - vol->u.dev); - if (opts.part_start_sect < 0) { - ntfs_log_warning("The partition start sector was not " - "specified for %s and it could not be obtained " - "automatically. It has been set to 0.\n", - vol->u.dev->d_name); - opts.part_start_sect = 0; - winboot = FALSE; - } else if (opts.part_start_sect >> 32) { - ntfs_log_warning("The partition start sector specified " - "for %s and the automatically determined value " - "is too large. It has been set to 0.\n", - vol->u.dev->d_name); - opts.part_start_sect = 0; - winboot = FALSE; - } - } else if (opts.part_start_sect >> 32) { - ntfs_log_error("Invalid partition start sector. Maximum is " - "4294967295 (2^32-1).\n"); - return FALSE; - } - /* If user didn't specify the sectors per track, determine it now. */ - if (opts.sectors_per_track < 0) { - opts.sectors_per_track = ntfs_device_sectors_per_track_get( - vol->u.dev); - if (opts.sectors_per_track < 0) { - ntfs_log_warning("The number of sectors per track was " - "not specified for %s and it could not be " - "obtained automatically. It has been set to " - "0.\n", vol->u.dev->d_name); - opts.sectors_per_track = 0; - winboot = FALSE; - } else if (opts.sectors_per_track > 65535) { - ntfs_log_warning("The number of sectors per track was " - "not specified for %s and the automatically " - "determined value is too large. It has been " - "set to 0.\n", vol->u.dev->d_name); - opts.sectors_per_track = 0; - winboot = FALSE; - } - } else if (opts.sectors_per_track > 65535) { - ntfs_log_error("Invalid number of sectors per track. Maximum " - "is 65535.\n"); - return FALSE; - } - /* If user didn't specify the number of heads, determine it now. */ - if (opts.heads < 0) { - opts.heads = ntfs_device_heads_get(vol->u.dev); - if (opts.heads < 0) { - ntfs_log_warning("The number of heads was not " - "specified for %s and it could not be obtained " - "automatically. It has been set to 0.\n", - vol->u.dev->d_name); - opts.heads = 0; - winboot = FALSE; - } else if (opts.heads > 65535) { - ntfs_log_warning("The number of heads was not " - "specified for %s and the automatically " - "determined value is too large. It has been " - "set to 0.\n", vol->u.dev->d_name); - opts.heads = 0; - winboot = FALSE; - } - } else if (opts.heads > 65535) { - ntfs_log_error("Invalid number of heads. Maximum is 65535.\n"); - return FALSE; - } - volume_size = opts.num_sectors * opts.sector_size; - /* Validate volume size. */ - if (volume_size < (1 << 20)) { /* 1MiB */ - ntfs_log_error("Device is too small (%llikiB). Minimum NTFS " - "volume size is 1MiB.\n", volume_size / 1024); - return FALSE; - } - ntfs_log_debug("volume size = %llikiB\n", volume_size / 1024); - /* If user didn't specify the cluster size, determine it now. */ - if (!vol->cluster_size) { - /* - * Windows Vista always uses 4096 bytes as the default cluster - * size regardless of the volume size so we do it, too. - */ - vol->cluster_size = 4096; - /* For small volumes on devices with large sector sizes. */ - if (vol->cluster_size < (u32)opts.sector_size) - vol->cluster_size = opts.sector_size; - /* - * For huge volumes, grow the cluster size until the number of - * clusters fits into 32 bits or the cluster size exceeds the - * maximum limit of 64kiB. - */ - while (volume_size >> (ffs(vol->cluster_size) - 1 + 32)) { - vol->cluster_size <<= 1; - if (vol->cluster_size > 65535) { - ntfs_log_error("Device is too large to hold an " - "NTFS volume (maximum size is " - "256TiB).\n"); - return FALSE; - } - } - ntfs_log_quiet("Cluster size has been automatically set to %u " - "bytes.\n", (unsigned)vol->cluster_size); - } - /* Validate cluster size. */ - if (vol->cluster_size & (vol->cluster_size - 1)) { - ntfs_log_error("The cluster size is invalid. It must be a " - "power of two, e.g. 1024, 4096.\n"); - return FALSE; - } - if (vol->cluster_size < (u32)opts.sector_size) { - ntfs_log_error("The cluster size is invalid. It must be equal " - "to, or larger than, the sector size.\n"); - return FALSE; - } - if (vol->cluster_size > 128 * (u32)opts.sector_size) { - ntfs_log_error("The cluster size is invalid. It cannot be " - "more that 128 times the size of the sector " - "size.\n"); - return FALSE; - } - if (vol->cluster_size > 65536) { - ntfs_log_error("The cluster size is invalid. The maximum " - "cluster size is 65536 bytes (64kiB).\n"); - return FALSE; - } - vol->cluster_size_bits = ffs(vol->cluster_size) - 1; - ntfs_log_debug("cluster size = %u bytes\n", - (unsigned int)vol->cluster_size); - if (vol->cluster_size > 4096) { - if (opts.enable_compression) { - if (!opts.force) { - ntfs_log_error("Windows cannot use compression " - "when the cluster size is " - "larger than 4096 bytes.\n"); - return FALSE; - } - opts.enable_compression = 0; - } - ntfs_log_warning("Windows cannot use compression when the " - "cluster size is larger than 4096 bytes. " - "Compression has been disabled for this " - "volume.\n"); - } - vol->nr_clusters = volume_size / vol->cluster_size; - /* - * Check the cluster_size and num_sectors for consistency with - * sector_size and num_sectors. And check both of these for consistency - * with volume_size. - */ - if ((vol->nr_clusters != ((opts.num_sectors * opts.sector_size) / - vol->cluster_size) || - (volume_size / opts.sector_size) != opts.num_sectors || - (volume_size / vol->cluster_size) != - vol->nr_clusters)) { - /* XXX is this code reachable? */ - ntfs_log_error("Illegal combination of volume/cluster/sector " - "size and/or cluster/sector number.\n"); - return FALSE; - } - ntfs_log_debug("number of clusters = %llu (0x%llx)\n", - vol->nr_clusters, vol->nr_clusters); - /* Number of clusters must fit within 32 bits (Win2k limitation). */ - if (vol->nr_clusters >> 32) { - if (vol->cluster_size >= 65536) { - ntfs_log_error("Device is too large to hold an NTFS " - "volume (maximum size is 256TiB).\n"); - return FALSE; - } - ntfs_log_error("Number of clusters exceeds 32 bits. Please " - "try again with a larger\ncluster size or " - "leave the cluster size unspecified and the " - "smallest possible cluster size for the size " - "of the device will be used.\n"); - return FALSE; - } - page_size = mkntfs_get_page_size(); - /* - * Set the mft record size. By default this is 1024 but it has to be - * at least as big as a sector and not bigger than a page on the system - * or the NTFS kernel driver will not be able to mount the volume. - * TODO: The mft record size should be user specifiable just like the - * "inode size" can be specified on other Linux/Unix file systems. - */ - vol->mft_record_size = 1024; - if (vol->mft_record_size < (u32)opts.sector_size) - vol->mft_record_size = opts.sector_size; - if (vol->mft_record_size > (unsigned long)page_size) - ntfs_log_warning("Mft record size (%u bytes) exceeds system " - "page size (%li bytes). You will not be able " - "to mount this volume using the NTFS kernel " - "driver.\n", (unsigned)vol->mft_record_size, - page_size); - vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1; - ntfs_log_debug("mft record size = %u bytes\n", - (unsigned)vol->mft_record_size); - /* - * Set the index record size. By default this is 4096 but it has to be - * at least as big as a sector and not bigger than a page on the system - * or the NTFS kernel driver will not be able to mount the volume. - * FIXME: Should we make the index record size to be user specifiable? - */ - vol->indx_record_size = 4096; - if (vol->indx_record_size < (u32)opts.sector_size) - vol->indx_record_size = opts.sector_size; - if (vol->indx_record_size > (unsigned long)page_size) - ntfs_log_warning("Index record size (%u bytes) exceeds system " - "page size (%li bytes). You will not be able " - "to mount this volume using the NTFS kernel " - "driver.\n", (unsigned)vol->indx_record_size, - page_size); - vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1; - ntfs_log_debug("index record size = %u bytes\n", - (unsigned)vol->indx_record_size); - if (!winboot) { - ntfs_log_warning("To boot from a device, Windows needs the " - "'partition start sector', the 'sectors per " - "track' and the 'number of heads' to be " - "set.\n"); - ntfs_log_warning("Windows will not be able to boot from this " - "device.\n"); - } - return TRUE; -} - -/** - * mkntfs_initialize_bitmaps - - */ -static BOOL mkntfs_initialize_bitmaps(void) -{ - u64 i; - int mft_bitmap_size; - - /* Determine lcn bitmap byte size and allocate it. */ - g_lcn_bitmap_byte_size = (g_vol->nr_clusters + 7) >> 3; - /* Needs to be multiple of 8 bytes. */ - g_lcn_bitmap_byte_size = (g_lcn_bitmap_byte_size + 7) & ~7; - i = (g_lcn_bitmap_byte_size + g_vol->cluster_size - 1) & - ~(g_vol->cluster_size - 1); - ntfs_log_debug("g_lcn_bitmap_byte_size = %i, allocated = %llu\n", - g_lcn_bitmap_byte_size, i); - g_lcn_bitmap = ntfs_calloc(g_lcn_bitmap_byte_size); - if (!g_lcn_bitmap) - return FALSE; - /* - * $Bitmap can overlap the end of the volume. Any bits in this region - * must be set. This region also encompasses the backup boot sector. - */ - for (i = g_vol->nr_clusters; i < (u64)g_lcn_bitmap_byte_size << 3; i++) - ntfs_bit_set(g_lcn_bitmap, i, 1); - /* - * Mft size is 27 (NTFS 3.0+) mft records or one cluster, whichever is - * bigger. - */ - g_mft_size = 27; - g_mft_size *= g_vol->mft_record_size; - if (g_mft_size < (s32)g_vol->cluster_size) - g_mft_size = g_vol->cluster_size; - ntfs_log_debug("MFT size = %i (0x%x) bytes\n", g_mft_size, g_mft_size); - /* Determine mft bitmap size and allocate it. */ - mft_bitmap_size = g_mft_size / g_vol->mft_record_size; - /* Convert to bytes, at least one. */ - g_mft_bitmap_byte_size = (mft_bitmap_size + 7) >> 3; - /* Mft bitmap is allocated in multiples of 8 bytes. */ - g_mft_bitmap_byte_size = (g_mft_bitmap_byte_size + 7) & ~7; - ntfs_log_debug("mft_bitmap_size = %i, g_mft_bitmap_byte_size = %i\n", - mft_bitmap_size, g_mft_bitmap_byte_size); - g_mft_bitmap = ntfs_calloc(g_mft_bitmap_byte_size); - if (!g_mft_bitmap) - return FALSE; - /* Create runlist for mft bitmap. */ - g_rl_mft_bmp = ntfs_malloc(2 * sizeof(runlist)); - if (!g_rl_mft_bmp) - return FALSE; - - g_rl_mft_bmp[0].vcn = 0LL; - /* Mft bitmap is right after $Boot's data. */ - i = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; - g_rl_mft_bmp[0].lcn = i; - /* - * Size is always one cluster, even though valid data size and - * initialized data size are only 8 bytes. - */ - g_rl_mft_bmp[1].vcn = 1LL; - g_rl_mft_bmp[0].length = 1LL; - g_rl_mft_bmp[1].lcn = -1LL; - g_rl_mft_bmp[1].length = 0LL; - /* Allocate cluster for mft bitmap. */ - ntfs_bit_set(g_lcn_bitmap, i, 1); - return TRUE; -} - -/** - * mkntfs_initialize_rl_mft - - */ -static BOOL mkntfs_initialize_rl_mft(void) -{ - int i, j; - - /* If user didn't specify the mft lcn, determine it now. */ - if (!g_mft_lcn) { - /* - * We start at the higher value out of 16kiB and just after the - * mft bitmap. - */ - g_mft_lcn = g_rl_mft_bmp[0].lcn + g_rl_mft_bmp[0].length; - if (g_mft_lcn * g_vol->cluster_size < 16 * 1024) - g_mft_lcn = (16 * 1024 + g_vol->cluster_size - 1) / - g_vol->cluster_size; - } - ntfs_log_debug("$MFT logical cluster number = 0x%llx\n", g_mft_lcn); - /* Determine MFT zone size. */ - g_mft_zone_end = g_vol->nr_clusters; - switch (opts.mft_zone_multiplier) { /* % of volume size in clusters */ - case 4: - g_mft_zone_end = g_mft_zone_end >> 1; /* 50% */ - break; - case 3: - g_mft_zone_end = g_mft_zone_end * 3 >> 3;/* 37.5% */ - break; - case 2: - g_mft_zone_end = g_mft_zone_end >> 2; /* 25% */ - break; - case 1: - default: - g_mft_zone_end = g_mft_zone_end >> 3; /* 12.5% */ - break; - } - ntfs_log_debug("MFT zone size = %lldkiB\n", g_mft_zone_end << - g_vol->cluster_size_bits >> 10 /* >> 10 == / 1024 */); - /* - * The mft zone begins with the mft data attribute, not at the beginning - * of the device. - */ - g_mft_zone_end += g_mft_lcn; - /* Create runlist for mft. */ - g_rl_mft = ntfs_malloc(2 * sizeof(runlist)); - if (!g_rl_mft) - return FALSE; - - g_rl_mft[0].vcn = 0LL; - g_rl_mft[0].lcn = g_mft_lcn; - /* rounded up division by cluster size */ - j = (g_mft_size + g_vol->cluster_size - 1) / g_vol->cluster_size; - g_rl_mft[1].vcn = j; - g_rl_mft[0].length = j; - g_rl_mft[1].lcn = -1LL; - g_rl_mft[1].length = 0LL; - /* Allocate clusters for mft. */ - for (i = 0; i < j; i++) - ntfs_bit_set(g_lcn_bitmap, g_mft_lcn + i, 1); - /* Determine mftmirr_lcn (middle of volume). */ - g_mftmirr_lcn = (opts.num_sectors * opts.sector_size >> 1) - / g_vol->cluster_size; - ntfs_log_debug("$MFTMirr logical cluster number = 0x%llx\n", - g_mftmirr_lcn); - /* Create runlist for mft mirror. */ - g_rl_mftmirr = ntfs_malloc(2 * sizeof(runlist)); - if (!g_rl_mftmirr) - return FALSE; - - g_rl_mftmirr[0].vcn = 0LL; - g_rl_mftmirr[0].lcn = g_mftmirr_lcn; - /* - * The mft mirror is either 4kb (the first four records) or one cluster - * in size, which ever is bigger. In either case, it contains a - * byte-for-byte identical copy of the beginning of the mft (i.e. either - * the first four records (4kb) or the first cluster worth of records, - * whichever is bigger). - */ - j = (4 * g_vol->mft_record_size + g_vol->cluster_size - 1) / g_vol->cluster_size; - g_rl_mftmirr[1].vcn = j; - g_rl_mftmirr[0].length = j; - g_rl_mftmirr[1].lcn = -1LL; - g_rl_mftmirr[1].length = 0LL; - /* Allocate clusters for mft mirror. */ - for (i = 0; i < j; i++) - ntfs_bit_set(g_lcn_bitmap, g_mftmirr_lcn + i, 1); - g_logfile_lcn = g_mftmirr_lcn + j; - ntfs_log_debug("$LogFile logical cluster number = 0x%llx\n", - g_logfile_lcn); - return TRUE; -} - -/** - * mkntfs_initialize_rl_logfile - - */ -static BOOL mkntfs_initialize_rl_logfile(void) -{ - int i, j; - u64 volume_size; - - /* Create runlist for log file. */ - g_rl_logfile = ntfs_malloc(2 * sizeof(runlist)); - if (!g_rl_logfile) - return FALSE; - - - volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; - - g_rl_logfile[0].vcn = 0LL; - g_rl_logfile[0].lcn = g_logfile_lcn; - /* - * Determine logfile_size from volume_size (rounded up to a cluster), - * making sure it does not overflow the end of the volume. - */ - if (volume_size < 2048LL * 1024) /* < 2MiB */ - g_logfile_size = 256LL * 1024; /* -> 256kiB */ - else if (volume_size < 4000000LL) /* < 4MB */ - g_logfile_size = 512LL * 1024; /* -> 512kiB */ - else if (volume_size <= 200LL * 1024 * 1024) /* < 200MiB */ - g_logfile_size = 2048LL * 1024; /* -> 2MiB */ - else { - /* - * FIXME: The $LogFile size is 64 MiB upwards from 12GiB but - * the "200" divider below apparently approximates "100" or - * some other value as the volume size decreases. For example: - * Volume size LogFile size Ratio - * 8799808 46048 191.100 - * 8603248 45072 190.877 - * 7341704 38768 189.375 - * 6144828 32784 187.433 - * 4192932 23024 182.111 - */ - if (volume_size >= 12LL << 30) /* > 12GiB */ - g_logfile_size = 64 << 20; /* -> 64MiB */ - else - g_logfile_size = (volume_size / 200) & - ~(g_vol->cluster_size - 1); - } - j = g_logfile_size / g_vol->cluster_size; - while (g_rl_logfile[0].lcn + j >= g_vol->nr_clusters) { - /* - * $Logfile would overflow volume. Need to make it smaller than - * the standard size. It's ok as we are creating a non-standard - * volume anyway if it is that small. - */ - g_logfile_size >>= 1; - j = g_logfile_size / g_vol->cluster_size; - } - g_logfile_size = (g_logfile_size + g_vol->cluster_size - 1) & - ~(g_vol->cluster_size - 1); - ntfs_log_debug("$LogFile (journal) size = %ikiB\n", - g_logfile_size / 1024); - /* - * FIXME: The 256kiB limit is arbitrary. Should find out what the real - * minimum requirement for Windows is so it doesn't blue screen. - */ - if (g_logfile_size < 256 << 10) { - ntfs_log_error("$LogFile would be created with invalid size. " - "This is not allowed as it would cause Windows " - "to blue screen and during boot.\n"); - return FALSE; - } - g_rl_logfile[1].vcn = j; - g_rl_logfile[0].length = j; - g_rl_logfile[1].lcn = -1LL; - g_rl_logfile[1].length = 0LL; - /* Allocate clusters for log file. */ - for (i = 0; i < j; i++) - ntfs_bit_set(g_lcn_bitmap, g_logfile_lcn + i, 1); - return TRUE; -} - -/** - * mkntfs_initialize_rl_boot - - */ -static BOOL mkntfs_initialize_rl_boot(void) -{ - int i, j; - /* Create runlist for $Boot. */ - g_rl_boot = ntfs_malloc(2 * sizeof(runlist)); - if (!g_rl_boot) - return FALSE; - - g_rl_boot[0].vcn = 0LL; - g_rl_boot[0].lcn = 0LL; - /* - * $Boot is always 8192 (0x2000) bytes or 1 cluster, whichever is - * bigger. - */ - j = (8192 + g_vol->cluster_size - 1) / g_vol->cluster_size; - g_rl_boot[1].vcn = j; - g_rl_boot[0].length = j; - g_rl_boot[1].lcn = -1LL; - g_rl_boot[1].length = 0LL; - /* Allocate clusters for $Boot. */ - for (i = 0; i < j; i++) - ntfs_bit_set(g_lcn_bitmap, 0LL + i, 1); - return TRUE; -} - -/** - * mkntfs_initialize_rl_bad - - */ -static BOOL mkntfs_initialize_rl_bad(void) -{ - /* Create runlist for $BadClus, $DATA named stream $Bad. */ - g_rl_bad = ntfs_malloc(2 * sizeof(runlist)); - if (!g_rl_bad) - return FALSE; - - g_rl_bad[0].vcn = 0LL; - g_rl_bad[0].lcn = -1LL; - /* - * $BadClus named stream $Bad contains the whole volume as a single - * sparse runlist entry. - */ - g_rl_bad[1].vcn = g_vol->nr_clusters; - g_rl_bad[0].length = g_vol->nr_clusters; - g_rl_bad[1].lcn = -1LL; - g_rl_bad[1].length = 0LL; - - /* TODO: Mark bad blocks as such. */ - return TRUE; -} - -/** - * mkntfs_fill_device_with_zeroes - - */ -static BOOL mkntfs_fill_device_with_zeroes(void) -{ - /* - * If not quick format, fill the device with 0s. - * FIXME: Except bad blocks! (AIA) - */ - int i; - ssize_t bw; - unsigned long long position; - float progress_inc = (float)g_vol->nr_clusters / 100; - u64 volume_size; - - volume_size = g_vol->nr_clusters << g_vol->cluster_size_bits; - - ntfs_log_progress("Initializing device with zeroes: 0%%"); - for (position = 0; position < (unsigned long long)g_vol->nr_clusters; - position++) { - if (!(position % (int)(progress_inc+1))) { - ntfs_log_progress("\b\b\b\b%3.0f%%", position / - progress_inc); - } - bw = mkntfs_write(g_vol->u.dev, g_buf, g_vol->cluster_size); - if (bw != (ssize_t)g_vol->cluster_size) { - if (bw != -1 || errno != EIO) { - ntfs_log_error("This should not happen.\n"); - return FALSE; - } - if (!position) { - ntfs_log_error("Error: Cluster zero is bad. " - "Cannot create NTFS file " - "system.\n"); - return FALSE; - } - /* Add the baddie to our bad blocks list. */ - if (!append_to_bad_blocks(position)) - return FALSE; - ntfs_log_quiet("\nFound bad cluster (%lld). Adding to " - "list of bad blocks.\nInitializing " - "device with zeroes: %3.0f%%", position, - position / progress_inc); - /* Seek to next cluster. */ - g_vol->u.dev->d_ops->seek(g_vol->u.dev, - ((off_t)position + 1) * - g_vol->cluster_size, SEEK_SET); - } - } - ntfs_log_progress("\b\b\b\b100%%"); - position = (volume_size & (g_vol->cluster_size - 1)) / - opts.sector_size; - for (i = 0; (unsigned long)i < position; i++) { - bw = mkntfs_write(g_vol->u.dev, g_buf, opts.sector_size); - if (bw != opts.sector_size) { - if (bw != -1 || errno != EIO) { - ntfs_log_error("This should not happen.\n"); - return FALSE; - } else if (i + 1ull == position) { - ntfs_log_error("Error: Bad cluster found in " - "location reserved for system " - "file $Boot.\n"); - return FALSE; - } - /* Seek to next sector. */ - g_vol->u.dev->d_ops->seek(g_vol->u.dev, - opts.sector_size, SEEK_CUR); - } - } - ntfs_log_progress(" - Done.\n"); - return TRUE; -} - -/** - * mkntfs_sync_index_record - * - * (ERSO) made a function out of this, but the reason for doing that - * disappeared during coding.... - */ -static BOOL mkntfs_sync_index_record(INDEX_ALLOCATION* idx, MFT_RECORD* m, - ntfschar* name, u32 name_len) -{ - int i, err; - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - long long lw; - runlist *rl_index = NULL; - - i = 5 * sizeof(ntfschar); - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search context"); - return FALSE; - } - /* FIXME: This should be IGNORE_CASE! */ - if (mkntfs_attr_lookup(AT_INDEX_ALLOCATION, name, name_len, 0, 0, - NULL, 0, ctx)) { - ntfs_attr_put_search_ctx(ctx); - ntfs_log_error("BUG: $INDEX_ALLOCATION attribute not found.\n"); - return FALSE; - } - a = ctx->attr; - rl_index = ntfs_mapping_pairs_decompress(g_vol, a, NULL); - if (!rl_index) { - ntfs_attr_put_search_ctx(ctx); - ntfs_log_error("Failed to decompress runlist of $INDEX_ALLOCATION " - "attribute.\n"); - return FALSE; - } - if (sle64_to_cpu(a->u.nonres.initialized_size) < i) { - ntfs_attr_put_search_ctx(ctx); - free(rl_index); - ntfs_log_error("BUG: $INDEX_ALLOCATION attribute too short.\n"); - return FALSE; - } - ntfs_attr_put_search_ctx(ctx); - i = sizeof(INDEX_BLOCK) - sizeof(INDEX_HEADER) + - le32_to_cpu(idx->index.allocated_size); - err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)idx, i); - if (err) { - free(rl_index); - ntfs_log_error("ntfs_mst_pre_write_fixup() failed while " - "syncing index block.\n"); - return FALSE; - } - lw = ntfs_rlwrite(g_vol->u.dev, rl_index, (u8*)idx, i, NULL); - free(rl_index); - if (lw != i) { - ntfs_log_error("Error writing $INDEX_ALLOCATION.\n"); - return FALSE; - } - /* No more changes to @idx below here so no need for fixup: */ - /* ntfs_mst_post_write_fixup((NTFS_RECORD*)idx); */ - return TRUE; -} - -/** - * create_file_volume - - */ -static BOOL create_file_volume(MFT_RECORD *m, leMFT_REF root_ref, - VOLUME_FLAGS fl, const GUID *volume_guid -#ifndef ENABLE_UUID - __attribute__((unused)) -#endif - ) -{ - int i, err; - u8 *sd; - - ntfs_log_verbose("Creating $Volume (mft record 3)\n"); - m = (MFT_RECORD*)(g_buf + 3 * g_vol->mft_record_size); - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_Volume, FILE_Volume), 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$Volume", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Volume, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (!err) - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - if (!err) - err = add_attr_vol_name(m, g_vol->vol_name, g_vol->vol_name ? - strlen(g_vol->vol_name) : 0); - if (!err) { - if (fl & VOLUME_IS_DIRTY) - ntfs_log_quiet("Setting the volume dirty so check " - "disk runs on next reboot into " - "Windows.\n"); - err = add_attr_vol_info(m, fl, g_vol->major_ver, - g_vol->minor_ver); - } -#ifdef ENABLE_UUID - if (!err) - err = add_attr_object_id(m, volume_guid); -#endif - if (err < 0) { - ntfs_log_error("Couldn't create $Volume: %s\n", - strerror(-err)); - return FALSE; - } - return TRUE; -} - -/** - * create_backup_boot_sector - * - * Return 0 on success or -1 if it couldn't be created. - */ -static int create_backup_boot_sector(u8 *buff) -{ - const char *s; - ssize_t bw; - int size, e; - - ntfs_log_verbose("Creating backup boot sector.\n"); - /* - * Write the first max(512, opts.sector_size) bytes from buf to the - * last sector, but limit that to 8192 bytes of written data since that - * is how big $Boot is (and how big our buffer is).. - */ - size = 512; - if (size < opts.sector_size) - size = opts.sector_size; - if (size < opts.cluster_size) - size = opts.cluster_size; - if (g_vol->u.dev->d_ops->seek(g_vol->u.dev, (opts.num_sectors + 1) * - opts.sector_size - size, SEEK_SET) == (off_t)-1) { - ntfs_log_perror("Seek failed"); - goto bb_err; - } - if (size > 8192) - size = 8192; - bw = mkntfs_write(g_vol->u.dev, buff, size); - if (bw == size) - return 0; - e = errno; - if (bw == -1LL) - s = strerror(e); - else - s = "unknown error"; - /* At least some 2.4 kernels return EIO instead of ENOSPC. */ - if (bw != -1LL || (bw == -1LL && e != ENOSPC && e != EIO)) { - ntfs_log_critical("Couldn't write backup boot sector: %s\n", s); - return -1; - } -bb_err: - ntfs_log_error("Couldn't write backup boot sector. This is due to a " - "limitation in the\nLinux kernel. This is not a major " - "problem as Windows check disk will create the\n" - "backup boot sector when it is run on your next boot " - "into Windows.\n"); - return -1; -} - -/** - * mkntfs_create_root_structures - - */ -static BOOL mkntfs_create_root_structures(void) -{ - NTFS_BOOT_SECTOR *bs; - MFT_RECORD *m; - leMFT_REF root_ref; - leMFT_REF extend_ref; - int i; - int j; - int err; - u8 *sd; - FILE_ATTR_FLAGS extend_flags; - VOLUME_FLAGS volume_flags = 0; - int nr_sysfiles; - u8 *buf_log = NULL; - int buf_sds_first_size; - char *buf_sds; - - ntfs_log_quiet("Creating NTFS volume structures.\n"); - nr_sysfiles = 27; - /* - * Setup an empty mft record. Note, we can just give 0 as the mft - * reference as we are creating an NTFS 1.2 volume for which the mft - * reference is ignored by ntfs_mft_record_layout(). - * - * Copy the mft record onto all 16 records in the buffer and setup the - * sequence numbers of each system file to equal the mft record number - * of that file (only for $MFT is the sequence number 1 rather than 0). - */ - for (i = 0; i < nr_sysfiles; i++) { - if (ntfs_mft_record_layout(g_vol, 0, m = (MFT_RECORD *)(g_buf + - i * g_vol->mft_record_size))) { - ntfs_log_error("Failed to layout system mft records." - "\n"); - return FALSE; - } - if (i == 0 || i > 23) - m->sequence_number = cpu_to_le16(1); - else - m->sequence_number = cpu_to_le16(i); - } - /* - * If only one cluster contains all system files then - * fill the rest of it with empty, formatted records. - */ - if (nr_sysfiles * (s32)g_vol->mft_record_size < g_mft_size) { - for (i = nr_sysfiles; - i * (s32)g_vol->mft_record_size < g_mft_size; i++) { - m = (MFT_RECORD *)(g_buf + i * g_vol->mft_record_size); - if (ntfs_mft_record_layout(g_vol, 0, m)) { - ntfs_log_error("Failed to layout mft record." - "\n"); - return FALSE; - } - m->flags = cpu_to_le16(0); - m->sequence_number = cpu_to_le16(i); - } - } - /* - * Create the 16 system files, adding the system information attribute - * to each as well as marking them in use in the mft bitmap. - */ - for (i = 0; i < nr_sysfiles; i++) { - le32 file_attrs; - - m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); - if (i < 16 || i > 23) { - m->mft_record_number = cpu_to_le32(i); - m->flags |= MFT_RECORD_IN_USE; - ntfs_bit_set(g_mft_bitmap, 0LL + i, 1); - } - file_attrs = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM; - if (i == FILE_root) { - file_attrs |= FILE_ATTR_ARCHIVE; - if (opts.disable_indexing) - file_attrs |= FILE_ATTR_NOT_CONTENT_INDEXED; - if (opts.enable_compression) - file_attrs |= FILE_ATTR_COMPRESSED; - } - /* setting specific security_id flag and */ - /* file permissions for ntfs 3.x */ - if (i == 0 || i == 1 || i == 2 || i == 6 || i == 8 || - i == 10) { - add_attr_std_info(m, file_attrs, - cpu_to_le32(0x0100)); - } else if (i == 9) { - file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; - add_attr_std_info(m, file_attrs, - cpu_to_le32(0x0101)); - } else if (i == 11) { - add_attr_std_info(m, file_attrs, - cpu_to_le32(0x0101)); - } else if (i == 24 || i == 25 || i == 26) { - file_attrs |= FILE_ATTR_ARCHIVE; - file_attrs |= FILE_ATTR_VIEW_INDEX_PRESENT; - add_attr_std_info(m, file_attrs, - cpu_to_le32(0x0101)); - } else { - add_attr_std_info(m, file_attrs, - cpu_to_le32(0x00)); - } - } - /* The root directory mft reference. */ - root_ref = MK_LE_MREF(FILE_root, FILE_root); - extend_ref = MK_LE_MREF(11,11); - ntfs_log_verbose("Creating root directory (mft record 5)\n"); - m = (MFT_RECORD*)(g_buf + 5 * g_vol->mft_record_size); - m->flags |= MFT_RECORD_IS_DIRECTORY; - m->link_count = cpu_to_le16(le16_to_cpu(m->link_count) + 1); - err = add_attr_file_name(m, root_ref, 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | - FILE_ATTR_I30_INDEX_PRESENT, 0, 0, ".", - FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_root_sd(&sd, &i); - err = add_attr_sd(m, sd, i); - } - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$I30", 4, 0, AT_FILE_NAME, - COLLATION_FILE_NAME, g_vol->indx_record_size); - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = upgrade_to_large_index(m, "$I30", 4, 0, &g_index_block); - if (!err) { - ntfs_attr_search_ctx *ctx; - ATTR_RECORD *a; - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search " - "context"); - return FALSE; - } - /* There is exactly one file name so this is ok. */ - if (mkntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, - 0, ctx)) { - ntfs_attr_put_search_ctx(ctx); - ntfs_log_error("BUG: $FILE_NAME attribute not found." - "\n"); - return FALSE; - } - a = ctx->attr; - err = insert_file_link_in_dir_index(g_index_block, root_ref, - (FILE_NAME_ATTR*)((char*)a + - le16_to_cpu(a->u.res.value_offset)), - le32_to_cpu(a->u.res.value_length)); - ntfs_attr_put_search_ctx(ctx); - } - if (err) { - ntfs_log_error("Couldn't create root directory: %s\n", - strerror(-err)); - return FALSE; - } - /* Add all other attributes, on a per-file basis for clarity. */ - ntfs_log_verbose("Creating $MFT (mft record 0)\n"); - m = (MFT_RECORD*)g_buf; - err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_mft, g_buf, - g_mft_size); - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_MFT, 1), g_mft_size, - g_mft_size, FILE_ATTR_HIDDEN | - FILE_ATTR_SYSTEM, 0, 0, "$MFT", - FILE_NAME_WIN32_AND_DOS); - /* mft_bitmap is not modified in mkntfs; no need to sync it later. */ - if (!err) - err = add_attr_bitmap_positioned(m, NULL, 0, 0, g_rl_mft_bmp, - g_mft_bitmap, g_mft_bitmap_byte_size); - if (err < 0) { - ntfs_log_error("Couldn't create $MFT: %s\n", strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $MFTMirr (mft record 1)\n"); - m = (MFT_RECORD*)(g_buf + 1 * g_vol->mft_record_size); - err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_mftmirr, g_buf, - g_rl_mftmirr[0].length * g_vol->cluster_size); - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_MFTMirr, FILE_MFTMirr), - g_rl_mftmirr[0].length * g_vol->cluster_size, - g_rl_mftmirr[0].length * g_vol->cluster_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$MFTMirr", FILE_NAME_WIN32_AND_DOS); - if (err < 0) { - ntfs_log_error("Couldn't create $MFTMirr: %s\n", - strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $LogFile (mft record 2)\n"); - m = (MFT_RECORD*)(g_buf + 2 * g_vol->mft_record_size); - buf_log = ntfs_malloc(g_logfile_size); - if (!buf_log) - return FALSE; - memset(buf_log, -1, g_logfile_size); - err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_logfile, buf_log, - g_logfile_size); - free(buf_log); - buf_log = NULL; - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_LogFile, FILE_LogFile), - g_logfile_size, g_logfile_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$LogFile", FILE_NAME_WIN32_AND_DOS); - if (err < 0) { - ntfs_log_error("Couldn't create $LogFile: %s\n", - strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $AttrDef (mft record 4)\n"); - m = (MFT_RECORD*)(g_buf + 4 * g_vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, (u8*)g_vol->attrdef, - g_vol->attrdef_len); - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_AttrDef, FILE_AttrDef), - (g_vol->attrdef_len + g_vol->cluster_size - 1) & - ~(g_vol->cluster_size - 1), g_vol->attrdef_len, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$AttrDef", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_AttrDef, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) { - ntfs_log_error("Couldn't create $AttrDef: %s\n", - strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $Bitmap (mft record 6)\n"); - m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); - /* the data attribute of $Bitmap must be non-resident or otherwise */ - /* windows 2003 will regard the volume as corrupt (ERSO) */ - if (!err) - err = insert_non_resident_attr_in_mft_record(m, - AT_DATA, NULL, 0, 0, 0, - g_lcn_bitmap, g_lcn_bitmap_byte_size); - - - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_Bitmap, FILE_Bitmap), - (g_lcn_bitmap_byte_size + g_vol->cluster_size - - 1) & ~(g_vol->cluster_size - 1), - g_lcn_bitmap_byte_size, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$Bitmap", FILE_NAME_WIN32_AND_DOS); - if (err < 0) { - ntfs_log_error("Couldn't create $Bitmap: %s\n", strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $Boot (mft record 7)\n"); - m = (MFT_RECORD*)(g_buf + 7 * g_vol->mft_record_size); - bs = ntfs_calloc(8192); - if (!bs) - return FALSE; - memcpy(bs, boot_array, sizeof(boot_array)); - /* - * Create the boot sector in bs. Note, that bs is already zeroed - * in the boot sector section and that it has the NTFS OEM id/magic - * already inserted, so no need to worry about these things. - */ - bs->bpb.bytes_per_sector = cpu_to_le16(opts.sector_size); - bs->bpb.sectors_per_cluster = (u8)(g_vol->cluster_size / - opts.sector_size); - bs->bpb.media_type = 0xf8; /* hard disk */ - bs->bpb.sectors_per_track = cpu_to_le16(opts.sectors_per_track); - ntfs_log_debug("sectors per track = %ld (0x%lx)\n", - opts.sectors_per_track, opts.sectors_per_track); - bs->bpb.heads = cpu_to_le16(opts.heads); - ntfs_log_debug("heads = %ld (0x%lx)\n", opts.heads, opts.heads); - bs->bpb.hidden_sectors = cpu_to_le32(opts.part_start_sect); - ntfs_log_debug("hidden sectors = %llu (0x%llx)\n", opts.part_start_sect, - opts.part_start_sect); - bs->physical_drive = 0x80; /* boot from hard disk */ - bs->extended_boot_signature = 0x80; /* everybody sets this, so we do */ - bs->number_of_sectors = cpu_to_sle64(opts.num_sectors); - bs->mft_lcn = cpu_to_sle64(g_mft_lcn); - bs->mftmirr_lcn = cpu_to_sle64(g_mftmirr_lcn); - if (g_vol->mft_record_size >= g_vol->cluster_size) { - bs->clusters_per_mft_record = g_vol->mft_record_size / - g_vol->cluster_size; - } else { - bs->clusters_per_mft_record = -(ffs(g_vol->mft_record_size) - - 1); - if ((u32)(1 << -bs->clusters_per_mft_record) != - g_vol->mft_record_size) { - free(bs); - ntfs_log_error("BUG: calculated clusters_per_mft_record" - " is wrong (= 0x%x)\n", - bs->clusters_per_mft_record); - return FALSE; - } - } - ntfs_log_debug("clusters per mft record = %i (0x%x)\n", - bs->clusters_per_mft_record, - bs->clusters_per_mft_record); - if (g_vol->indx_record_size >= g_vol->cluster_size) { - bs->clusters_per_index_record = g_vol->indx_record_size / - g_vol->cluster_size; - } else { - bs->clusters_per_index_record = -g_vol->indx_record_size_bits; - if ((1 << -bs->clusters_per_index_record) != - (s32)g_vol->indx_record_size) { - free(bs); - ntfs_log_error("BUG: calculated " - "clusters_per_index_record is wrong " - "(= 0x%x)\n", - bs->clusters_per_index_record); - return FALSE; - } - } - ntfs_log_debug("clusters per index block = %i (0x%x)\n", - bs->clusters_per_index_record, - bs->clusters_per_index_record); - /* Generate a 64-bit random number for the serial number. */ - bs->volume_serial_number = cpu_to_le64(((u64)random() << 32) | - ((u64)random() & 0xffffffff)); - /* - * Leave zero for now as NT4 leaves it zero, too. If want it later, see - * ../libntfs/bootsect.c for how to calculate it. - */ - bs->checksum = cpu_to_le32(0); - /* Make sure the bootsector is ok. */ - if (!ntfs_boot_sector_is_ntfs(bs, TRUE)) { - free(bs); - ntfs_log_error("FATAL: Generated boot sector is invalid!\n"); - return FALSE; - } - err = add_attr_data_positioned(m, NULL, 0, 0, 0, g_rl_boot, (u8*)bs, - 8192); - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_Boot, FILE_Boot), - (8192 + g_vol->cluster_size - 1) & - ~(g_vol->cluster_size - 1), 8192, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$Boot", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Boot, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) { - free(bs); - ntfs_log_error("Couldn't create $Boot: %s\n", strerror(-err)); - return FALSE; - } - if (create_backup_boot_sector((u8*)bs)) { - /* - * Pre-2.6 kernels couldn't access the last sector if it was - * odd and we failed to set the device block size to the sector - * size, hence we schedule chkdsk to create it. - */ - volume_flags |= VOLUME_IS_DIRTY; - } - free(bs); -#ifdef ENABLE_UUID - /* - * We cheat a little here and if the user has requested all times to be - * set to zero then we set the GUID to zero as well. This options is - * only used for development purposes so that should be fine. - */ - if (!opts.use_epoch_time) { - /* Generate a GUID for the volume. */ - uuid_generate((void*)&g_vol->guid); - } else - memset(&g_vol->guid, 0, sizeof(g_vol->guid)); -#endif - if (!create_file_volume(m, root_ref, volume_flags, &g_vol->guid)) - return FALSE; - ntfs_log_verbose("Creating $BadClus (mft record 8)\n"); - m = (MFT_RECORD*)(g_buf + 8 * g_vol->mft_record_size); - /* FIXME: This should be IGNORE_CASE */ - /* Create a sparse named stream of size equal to the volume size. */ - err = add_attr_data_positioned(m, "$Bad", 4, 0, 0, g_rl_bad, NULL, - g_vol->nr_clusters * g_vol->cluster_size); - if (!err) { - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - } - if (!err) { - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_BadClus, FILE_BadClus), - 0LL, 0LL, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, - 0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS); - } - if (err < 0) { - ntfs_log_error("Couldn't create $BadClus: %s\n", - strerror(-err)); - return FALSE; - } - /* create $Secure (NTFS 3.0+) */ - ntfs_log_verbose("Creating $Secure (mft record 9)\n"); - m = (MFT_RECORD*)(g_buf + 9 * g_vol->mft_record_size); - m->flags |= MFT_RECORD_IS_VIEW_INDEX; - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(9, 9), 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | - FILE_ATTR_VIEW_INDEX_PRESENT, 0, 0, - "$Secure", FILE_NAME_WIN32_AND_DOS); - buf_sds = NULL; - buf_sds_first_size = 0; - if (!err) { - int buf_sds_size; - - buf_sds_first_size = 0xfc; - buf_sds_size = 0x40000 + buf_sds_first_size; - buf_sds = ntfs_calloc(buf_sds_size); - if (!buf_sds) - return FALSE; - init_secure_sds(buf_sds); - memcpy(buf_sds + 0x40000, buf_sds, buf_sds_first_size); - err = add_attr_data(m, "$SDS", 4, 0, 0, (u8*)buf_sds, - buf_sds_size); - } - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$SDH", 4, 0, AT_UNUSED, - COLLATION_NTOFS_SECURITY_HASH, - g_vol->indx_record_size); - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$SII", 4, 0, AT_UNUSED, - COLLATION_NTOFS_ULONG, g_vol->indx_record_size); - if (!err) - err = initialize_secure(buf_sds, buf_sds_first_size, m); - free(buf_sds); - if (err < 0) { - ntfs_log_error("Couldn't create $Secure: %s\n", - strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $UpCase (mft record 0xa)\n"); - m = (MFT_RECORD*)(g_buf + 0xa * g_vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, (u8*)g_vol->upcase, - g_vol->upcase_len << 1); - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(FILE_UpCase, FILE_UpCase), - ((g_vol->upcase_len << 1) + - g_vol->cluster_size - 1) & - ~(g_vol->cluster_size - 1), - g_vol->upcase_len << 1, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, - "$UpCase", FILE_NAME_WIN32_AND_DOS); - if (err < 0) { - ntfs_log_error("Couldn't create $UpCase: %s\n", strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $Extend (mft record 11)\n"); - /* - * $Extend index must be resident. Otherwise, w2k3 will regard the - * volume as corrupt. (ERSO) - */ - m = (MFT_RECORD*)(g_buf + 11 * g_vol->mft_record_size); - m->flags |= MFT_RECORD_IS_DIRECTORY; - if (!err) - err = create_hardlink(g_index_block, root_ref, m, - MK_LE_MREF(11, 11), 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | - FILE_ATTR_I30_INDEX_PRESENT, 0, 0, - "$Extend", FILE_NAME_WIN32_AND_DOS); - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$I30", 4, 0, AT_FILE_NAME, - COLLATION_FILE_NAME, g_vol->indx_record_size); - if (err < 0) { - ntfs_log_error("Couldn't create $Extend: %s\n", - strerror(-err)); - return FALSE; - } - /* NTFS reserved system files (mft records 0xc-0xf) */ - for (i = 0xc; i < 0x10; i++) { - ntfs_log_verbose("Creating system file (mft record 0x%x)\n", i); - m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - if (!err) { - init_system_file_sd(i, &sd, &j); - err = add_attr_sd(m, sd, j); - } - if (err < 0) { - ntfs_log_error("Couldn't create system file %i (0x%x): " - "%s\n", i, i, strerror(-err)); - return FALSE; - } - } - /* create systemfiles for ntfs volumes (3.1) */ - /* starting with file 24 (ignoring file 16-23) */ - extend_flags = FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM | - FILE_ATTR_ARCHIVE | FILE_ATTR_VIEW_INDEX_PRESENT; - ntfs_log_verbose("Creating $Quota (mft record 24)\n"); - m = (MFT_RECORD*)(g_buf + 24 * g_vol->mft_record_size); - m->flags |= MFT_RECORD_IS_4; - m->flags |= MFT_RECORD_IS_VIEW_INDEX; - if (!err) - err = create_hardlink_res((MFT_RECORD*)(g_buf + - 11 * g_vol->mft_record_size), extend_ref, m, - MK_LE_MREF(24, 1), 0LL, 0LL, extend_flags, - 0, 0, "$Quota", FILE_NAME_WIN32_AND_DOS); - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$Q", 2, 0, AT_UNUSED, - COLLATION_NTOFS_ULONG, g_vol->indx_record_size); - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$O", 2, 0, AT_UNUSED, - COLLATION_NTOFS_SID, g_vol->indx_record_size); - if (!err) - err = initialize_quota(m); - if (err < 0) { - ntfs_log_error("Couldn't create $Quota: %s\n", strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $ObjId (mft record 25)\n"); - m = (MFT_RECORD*)(g_buf + 25 * g_vol->mft_record_size); - m->flags |= MFT_RECORD_IS_4; - m->flags |= MFT_RECORD_IS_VIEW_INDEX; - if (!err) - err = create_hardlink_res((MFT_RECORD*)(g_buf + - 11 * g_vol->mft_record_size), extend_ref, - m, MK_LE_MREF(25, 1), 0LL, 0LL, - extend_flags, 0, 0, "$ObjId", - FILE_NAME_WIN32_AND_DOS); - - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$O", 2, 0, AT_UNUSED, - COLLATION_NTOFS_ULONGS, - g_vol->indx_record_size); -#ifdef ENABLE_UUID - if (!err) - err = index_obj_id_insert(m, &g_vol->guid, - MK_LE_MREF(FILE_Volume, FILE_Volume)); -#endif - if (err < 0) { - ntfs_log_error("Couldn't create $ObjId: %s\n", - strerror(-err)); - return FALSE; - } - ntfs_log_verbose("Creating $Reparse (mft record 26)\n"); - m = (MFT_RECORD*)(g_buf + 26 * g_vol->mft_record_size); - m->flags |= MFT_RECORD_IS_4; - m->flags |= MFT_RECORD_IS_VIEW_INDEX; - if (!err) - err = create_hardlink_res((MFT_RECORD*)(g_buf + - 11 * g_vol->mft_record_size), - extend_ref, m, MK_LE_MREF(26, 1), - 0LL, 0LL, extend_flags, 0, 0, - "$Reparse", FILE_NAME_WIN32_AND_DOS); - /* FIXME: This should be IGNORE_CASE */ - if (!err) - err = add_attr_index_root(m, "$R", 2, 0, AT_UNUSED, - COLLATION_NTOFS_ULONGS, g_vol->indx_record_size); - if (err < 0) { - ntfs_log_error("Couldn't create $Reparse: %s\n", - strerror(-err)); - return FALSE; - } - return TRUE; -} - -/** - * mkntfs_redirect - */ -static int mkntfs_redirect(struct mkntfs_options *opts2) -{ - int result = 1; - ntfs_attr_search_ctx *ctx = NULL; - long long lw, pos; - ATTR_RECORD *a; - MFT_RECORD *m; - int i, err; - - if (!opts2) { - ntfs_log_error("Internal error: invalid parameters to mkntfs_options.\n"); - goto done; - } - /* Initialize the random number generator with the current time. */ - srandom(mkntfs_time()); - /* Allocate and initialize ntfs_volume structure g_vol. */ - g_vol = ntfs_volume_alloc(); - if (!g_vol) { - ntfs_log_perror("Could not create volume"); - goto done; - } - /* Create NTFS 3.1 (Windows XP/Vista) volumes. */ - g_vol->major_ver = 3; - g_vol->minor_ver = 1; - /* Transfer some options to the volume. */ - if (opts.label) { - g_vol->vol_name = strdup(opts.label); - if (!g_vol->vol_name) { - ntfs_log_perror("Could not copy volume name"); - goto done; - } - } - if (opts.cluster_size >= 0) - g_vol->cluster_size = opts.cluster_size; - /* Length is in unicode characters. */ - g_vol->upcase_len = 65536; - g_vol->upcase = ntfs_malloc(g_vol->upcase_len * sizeof(ntfschar)); - if (!g_vol->upcase) - goto done; - ntfs_upcase_table_build(g_vol->upcase, - g_vol->upcase_len * sizeof(ntfschar)); - g_vol->attrdef = ntfs_malloc(sizeof(attrdef_ntfs3x_array)); - if (!g_vol->attrdef) { - ntfs_log_perror("Could not create attrdef structure"); - goto done; - } - memcpy(g_vol->attrdef, attrdef_ntfs3x_array, - sizeof(attrdef_ntfs3x_array)); - g_vol->attrdef_len = sizeof(attrdef_ntfs3x_array); - /* Open the partition. */ - if (!mkntfs_open_partition(g_vol)) - goto done; - /* - * Decide on the sector size, cluster size, mft record and index record - * sizes as well as the number of sectors/tracks/heads/size, etc. - */ - if (!mkntfs_override_vol_params(g_vol)) - goto done; - /* Initialize $Bitmap and $MFT/$BITMAP related stuff. */ - if (!mkntfs_initialize_bitmaps()) - goto done; - /* Initialize MFT & set g_logfile_lcn. */ - if (!mkntfs_initialize_rl_mft()) - goto done; - /* Initialize $LogFile. */ - if (!mkntfs_initialize_rl_logfile()) - goto done; - /* Initialize $Boot. */ - if (!mkntfs_initialize_rl_boot()) - goto done; - /* Allocate a buffer large enough to hold the mft. */ - g_buf = ntfs_calloc(g_mft_size); - if (!g_buf) - goto done; - /* Create runlist for $BadClus, $DATA named stream $Bad. */ - if (!mkntfs_initialize_rl_bad()) - goto done; - /* If not quick format, fill the device with 0s. */ - if (!opts.quick_format) { - if (!mkntfs_fill_device_with_zeroes()) - goto done; - } - /* Create NTFS volume structures. */ - if (!mkntfs_create_root_structures()) - goto done; - /* - * - Do not step onto bad blocks!!! - * - If any bad blocks were specified or found, modify $BadClus, - * allocating the bad clusters in $Bitmap. - * - C&w bootsector backup bootsector (backup in last sector of the - * partition). - * - If NTFS 3.0+, c&w $Secure file and $Extend directory with the - * corresponding special files in it, i.e. $ObjId, $Quota, $Reparse, - * and $UsnJrnl. And others? Or not all necessary? - * - RE: Populate $root with the system files (and $Extend directory if - * applicable). Possibly should move this as far to the top as - * possible and update during each subsequent c&w of each system file. - */ - ntfs_log_verbose("Syncing root directory index record.\n"); - if (!mkntfs_sync_index_record(g_index_block, (MFT_RECORD*)(g_buf + 5 * - g_vol->mft_record_size), NTFS_INDEX_I30, 4)) - goto done; - - ntfs_log_verbose("Syncing $Bitmap.\n"); - m = (MFT_RECORD*)(g_buf + 6 * g_vol->mft_record_size); - - ctx = ntfs_attr_get_search_ctx(NULL, m); - if (!ctx) { - ntfs_log_perror("Could not create an attribute search context"); - goto done; - } - - if (mkntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { - ntfs_log_error("BUG: $DATA attribute not found.\n"); - goto done; - } - - a = ctx->attr; - if (a->non_resident) { - runlist *rl = ntfs_mapping_pairs_decompress(g_vol, a, NULL); - if (!rl) { - ntfs_log_error("ntfs_mapping_pairs_decompress() failed\n"); - goto done; - } - lw = ntfs_rlwrite(g_vol->u.dev, rl, g_lcn_bitmap, g_lcn_bitmap_byte_size, NULL); - err = errno; - free(rl); - if (lw != g_lcn_bitmap_byte_size) { - ntfs_log_error("ntfs_rlwrite: %s\n", lw == -1 ? - strerror(err) : "unknown error"); - goto done; - } - } else { - memcpy((char*)a + le16_to_cpu(a->u.res.value_offset), g_lcn_bitmap, le32_to_cpu(a->u.res.value_length)); - } - - /* - * No need to sync $MFT/$BITMAP as that has never been modified since - * its creation. - */ - ntfs_log_verbose("Syncing $MFT.\n"); - pos = g_mft_lcn * g_vol->cluster_size; - lw = 1; - for (i = 0; i < g_mft_size / (s32)g_vol->mft_record_size; i++) { - if (!opts.no_action) - lw = ntfs_mst_pwrite(g_vol->u.dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); - if (lw != 1) { - ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? - strerror(errno) : "unknown error"); - goto done; - } - pos += g_vol->mft_record_size; - } - ntfs_log_verbose("Updating $MFTMirr.\n"); - pos = g_mftmirr_lcn * g_vol->cluster_size; - lw = 1; - for (i = 0; i < g_rl_mftmirr[0].length * g_vol->cluster_size / g_vol->mft_record_size; i++) { - m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); - /* - * Decrement the usn by one, so it becomes the same as the one - * in $MFT once it is mst protected. - This is as we need the - * $MFTMirr to have the exact same byte by byte content as - * $MFT, rather than just equivalent meaning content. - */ - if (ntfs_mft_usn_dec(m)) { - ntfs_log_error("ntfs_mft_usn_dec"); - goto done; - } - if (!opts.no_action) - lw = ntfs_mst_pwrite(g_vol->u.dev, pos, 1, g_vol->mft_record_size, g_buf + i * g_vol->mft_record_size); - if (lw != 1) { - ntfs_log_error("ntfs_mst_pwrite: %s\n", lw == -1 ? - strerror(errno) : "unknown error"); - goto done; - } - pos += g_vol->mft_record_size; - } - ntfs_log_verbose("Syncing device.\n"); - if (g_vol->u.dev->d_ops->sync(g_vol->u.dev)) { - ntfs_log_error("Syncing device. FAILED"); - goto done; - } - ntfs_log_quiet("mkntfs completed successfully. Have a nice day.\n"); - result = 0; -done: - ntfs_attr_put_search_ctx(ctx); - mkntfs_cleanup(); /* Device is unlocked and closed here */ - return result; -} - - -/** - * main - Begin here - * - * Start from here. - * - * Return: 0 Success, the program worked - * 1 Error, something went wrong - */ -int main(int argc, char *argv[]) -{ - int result = 1; - - ntfs_log_set_handler(ntfs_log_handler_outerr); - utils_set_locale(); - - mkntfs_init_options(&opts); /* Set up the options */ - - if (!mkntfs_parse_options(argc, argv, &opts)) /* Read the command line options */ - goto done; - - result = mkntfs_redirect(&opts); -done: - return result; -} |