/**
* 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.
*
* 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
#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
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
extern char *optarg;
extern int optind;
#endif
#ifdef HAVE_LINUX_MAJOR_H
# ifndef MAJOR
# endif
# ifndef IDE_DISK_MAJOR
# ifndef IDE0_MAJOR
# 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
# 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"
#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. */
/**
* global variables
*/
static int g_mft_bitmap_byte_size = 0;
static int g_lcn_bitmap_byte_size = 0;
static int g_mft_size = 0;
static long long g_mft_zone_end = 0; /* Determined from volume_size and mft_zone_multiplier, in clusters */
/**
* struct mkntfs_options
*/
static struct mkntfs_options {
} opts;
/**
* mkntfs_license
*/
static void mkntfs_license(void)
{
}
/**
* 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"
}
/**
* mkntfs_version
*/
static void mkntfs_version(void)
{
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");
}
/**
* mkntfs_parse_long
*/
{
long tmp;
return FALSE;
if (*num >= 0) {
return FALSE;
}
return FALSE;
} else {
return TRUE;
}
}
/**
* mkntfs_parse_llong
*/
long long *num)
{
long long tmp;
return FALSE;
if (*num >= 0) {
return FALSE;
}
string);
return FALSE;
} else {
return TRUE;
}
}
/**
* mkntfs_init_options
*/
{
if (!opts2)
return;
/* Mark all the numeric options as "unset". */
}
/**
* mkntfs_parse_options
*/
{
};
int c = -1;
int lic = 0;
int err = 0;
int ver = 0;
ntfs_log_error("Internal error: invalid parameters to "
"mkntfs_options.\n");
return FALSE;
}
opterr = 0; /* We'll handle the errors, thank you. */
switch (c) {
case 1: /* A device, or a number of sectors */
else if (!mkntfs_parse_llong(optarg,
"number of sectors",
&opts2->num_sectors))
err++;
break;
case 'C':
break;
case 'c':
&opts2->cluster_size))
err++;
break;
case 'F':
break;
case 'f': /* fast */
case 'Q': /* quick */
break;
case 'H':
err++;
break;
case 'h':
err++; /* display help */
break;
case 'I':
break;
case 'L':
} else {
ntfs_log_error("You may only specify the label "
"once.\n");
err++;
}
break;
case 'l':
lic++; /* display the license */
break;
case 'n':
break;
case 'p':
&opts2->part_start_sect))
err++;
break;
case 'q':
break;
case 's':
&opts2->sector_size))
err++;
break;
case 'S':
err++;
break;
case 'T':
break;
case 'v':
break;
case 'V':
ver++; /* display version info */
break;
case 'Z': /* debug - turn on everything */
break;
case 'z':
err++;
break;
default:
break;
(!optarg)) {
ntfs_log_error("Option '%s' requires an "
} else if (optopt != '?') {
ntfs_log_error("Unknown option '%s'.\n",
}
err++;
break;
}
}
if (argc > 1)
ntfs_log_error("You must specify a device.\n");
err++;
}
}
if (ver)
if (lic)
if (err)
mkntfs_usage();
}
/**
* mkntfs_time
*/
{
if (!opts.use_epoch_time)
return 0;
}
/**
* append_to_bad_blocks
*/
{
long long *new_buf;
if (!(g_num_bad_blocks & 15)) {
sizeof(long long));
if (!new_buf) {
ntfs_log_perror("Reallocating memory for bad blocks "
"list failed");
return FALSE;
}
}
return TRUE;
}
/**
* mkntfs_write
*/
const void *b, long long count)
{
int retry;
return count;
retry = 0;
do {
if (bytes_written == -1LL) {
return bytes_written;
} else if (!bytes_written) {
retry++;
} else {
count -= bytes_written;
total += bytes_written;
}
if (count)
ntfs_log_error("Failed to complete writing to %s after three retries."
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.
*/
{
int retry, i;
if (inited_size)
*inited_size = 0LL;
return val_len;
/* Don't write sparse runs. */
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.
*/
}
return -1LL;
retry = 0;
do {
length);
if (bytes_written == -1LL) {
ntfs_log_perror("Error writing to %s",
return bytes_written;
}
if (bytes_written) {
length -= bytes_written;
total += bytes_written;
if (inited_size)
*inited_size += bytes_written;
} else {
retry++;
}
if (length) {
ntfs_log_error("Failed to complete writing to %s after three "
return total;
}
}
if (delta) {
int eo;
char *b = ntfs_calloc(delta);
if (!b)
return -1;
free(b);
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.
*/
{
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.
*/
ntfs_log_error("make_room_for_attribute() received non 8-byte aligned "
"size.\n");
return -EINVAL;
}
if (!m || !pos)
return -EINVAL;
return -EINVAL;
/* The -8 is for the attribute terminator. */
return -EINVAL;
#endif
/* Do we have enough space? */
return -ENOSPC;
/* Move everything after pos to pos + size. */
/* Update mft record. */
return 0;
}
/**
* deallocate_scattered_clusters
*/
{
LCN j;
int i;
if (!rl)
return;
/* Iterate over all runs in the runlist @rl. */
/* Skip sparse runs. */
continue;
/* Deallocate the current run. */
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.
*/
{
int rlpos = 0;
int rlsize = 0;
char bit;
/* Loop until all clusters are allocated. */
while (clusters) {
/* Loop in current zone until we run out of free clusters. */
if (bit)
continue;
/*
* Reallocate memory if necessary. Make sure we have
* enough for the terminator entry as well.
*/
if (!rlt)
goto err_end;
}
/* Coalesce with previous run if adjacent LCNs. */
vcn++;
} else {
prev_run_len = 1LL;
rlpos++;
}
/* Done? */
if (!--clusters) {
/* Add terminator element and return. */
return rl;
}
}
/* Switch to next zone, decreasing mft zone by factor 2. */
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;
if (rl) {
/* Add terminator element. */
/* Deallocate all allocated clusters. */
/* Free the runlist. */
}
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!
*/
{
ATTR_RECORD *a;
/*
* Iterate over attributes in mft record starting at @ctx->attr, or the
* attribute following that, if @ctx->is_first is TRUE.
*/
} else {
}
break;
le32_to_cpu(type))) ||
return -1;
}
if (!a->length)
break;
/* If this is an enumeration return this attribute. */
return 0;
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) {
return -1;
}
int rc;
(ntfschar*)((char*)a +
le16_to_cpu(a->name_offset)),
upcase, upcase_len);
/*
* If @name collates before a->name, there is no
* matching attribute.
*/
if (rc == -1) {
return -1;
}
/* If the strings are not equal, continue search. */
if (rc)
continue;
(ntfschar*)((char*)a +
le16_to_cpu(a->name_offset)),
upcase, upcase_len);
if (rc == -1) {
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;
/*
* If @val collates before the current attribute's
* value, there is no matching attribute.
*/
if (!rc) {
return 0;
return -1;
}
} else if (rc < 0) {
return -1;
}
}
}
ntfs_log_trace("File is corrupt. Run chkdsk.\n");
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.
*
* 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.
*/
{
return -1;
}
if (ctx->base_ntfs_ino)
else
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.
*/
{
ATTR_RECORD *a;
int uname_len = 0;
/*
if (base record)
attr_lookup();
else
*/
if (!uname)
return -errno;
/* Check if the attribute is already there. */
if (!ctx) {
ntfs_log_error("Failed to allocate attribute search context.\n");
goto err_out;
}
if (ic == IGNORE_CASE) {
ntfs_log_error("FIXME: Hit unimplemented code path #1.\n");
err = -EOPNOTSUPP;
goto err_out;
}
goto err_out;
}
ntfs_log_error("Corrupt inode.\n");
goto err_out;
}
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;
}
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) {
if (mpa_size < 0) {
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. */
/* Get the highest vcn. */
/* Does the value fit inside the allocated size? */
ntfs_log_error("BUG: Allocated size is smaller than data size!\n");
goto err_out;
}
/*
* 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
ntfs_log_error("BUG(): in insert_positioned_attribute_in_mft_"
"record(): make_room_for_attribute() returned "
"error: EINVAL!\n");
goto err_out;
#endif
}
a->non_resident = 1;
a->name_length = name_len;
a->instance = m->next_attr_instance;
+ 1) & 0xffff);
a->u.nonres.lowest_vcn = 0;
/* FIXME: Allocated size depends on compression. */
if (name_len)
if (flags & ATTR_COMPRESSION_MASK) {
ntfs_log_error("Unknown compression format. Reverting "
"to standard compression.\n");
a->flags &= ~ATTR_COMPRESSION_MASK;
a->flags |= ATTR_IS_COMPRESSED;
}
/* 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;
ntfs_log_error("Error writing non-resident attribute "
"value.\n");
return -errno;
}
}
/* FIXME: Handle error. */
/* deallocate clusters */
/* remove attribute */
if (err >= 0)
ntfs_log_error("insert_positioned_attr_in_mft_record failed "
}
if (ctx)
return err;
}
/**
* insert_non_resident_attr_in_mft_record
*
* Return 0 on success and -errno on error.
*/
{
ATTR_RECORD *a;
int uname_len = 0;
/*
if (base record)
attr_lookup();
else
*/
if (!uname)
return -errno;
/* Check if the attribute is already there. */
if (!ctx) {
ntfs_log_error("Failed to allocate attribute search context.\n");
goto err_out;
}
if (ic == IGNORE_CASE) {
ntfs_log_error("FIXME: Hit unimplemented code path #2.\n");
err = -EOPNOTSUPP;
goto err_out;
}
goto err_out;
}
ntfs_log_error("Corrupt inode.\n");
goto err_out;
}
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;
}
ntfs_log_error("Encrypted/sparse attributes not supported.\n");
err = -EOPNOTSUPP;
goto err_out;
}
if (val_len) {
if (!rl) {
ntfs_log_perror("Failed to allocate scattered clusters");
goto err_out;
}
} else {
}
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) {
if (mpa_size < 0) {
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. */
/*
* 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
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->non_resident = 1;
a->name_length = name_len;
a->instance = m->next_attr_instance;
+ 1) & 0xffff);
a->u.nonres.lowest_vcn = 0;
;
/* FIXME: Allocated size depends on compression. */
if (name_len)
if (flags & ATTR_COMPRESSION_MASK) {
ntfs_log_error("Unknown compression format. Reverting "
"to standard compression.\n");
a->flags &= ~ATTR_COMPRESSION_MASK;
a->flags |= ATTR_IS_COMPRESSED;
}
/* 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;
ntfs_log_error("Error writing non-resident attribute "
"value.\n");
return -errno;
}
}
/* FIXME: Handle error. */
/* deallocate clusters */
/* remove attribute */
if (err >= 0)
ntfs_log_error("insert_non_resident_attr_in_mft_record failed with "
}
if (ctx)
return err;
}
/**
* insert_resident_attr_in_mft_record
*
* Return 0 on success and -errno on error.
*/
const RESIDENT_ATTR_FLAGS res_flags,
{
ATTR_RECORD *a;
int uname_len = 0;
/*
if (base record)
mkntfs_attr_lookup();
else
*/
if (!uname)
return -errno;
/* Check if the attribute is already there. */
if (!ctx) {
ntfs_log_error("Failed to allocate attribute search context.\n");
goto err_out;
}
if (ic == IGNORE_CASE) {
ntfs_log_error("FIXME: Hit unimplemented code path #3.\n");
err = -EOPNOTSUPP;
goto err_out;
}
ctx)) {
goto err_out;
}
ntfs_log_error("Corrupt inode.\n");
goto err_out;
}
/* sizeof(resident attribute record header) == 24 */
/*
* 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
ntfs_log_error("BUG(): in insert_resident_attribute_in_mft_"
"record(): make_room_for_attribute() returned "
"error: EINVAL!\n");
goto err_out;
}
#endif
a->non_resident = 0;
a->name_length = name_len;
if (type == AT_OBJECT_ID)
a->name_offset = const_cpu_to_le16(0);
else
a->instance = m->next_attr_instance;
+ 1) & 0xffff);
if (name_len)
if (val_len)
if (ctx)
return err;
}
/**
* add_attr_std_info
*
* Return 0 on success or -errno on error.
*/
{
sd_size = 48;
sd_size = 72;
/* FIXME: $Quota support... */
/* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */
/* NTFS 1.2: size of si = 48, NTFS 3.[01]: size of si = 72 */
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.
*/
{
int i, fn_size;
/* Check if the attribute is already there. */
if (!ctx) {
ntfs_log_error("Failed to get attribute search context.\n");
return -ENOMEM;
}
ntfs_log_error("BUG: Standard information attribute not "
"present in file record.\n");
return -eo;
}
fn_size = sizeof(FILE_NAME_ATTR) + i;
if (!fn) {
return -errno;
}
/* These are in a union so can't have both. */
if (packed_ea_size && reparse_point_tag) {
return -EINVAL;
}
if (packed_ea_size) {
} else {
}
if (i < 1) {
return -EINVAL;
}
if (i > 0xff) {
return -ENAMETOOLONG;
}
/* No terminating null in file names. */
fn->file_name_length = i;
if (i < 0)
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.
*/
{
int err;
oi = (OBJECT_ID_ATTR) {
};
if (err < 0)
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.
*/
{
int err;
/* Does it fit? NO: create non-resident. YES: create resident. */
sd_len);
else
sd_len);
if (err < 0)
return err;
}
/**
* add_attr_data
*
* Return 0 on success or -errno on error.
*/
{
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.
*/
else
if (err < 0)
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.
*/
{
int err;
if (err < 0)
ntfs_log_error("add_attr_data_positioned failed: %s\n",
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.
*/
{
int uname_len = 0;
int i;
if (vol_name) {
if (uname_len < 0)
return -errno;
if (uname_len > 0xff) {
return -ENAMETOOLONG;
}
}
if (i < 0)
return i;
}
/**
* add_attr_vol_info
*
* Return 0 on success or -errno on error.
*/
{
int err;
if (err < 0)
return err;
}
/**
* add_attr_index_root
*
* Return 0 on success or -errno on error.
*/
const ATTR_TYPES indexed_attr_type,
const COLLATION_RULES collation_rule,
const u32 index_block_size)
{
INDEX_ROOT *r;
r = ntfs_malloc(val_len);
if (!r)
return -errno;
if (indexed_attr_type == AT_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;
ntfs_log_error("add_attr_index_root: index block size is not "
"a multiple of the cluster size.\n");
free(r);
return -EINVAL;
}
} else { /* if (g_vol->cluster_size > index_block_size) */
ntfs_log_error("add_attr_index_root: index block size is not "
"a power of 2.\n");
free(r);
return -EINVAL;
}
ntfs_log_error("add_attr_index_root: index block size "
"is smaller than the sector size.\n");
free(r);
return -EINVAL;
}
}
sizeof(INDEX_ENTRY_HEADER));
/*
* 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->key_length = const_cpu_to_le16(0);
e->flags = INDEX_ENTRY_END;
e->reserved = const_cpu_to_le16(0);
free(r);
if (err < 0)
return err;
}
/**
* add_attr_index_alloc
*
* Return 0 on success or -errno on error.
*/
{
int err;
if (err < 0)
return err;
}
/**
* add_attr_bitmap
*
* Return 0 on success or -errno on error.
*/
const u32 bitmap_len)
{
int err;
/* Does it fit? NO: create non-resident. YES: create resident. */
else
if (err < 0)
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.
*/
{
int err;
if (err < 0)
ntfs_log_error("add_attr_bitmap_positioned failed: %s\n",
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.
*/
{
ATTR_RECORD *a;
INDEX_ROOT *r;
int uname_len = 0;
if (!uname)
return -errno;
/* Find the index root attribute. */
if (!ctx) {
ntfs_log_error("Failed to allocate attribute search context.\n");
return -ENOMEM;
}
if (ic == IGNORE_CASE) {
ntfs_log_error("FIXME: Hit unimplemented code path #4.\n");
err = -EOPNOTSUPP;
goto err_out;
}
ctx);
if (err) {
goto err_out;
}
if (a->non_resident || a->flags) {
goto err_out;
}
/* Bitmap has to be at least 8 bytes in size. */
if (err)
goto err_out;
if (!ia_val) {
goto err_out;
}
/* Setup header. */
if (index_block_size >= NTFS_BLOCK_SIZE) {
NTFS_BLOCK_SIZE + 1);
} else {
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. */
cpu_to_le16(1);
ia_val->index_block_vcn = 0;
/* Align to 8-byte boundary. */
(sizeof(INDEX_ALLOCATION) - sizeof(INDEX_HEADER)));
/* Find the last entry in the index root and save it in re. */
/* Next entry in index root. */
}
/* Copy all the entries including the termination entry. */
/* Finish setting up index allocation. */
/* Move the termination entry forward to the beginning if necessary. */
}
/* Now fixup empty index root with pointer to index allocation VCN 0. */
/* Resize index root attribute. */
if (ntfs_resident_attr_value_resize(m, a, sizeof(INDEX_ROOT) -
sizeof(INDEX_HEADER) +
/* TODO: Remove the added bitmap! */
/* Revert index root from index allocation. */
goto err_out;
}
/* Set VCN pointer to 0LL. */
if (err) {
ntfs_log_error("ntfs_mst_pre_write_fixup() failed in "
"upgrade_to_large_index.\n");
goto err_out;
}
if (err) {
/* TODO: Remove the added bitmap! */
/* Revert index root from index allocation. */
goto err_out;
}
return 0;
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.
*/
{
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.
*/
ntfs_log_error("make_room_for_index_entry_in_index_block() received "
"non 8-byte aligned size.\n");
return -EINVAL;
}
return -EINVAL;
sizeof(INDEX_HEADER) +
sizeof(INDEX_HEADER) +
return -EINVAL;
/* The - sizeof(INDEX_ENTRY_HEADER) is for the index terminator. */
- (int)sizeof(INDEX_ENTRY_HEADER))
return -EINVAL;
#endif
/* Do we have enough space? */
return -ENOSPC;
/* Move everything after pos to pos + size. */
/* Update index block. */
return 0;
}
/**
* ntfs_index_keys_compare
*
* not all types of COLLATION_RULES supported yet...
* added as needed.. (remove this comment when all are added)
*/
{
int i;
if (collation_rule == COLLATION_NTOFS_ULONG) {
/* i.e. $SII or $QUOTA-$Q */
return -1;
return 1;
/* u1 == u2 */
return 0;
}
if (collation_rule == COLLATION_NTOFS_ULONGS) {
/* i.e $OBJID-$O */
i = 0;
return -1;
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 */
return -1;
return 1;
/* u1 == u2 */
return -1;
return 1;
return 0;
}
if (collation_rule == COLLATION_NTOFS_SID) {
/* i.e. $QUOTA-O */
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
*/
{
ATTR_RECORD *a;
int err, i;
err = 0;
/* does it fit ?*/
return -ENOSPC;
/* find the INDEX_ROOT attribute:*/
if (!ctx) {
ntfs_log_error("Failed to allocate attribute search "
"context.\n");
goto err_out;
}
ctx)) {
goto err_out;
}
/* found attribute */
+ 0x10);
/*
* Loop until we exceed valid memory (corruption case) or until we
* reach the last entry.
*/
if (type == AT_FILE_NAME) {
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_NAME_POSIX)
return -EEXIST;
g_vol->upcase_len);
if (!i)
return -EEXIST;
if (i == -1)
break;
}
if (!i)
return -EEXIST;
if (i == -1)
break;
}
} else
return -EINVAL;
le32_to_cpu(m->bytes_in_use) -
/* Adjust various offsets, etc... */
if (ctx)
return err;
}
/**
* initialize_secure
*
* initializes $Secure's $SDH and $SII indexes from $SDS datastream
*/
{
sdh_size = sizeof(INDEX_ENTRY_HEADER);
sii_size = sizeof(INDEX_ENTRY_HEADER);
if (!idx_entry_sdh)
return -errno;
if (!idx_entry_sii) {
return -errno;
}
err = 0;
if (!sds_header->length)
break;
/* SDH index entry */
/* SII index entry */
break;
break;
}
return err;
}
/**
* initialize_quota
*
* initialize $Quota with the default quota index-entries.
*/
{
err = 0;
/* q index entry num 1 */
q1_size = 0x48;
if (!idx_entry_q1)
return errno;
if (err)
return err;
/* q index entry num 2 */
q2_size = 0x58;
if (!idx_entry_q2)
return errno;
for (i = 0; i < 5; i++)
if (err)
return err;
o_size = 0x28;
if (!idx_entry_o)
return errno;
for (i = 0; i < 5; i++)
/* 20 00 00 00 padding after here on ntfs 3.1. 3.0 is unchecked. */
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.
*/
{
int err, i;
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)
*/
/*
* Loop until we exceed valid memory (corruption case) or until we
* reach the last entry.
*/
#if 0
#ifdef DEBUG
ntfs_log_debug("file_name_attr1->file_name_length = %i\n",
if (file_name->file_name_length) {
if (i < 0)
ntfs_log_debug("Name contains non-displayable "
"Unicode characters.\n");
ntfs_log_debug("file_name_attr1->file_name = %s\n",
__buf);
}
ntfs_log_debug("file_name_attr2->file_name_length = %i\n",
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);
}
#endif
#endif
/*
* 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)
*/
return -EEXIST;
g_vol->upcase_len);
if (i == -1)
break;
/* Complete match. Bugger. Can't insert. */
if (!i)
return -EEXIST;
#ifdef DEBUG
/* Next entry. */
ntfs_log_debug("BUG: ie->length is zero, breaking out "
"of loop.\n");
break;
}
#endif
};
if (err) {
ntfs_log_error("make_room_for_index_entry_in_index_block "
return err;
}
/* Create entry in place and copy file name attribute value. */
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.
*/
{
/* Create the file_name attribute. */
fn_size = sizeof(FILE_NAME_ATTR) + i;
if (!fn)
return -errno;
/* FIXME: copy the creation_time from the std info */
/* These are in a union so can't have both. */
if (packed_ea_size && reparse_point_tag) {
return -EINVAL;
}
if (packed_ea_size) {
return -EINVAL;
}
if (packed_ea_size) {
} else {
}
if (i < 1) {
return -EINVAL;
}
if (i > 0xff) {
return -ENAMETOOLONG;
}
/* No terminating null in file names. */
fn->file_name_length = i;
/* Increment the link count of @m_file. */
if (i == 0xffff) {
ntfs_log_error("Too many hardlinks present already.\n");
return -EINVAL;
}
/* Add the file_name to @m_file. */
if (i < 0) {
ntfs_log_error("create_hardlink failed adding file name "
"attribute: %s\n", strerror(-i));
/* Undo link count increment. */
return i;
}
/* Insert the index entry for file_name in @idx. */
if (!idx_entry_new)
return -errno;
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. */
/* Undo link count increment. */
return i;
}
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.
*/
{
int i, fn_size;
/* Create the file_name attribute. */
fn_size = sizeof(FILE_NAME_ATTR) + i;
if (!fn)
return -errno;
/* FIXME: Is this correct? Or do we have to copy the creation_time */
/* from the std info? */
/* These are in a union so can't have both. */
if (packed_ea_size && reparse_point_tag) {
return -EINVAL;
}
if (packed_ea_size) {
} else {
}
if (i < 1) {
return -EINVAL;
}
if (i > 0xff) {
return -ENAMETOOLONG;
}
/* No terminating null in file names. */
fn->file_name_length = i;
/* Increment the link count of @m_file. */
if (i == 0xffff) {
ntfs_log_error("Too many hardlinks present already.\n");
return -EINVAL;
}
/* Add the file_name to @m_file. */
if (i < 0) {
ntfs_log_error("create_hardlink failed adding file name attribute: "
"%s\n", strerror(-i));
/* Undo link count increment. */
return i;
}
/* Insert the index entry for file_name in @idx. */
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. */
/* Undo link count increment. */
return i;
}
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.
*/
{
/*
* 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.
*/
if (!idx_entry_new)
return -errno;
if (err < 0) {
ntfs_log_error("index_obj_id_insert failed inserting index "
return err;
}
return 0;
}
#endif
/**
* mkntfs_cleanup
*/
static void mkntfs_cleanup(void)
{
/* Close the volume */
if (g_vol) {
}
}
/* Free any memory we've used */
}
/**
* mkntfs_open_partition -
*/
{
int i;
unsigned long mnt_flags;
/*
* Allocate and initialize an ntfs device structure and attach it to
* the volume.
*/
ntfs_log_perror("Could not create device");
goto done;
}
/* Open the device for reading or reading and writing. */
ntfs_log_quiet("Running in READ-ONLY mode!\n");
i = O_RDONLY;
} else {
i = O_RDWR;
}
ntfs_log_error("The device doesn't exist; did you specify it correctly?\n");
else
goto done;
}
/* Verify we are dealing with a block device. */
goto done;
}
ntfs_log_error("Refusing to make a filesystem here!\n");
goto done;
}
if (!opts.num_sectors) {
ntfs_log_error("You must specify the number of sectors.\n");
goto done;
}
if (opts.sector_size) {
else
} else {
else
}
}
ntfs_log_warning("mkntfs forced anyway.\n");
#ifdef HAVE_LINUX_MAJOR_H
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. */
} else if (mnt_flags & NTFS_MF_MOUNTED) {
ntfs_log_error("Refusing to make a filesystem here!\n");
goto done;
}
ntfs_log_warning("mkntfs forced anyway. Hope /etc/mtab is incorrect.\n");
}
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
if (page_size < 0)
#endif
#ifdef _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;
}
return page_size;
}
/**
* mkntfs_override_vol_params -
*/
{
long page_size;
int i;
/* If user didn't specify the sector size, determine it now. */
if (opts.sector_size < 0) {
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 "
}
}
/* Validate sector size. */
ntfs_log_error("The sector size is invalid. It must be a "
"power of two, e.g. 512, 1024.\n");
return FALSE;
}
ntfs_log_error("The sector size is invalid. The minimum size "
"is 256 bytes and the maximum is 4096 bytes.\n");
return FALSE;
}
/* Now set the device block size to the 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 "
/* If user didn't specify the number of sectors, determine it now. */
if (opts.num_sectors < 0) {
if (opts.num_sectors <= 0) {
ntfs_log_error("Couldn't determine the size of %s. "
"Please specify the number of sectors "
return FALSE;
}
}
/*
* 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;
opts.num_sectors -= i;
/* If user didn't specify the partition start sector, determine it. */
if (opts.part_start_sect < 0) {
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",
opts.part_start_sect = 0;
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",
opts.part_start_sect = 0;
}
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) {
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 "
opts.sectors_per_track = 0;
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 "
opts.sectors_per_track = 0;
}
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. */
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",
ntfs_log_warning("The number of heads was not "
"specified for %s and the automatically "
"determined value is too large. It has been "
}
ntfs_log_error("Invalid number of heads. Maximum is 65535.\n");
return FALSE;
}
/* Validate volume size. */
ntfs_log_error("Device is too small (%llikiB). Minimum NTFS "
return FALSE;
}
/* 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.
*/
/* For small volumes on devices with large sector sizes. */
/*
* 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.
*/
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 "
}
/* Validate cluster size. */
ntfs_log_error("The cluster size is invalid. It must be a "
"power of two, e.g. 1024, 4096.\n");
return FALSE;
}
ntfs_log_error("The cluster size is invalid. It must be equal "
"to, or larger than, the sector size.\n");
return FALSE;
}
ntfs_log_error("The cluster size is invalid. It cannot be "
"more that 128 times the size of the sector "
"size.\n");
return FALSE;
}
ntfs_log_error("The cluster size is invalid. The maximum "
"cluster size is 65536 bytes (64kiB).\n");
return FALSE;
}
ntfs_log_debug("cluster size = %u bytes\n",
(unsigned int)vol->cluster_size);
if (opts.enable_compression) {
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");
}
/*
* 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.
*/
vol->cluster_size) ||
vol->nr_clusters)) {
/* XXX is this code reachable? */
ntfs_log_error("Illegal combination of volume/cluster/sector "
return FALSE;
}
ntfs_log_debug("number of clusters = %llu (0x%llx)\n",
/* Number of clusters must fit within 32 bits (Win2k limitation). */
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;
}
/*
* 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
*/
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 "
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?
*/
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 "
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 -
*/
{
u64 i;
int mft_bitmap_size;
/* Determine lcn bitmap byte size and allocate it. */
/* Needs to be multiple of 8 bytes. */
ntfs_log_debug("g_lcn_bitmap_byte_size = %i, allocated = %llu\n",
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.
*/
/*
* Mft size is 27 (NTFS 3.0+) mft records or one cluster, whichever is
* bigger.
*/
g_mft_size = 27;
/* Determine mft bitmap size and allocate it. */
/* Convert to bytes, at least one. */
/* Mft bitmap is allocated in multiples of 8 bytes. */
ntfs_log_debug("mft_bitmap_size = %i, g_mft_bitmap_byte_size = %i\n",
if (!g_mft_bitmap)
return FALSE;
/* Create runlist for mft bitmap. */
if (!g_rl_mft_bmp)
return FALSE;
/* Mft bitmap is right after $Boot's data. */
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.
*/
/* Allocate cluster for mft bitmap. */
return TRUE;
}
/**
* mkntfs_initialize_rl_mft -
*/
{
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.
*/
}
/* Determine MFT zone size. */
case 4:
break;
case 3:
break;
case 2:
break;
case 1:
default:
break;
}
/*
* The mft zone begins with the mft data attribute, not at the beginning
* of the device.
*/
/* Create runlist for mft. */
if (!g_rl_mft)
return FALSE;
/* rounded up division by cluster size */
/* Allocate clusters for mft. */
for (i = 0; i < j; i++)
/* Determine mftmirr_lcn (middle of volume). */
/ g_vol->cluster_size;
ntfs_log_debug("$MFTMirr logical cluster number = 0x%llx\n",
/* Create runlist for mft mirror. */
if (!g_rl_mftmirr)
return FALSE;
/*
* 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).
*/
g_rl_mftmirr[0].length = j;
/* Allocate clusters for mft mirror. */
for (i = 0; i < j; i++)
g_logfile_lcn = g_mftmirr_lcn + j;
ntfs_log_debug("$LogFile logical cluster number = 0x%llx\n",
return TRUE;
}
/**
* mkntfs_initialize_rl_logfile -
*/
{
int i, j;
/* Create runlist for log file. */
if (!g_rl_logfile)
return FALSE;
/*
* Determine logfile_size from volume_size (rounded up to a cluster),
* making sure it does not overflow the end of the volume.
*/
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
*/
else
}
/*
* $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;
}
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.
*/
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[0].length = j;
/* Allocate clusters for log file. */
for (i = 0; i < j; i++)
return TRUE;
}
/**
* mkntfs_initialize_rl_boot -
*/
{
int i, j;
/* Create runlist for $Boot. */
if (!g_rl_boot)
return FALSE;
/*
* $Boot is always 8192 (0x2000) bytes or 1 cluster, whichever is
* bigger.
*/
/* Allocate clusters for $Boot. */
for (i = 0; i < j; i++)
return TRUE;
}
/**
* mkntfs_initialize_rl_bad -
*/
{
/* Create runlist for $BadClus, $DATA named stream $Bad. */
if (!g_rl_bad)
return FALSE;
/*
* $BadClus named stream $Bad contains the whole volume as a single
* sparse runlist entry.
*/
/* TODO: Mark bad blocks as such. */
return TRUE;
}
/**
* mkntfs_fill_device_with_zeroes -
*/
{
/*
* If not quick format, fill the device with 0s.
* FIXME: Except bad blocks! (AIA)
*/
int i;
unsigned long long position;
ntfs_log_progress("Initializing device with zeroes: 0%%");
position++) {
}
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. */
}
}
ntfs_log_progress("\b\b\b\b100%%");
for (i = 0; (unsigned long)i < position; i++) {
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. */
}
}
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....
*/
{
int i, err;
ATTR_RECORD *a;
long long lw;
i = 5 * sizeof(ntfschar);
if (!ctx) {
ntfs_log_perror("Failed to allocate attribute search context");
return FALSE;
}
/* FIXME: This should be IGNORE_CASE! */
ntfs_log_error("BUG: $INDEX_ALLOCATION attribute not found.\n");
return FALSE;
}
if (!rl_index) {
ntfs_log_error("Failed to decompress runlist of $INDEX_ALLOCATION "
"attribute.\n");
return FALSE;
}
ntfs_log_error("BUG: $INDEX_ALLOCATION attribute too short.\n");
return FALSE;
}
i = sizeof(INDEX_BLOCK) - sizeof(INDEX_HEADER) +
if (err) {
ntfs_log_error("ntfs_mst_pre_write_fixup() failed while "
"syncing index block.\n");
return FALSE;
}
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 -
*/
#ifndef ENABLE_UUID
#endif
)
{
int i, err;
ntfs_log_verbose("Creating $Volume (mft record 3)\n");
FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0,
"$Volume", FILE_NAME_WIN32_AND_DOS);
if (!err) {
}
if (!err)
if (!err)
if (!err) {
if (fl & VOLUME_IS_DIRTY)
ntfs_log_quiet("Setting the volume dirty so check "
"disk runs on next reboot into "
"Windows.\n");
}
#ifdef ENABLE_UUID
if (!err)
#endif
if (err < 0) {
ntfs_log_error("Couldn't create $Volume: %s\n",
return FALSE;
}
return TRUE;
}
/**
* create_backup_boot_sector
*
* Return 0 on success or -1 if it couldn't be created.
*/
{
const char *s;
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;
ntfs_log_perror("Seek failed");
goto bb_err;
}
if (size > 8192)
size = 8192;
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. */
ntfs_log_critical("Couldn't write backup boot sector: %s\n", s);
return -1;
}
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 -
*/
{
MFT_RECORD *m;
int i;
int j;
int err;
int nr_sysfiles;
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++) {
i * g_vol->mft_record_size))) {
ntfs_log_error("Failed to layout system mft records."
"\n");
return FALSE;
}
if (i == 0 || i > 23)
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.
*/
for (i = nr_sysfiles;
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++) {
if (i < 16 || i > 23) {
m->mft_record_number = cpu_to_le32(i);
m->flags |= MFT_RECORD_IN_USE;
}
if (i == FILE_root) {
if (opts.disable_indexing)
if (opts.enable_compression)
}
/* 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) {
cpu_to_le32(0x0100));
} else if (i == 9) {
cpu_to_le32(0x0101));
} else if (i == 11) {
cpu_to_le32(0x0101));
} else if (i == 24 || i == 25 || i == 26) {
cpu_to_le32(0x0101));
} else {
cpu_to_le32(0x00));
}
}
/* The root directory mft reference. */
ntfs_log_verbose("Creating root directory (mft record 5)\n");
m->flags |= MFT_RECORD_IS_DIRECTORY;
FILE_ATTR_I30_INDEX_PRESENT, 0, 0, ".",
if (!err) {
init_root_sd(&sd, &i);
}
/* FIXME: This should be IGNORE_CASE */
if (!err)
/* FIXME: This should be IGNORE_CASE */
if (!err)
if (!err) {
ATTR_RECORD *a;
if (!ctx) {
ntfs_log_perror("Failed to allocate attribute search "
"context");
return FALSE;
}
/* There is exactly one file name so this is ok. */
0, ctx)) {
ntfs_log_error("BUG: $FILE_NAME attribute not found."
"\n");
return FALSE;
}
(FILE_NAME_ATTR*)((char*)a +
}
if (err) {
ntfs_log_error("Couldn't create root directory: %s\n",
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;
if (!err)
FILE_ATTR_SYSTEM, 0, 0, "$MFT",
/* mft_bitmap is not modified in mkntfs; no need to sync it later. */
if (!err)
if (err < 0) {
return FALSE;
}
ntfs_log_verbose("Creating $MFTMirr (mft record 1)\n");
if (!err)
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",
return FALSE;
}
ntfs_log_verbose("Creating $LogFile (mft record 2)\n");
if (!buf_log)
return FALSE;
if (!err)
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",
return FALSE;
}
ntfs_log_verbose("Creating $AttrDef (mft record 4)\n");
g_vol->attrdef_len);
if (!err)
FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0,
"$AttrDef", FILE_NAME_WIN32_AND_DOS);
if (!err) {
}
if (err < 0) {
ntfs_log_error("Couldn't create $AttrDef: %s\n",
return FALSE;
}
ntfs_log_verbose("Creating $Bitmap (mft record 6)\n");
/* the data attribute of $Bitmap must be non-resident or otherwise */
/* windows 2003 will regard the volume as corrupt (ERSO) */
if (!err)
if (!err)
FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0,
"$Bitmap", FILE_NAME_WIN32_AND_DOS);
if (err < 0) {
return FALSE;
}
ntfs_log_verbose("Creating $Boot (mft record 7)\n");
if (!bs)
return FALSE;
/*
* Create the boot sector in bs. Note, that bs is already zeroed
* already inserted, so no need to worry about these things.
*/
ntfs_log_debug("sectors per track = %ld (0x%lx)\n",
} else {
1);
g_vol->mft_record_size) {
ntfs_log_error("BUG: calculated clusters_per_mft_record"
" is wrong (= 0x%x)\n",
return FALSE;
}
}
ntfs_log_debug("clusters per mft record = %i (0x%x)\n",
} else {
ntfs_log_error("BUG: calculated "
"clusters_per_index_record is wrong "
"(= 0x%x)\n",
return FALSE;
}
}
ntfs_log_debug("clusters per index block = %i (0x%x)\n",
/* Generate a 64-bit random number for the serial number. */
/*
* Leave zero for now as NT4 leaves it zero, too. If want it later, see
* ../libntfs/bootsect.c for how to calculate it.
*/
/* Make sure the bootsector is ok. */
ntfs_log_error("FATAL: Generated boot sector is invalid!\n");
return FALSE;
}
8192);
if (!err)
FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0,
"$Boot", FILE_NAME_WIN32_AND_DOS);
if (!err) {
}
if (err < 0) {
return FALSE;
}
/*
* 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.
*/
}
#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. */
} else
#endif
return FALSE;
ntfs_log_verbose("Creating $BadClus (mft record 8)\n");
/* FIXME: This should be IGNORE_CASE */
/* Create a sparse named stream of size equal to the volume size. */
if (!err) {
}
if (!err) {
0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS);
}
if (err < 0) {
ntfs_log_error("Couldn't create $BadClus: %s\n",
return FALSE;
}
/* create $Secure (NTFS 3.0+) */
ntfs_log_verbose("Creating $Secure (mft record 9)\n");
m->flags |= MFT_RECORD_IS_VIEW_INDEX;
if (!err)
FILE_ATTR_VIEW_INDEX_PRESENT, 0, 0,
"$Secure", FILE_NAME_WIN32_AND_DOS);
buf_sds_first_size = 0;
if (!err) {
int buf_sds_size;
buf_sds_first_size = 0xfc;
if (!buf_sds)
return FALSE;
}
/* FIXME: This should be IGNORE_CASE */
if (!err)
/* FIXME: This should be IGNORE_CASE */
if (!err)
if (!err)
if (err < 0) {
ntfs_log_error("Couldn't create $Secure: %s\n",
return FALSE;
}
ntfs_log_verbose("Creating $UpCase (mft record 0xa)\n");
if (!err)
FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0,
"$UpCase", FILE_NAME_WIN32_AND_DOS);
if (err < 0) {
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->flags |= MFT_RECORD_IS_DIRECTORY;
if (!err)
FILE_ATTR_I30_INDEX_PRESENT, 0, 0,
"$Extend", FILE_NAME_WIN32_AND_DOS);
/* FIXME: This should be IGNORE_CASE */
if (!err)
if (err < 0) {
ntfs_log_error("Couldn't create $Extend: %s\n",
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);
if (!err) {
init_system_file_sd(i, &sd, &j);
}
if (err < 0) {
ntfs_log_error("Couldn't create system file %i (0x%x): "
return FALSE;
}
}
/* create systemfiles for ntfs volumes (3.1) */
/* starting with file 24 (ignoring file 16-23) */
ntfs_log_verbose("Creating $Quota (mft record 24)\n");
m->flags |= MFT_RECORD_IS_4;
m->flags |= MFT_RECORD_IS_VIEW_INDEX;
if (!err)
0, 0, "$Quota", FILE_NAME_WIN32_AND_DOS);
/* FIXME: This should be IGNORE_CASE */
if (!err)
/* FIXME: This should be IGNORE_CASE */
if (!err)
if (!err)
err = initialize_quota(m);
if (err < 0) {
return FALSE;
}
ntfs_log_verbose("Creating $ObjId (mft record 25)\n");
m->flags |= MFT_RECORD_IS_4;
m->flags |= MFT_RECORD_IS_VIEW_INDEX;
if (!err)
extend_flags, 0, 0, "$ObjId",
/* FIXME: This should be IGNORE_CASE */
if (!err)
#ifdef ENABLE_UUID
if (!err)
#endif
if (err < 0) {
ntfs_log_error("Couldn't create $ObjId: %s\n",
return FALSE;
}
ntfs_log_verbose("Creating $Reparse (mft record 26)\n");
m->flags |= MFT_RECORD_IS_4;
m->flags |= MFT_RECORD_IS_VIEW_INDEX;
if (!err)
"$Reparse", FILE_NAME_WIN32_AND_DOS);
/* FIXME: This should be IGNORE_CASE */
if (!err)
if (err < 0) {
ntfs_log_error("Couldn't create $Reparse: %s\n",
return FALSE;
}
return TRUE;
}
/**
* mkntfs_redirect
*/
{
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;
}
/* Transfer some options to the volume. */
ntfs_log_perror("Could not copy volume name");
goto done;
}
}
if (opts.cluster_size >= 0)
/* Length is in unicode characters. */
goto done;
ntfs_log_perror("Could not create attrdef structure");
goto done;
}
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
*/
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. */
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");
goto done;
ntfs_log_verbose("Syncing $Bitmap.\n");
if (!ctx) {
ntfs_log_perror("Could not create an attribute search context");
goto done;
}
ntfs_log_error("BUG: $DATA attribute not found.\n");
goto done;
}
if (a->non_resident) {
if (!rl) {
ntfs_log_error("ntfs_mapping_pairs_decompress() failed\n");
goto done;
}
if (lw != g_lcn_bitmap_byte_size) {
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");
lw = 1;
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) {
goto done;
}
}
ntfs_log_verbose("Updating $MFTMirr.\n");
lw = 1;
/*
* 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;
}
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) {
goto done;
}
}
ntfs_log_verbose("Syncing device.\n");
ntfs_log_error("Syncing device. FAILED");
goto done;
}
ntfs_log_quiet("mkntfs completed successfully. Have a nice day.\n");
result = 0;
done:
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
*/
{
goto done;
done:
return result;
}