diff --git a/TODO.ntfsprogs b/TODO.ntfsprogs index 456794b7..71149329 100644 --- a/TODO.ntfsprogs +++ b/TODO.ntfsprogs @@ -65,7 +65,6 @@ Thanks, ********** - add ability to copy multiply files at once. -- add ability to create new files. *********** @@ -93,7 +92,6 @@ Thanks, * ntfsmount * ************* -- Cache opened inodes for faster access. ************** diff --git a/configure.ac b/configure.ac index 41c45bb0..61084752 100644 --- a/configure.ac +++ b/configure.ac @@ -278,7 +278,7 @@ fi compile_crypto=false if test "$enable_crypto" != "no"; then have_libgcrypt=false - AM_PATH_LIBGCRYPT(1.2.0, [ have_libgcrypt=true ], + AM_PATH_LIBGCRYPT(1.2.2, [ have_libgcrypt=true ], [ if test "$enable_crypto" = "yes"; then AC_MSG_ERROR([ntfsprogs crypto code requires the gcrypt library.]) @@ -287,17 +287,29 @@ if test "$enable_crypto" != "no"; then fi ]) have_libgnutls=false - PKG_CHECK_MODULES(GNUTLS_MODULE, gnutls >= 1.2.8, [ have_libgnutls=true ], - [ + PKG_CHECK_MODULES(GNUTLS, gnutls >= 1.4.4, [ have_libgnutls=true ], if test "$enable_crypto" = "yes"; then AC_MSG_ERROR([ntfsprogs crypto code requires the gnutls library.]) else AC_MSG_WARN([ntfsprogs crypto code requires the gnutls library.]) fi - ]) + ) + have_libconfig=false + PKG_CHECK_MODULES(libconfig, libconfig >= 1.0.1, [ have_libconfig=true ], + if test "$enable_crypto" = "yes"; then + AC_MSG_ERROR([ntfsprogs crypto code requires the libconfig.]) + else + AC_MSG_WARN([ntfsprogs crypto code requires the libconfig.]) + fi + ) if test "$have_libgcrypt" = "true"; then if test "$have_libgnutls" = "true"; then - compile_crypto=true + if test "$have_libconfig" = "true"; then + compile_crypto=true + AC_DEFINE([ENABLE_CRYPTO], 1, + [Define this to 1 if you want to enable support of + encrypted files in libntfs and utilities.]) + fi fi fi fi @@ -309,6 +321,42 @@ all_includes="$all_includes $USER_INCLUDES" AC_SUBST(all_includes) AC_SUBST(all_libraries) +# Specify support for generating DCE compliant UUIDs (aka GUIDs). We check if +# uuid/uuid.h header is present and the uuid library is present that goes with +# it and then check if uuid_generate() is present and usable. +# +# DCE UUIDs are enabled by default and can be disabled with the --disable-uuid +# option to the configure script. +AC_ARG_WITH(uuid, [ + --with-uuid@<:@=PFX@:>@ generate DCE compliant UUIDs, with optional prefix + to uuid library and headers @<:@default=detect@:>@ + --without-uuid do not generate DCE compliant UUIDs], + if test "$with_uuid" = "yes"; then + extrapath=default + elif test "$with_uuid" = "no"; then + extrapath= + else + extrapath=$with_uuid + fi, + extrapath=default +) +if test "x$extrapath" != "x"; then + if test "x$extrapath" != "xdefault"; then + MKNTFS_CPPFLAGS="$MKNTFS_CPPFLAGS -I$extrapath/include" + MKNTFS_LIBS="$MKNTFS_LIBS -L$extrapath/lib" + fi + AC_CHECK_HEADER([uuid/uuid.h], + AC_CHECK_LIB([uuid], [uuid_generate], + AC_DEFINE([ENABLE_UUID], 1, + [Define this to 1 if you want to enable generation of + DCE compliant UUIDs.]) + MKNTFS_LIBS="$MKNTFS_LIBS -luuid", + AC_MSG_WARN([ntfsprogs DCE compliant UUID generation code requires the uuid library.]), + ), + AC_MSG_WARN([ntfsprogs DCE compliant UUID generation code requires the uuid library.]), + ) +fi + # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ @@ -317,7 +365,7 @@ AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ endian.h byteswap.h sys/byteorder.h sys/endian.h sys/param.h \ sys/ioctl.h sys/mkdev.h sys/mount.h sys/stat.h sys/types.h sys/vfs.h \ sys/statvfs.h sys/sysmacros.h linux/major.h linux/fd.h linux/hdreg.h \ - machine/endian.h gcrypt.h windows.h gnutls/pkcs12.h syslog.h]) + machine/endian.h windows.h syslog.h pwd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL diff --git a/include/ntfs/crypto.h b/include/ntfs/crypto.h new file mode 100644 index 00000000..a4b72435 --- /dev/null +++ b/include/ntfs/crypto.h @@ -0,0 +1,46 @@ +/** + * crypto.h - Exports for dealing with encrypted files. Part of the + * Linux-NTFS project. + * + * Copyright (c) 2007 Yura Pakhuchiy + * + * 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 + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_CRYPTO_H +#define _NTFS_CRYPTO_H + +extern ntfschar NTFS_EFS[5]; + +/* + * This is our Big Secret (TM) structure, so do not allow anyone even read it + * values. ;-) In fact, it is private because exist only in libntfs version + * compiled with cryptography support, so users can not depend on it. + */ +typedef struct _ntfs_crypto_attr ntfs_crypto_attr; + +/* + * These functions should not be used directly. They are called for encrypted + * attributes from corresponding functions without _crypto_ part. + */ + +extern int ntfs_crypto_attr_open(ntfs_attr *na); +extern void ntfs_crypto_attr_close(ntfs_attr *na); + +extern s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, + void *b); + +#endif /* _NTFS_CRYPTO_H */ diff --git a/libntfs/config b/libntfs/config new file mode 100644 index 00000000..71a18d99 --- /dev/null +++ b/libntfs/config @@ -0,0 +1,10 @@ +# libntfs sample configuration file + +crypto : { + keys = ( + ("/home/yura/ntfs/my3.pfx", "my3"), # key with password +# ("/home/yura/ntfs/my-rec.pfx", ""), // password-less key + ("/home/yura/ntfs/my.pfx") /* password-less key */ + ); +}; + diff --git a/libntfs/crypto.c b/libntfs/crypto.c new file mode 100644 index 00000000..688716db --- /dev/null +++ b/libntfs/crypto.c @@ -0,0 +1,1518 @@ +/** + * crypto.c - Routines for dealing with encrypted files. Part of the + * Linux-NTFS project. + * + * Copyright (c) 2005 Yuval Fledel + * Copyright (c) 2005-2007 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy + * + * 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 + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO: Cleanup this file. Write nice descriptions for non-exported functions + * and maybe clean up namespace (not necessary for all functions to belong to + * ntfs_crypto, we can have ntfs_fek, ntfs_rsa, etc.., but there should be + * maximum 2-3 namespaces, not every function begins with it own namespace + * like now). + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "attrib.h" +#include "types.h" +#include "volume.h" +#include "debug.h" +#include "dir.h" +#include "layout.h" +#include "crypto.h" + +#ifdef ENABLE_CRYPTO + +#include +#include +#include + +#include + +#define NTFS_CONFIG_PATH_SYSTEM "/etc/libntfs/config" +#define NTFS_CONFIG_PATH_USER ".libntfs/config" + +#define NTFS_SHA1_THUMBPRINT_SIZE 0x14 + +#define NTFS_CRED_TYPE_CERT_THUMBPRINT const_cpu_to_le32(3) + +#define NTFS_EFS_CERT_PURPOSE_OID_DDF "1.3.6.1.4.1.311.10.3.4" +#define NTFS_EFS_CERT_PURPOSE_OID_DRF "1.3.6.1.4.1.311.10.3.4.1" + +#define NTFS_EFS_SECTOR_SIZE 512 + +typedef enum { + DF_TYPE_UNKNOWN, + DF_TYPE_DDF, + DF_TYPE_DRF, +} NTFS_DF_TYPES; + +/** + * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit) + * + * To choose which one is used in Windows, create or set the REG_DWORD registry + * key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\ + * AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX, + * set AlgorithmID to 0x6604. + * + * Note that the Windows versions I have tried so far (all are high crypto + * enabled) ignore the AlgorithmID value if it is not one of CALG_3DES, + * CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using + * this registry key. It would be interesting to check out encryption on one + * of the "crippled" crypto Windows versions... + */ +typedef enum { + CALG_DES = const_cpu_to_le32(0x6601), + /* If not one of the below three, fall back to standard Des. */ + CALG_3DES = const_cpu_to_le32(0x6603), + CALG_DESX = const_cpu_to_le32(0x6604), + CALG_AES_256 = const_cpu_to_le32(0x6610), +} NTFS_CRYPTO_ALGORITHMS; + +/** + * struct ntfs_fek - Decrypted, in-memory file encryption key. + */ +struct _ntfs_fek { + gcry_cipher_hd_t gcry_cipher_hd; + le32 alg_id; + u8 *key_data; + gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; +}; + +typedef struct _ntfs_fek ntfs_fek; + +struct _ntfs_crypto_attr { + ntfs_fek *fek; +}; + +typedef struct { + u64 in_whitening, out_whitening; + gcry_cipher_hd_t gcry_cipher_hd; +} ntfs_desx_ctx; + +ntfschar NTFS_EFS[5] = { + const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'), + const_cpu_to_le16('S'), const_cpu_to_le16(0) +}; + +typedef struct { + gcry_sexp_t key; + NTFS_DF_TYPES df_type; + char thumbprint[NTFS_SHA1_THUMBPRINT_SIZE]; +} ntfs_rsa_private_key_t; + +/* + * Yes, global variables sucks, but we need to keep whether we performed + * gcrypt/gnutls global initialization and keep user's RSA keys. + */ +typedef struct { + int initialized; + int desx_alg_id; + gcry_module_t desx_module; + ntfs_rsa_private_key_t **rsa_key; + int nr_rsa_keys; +} ntfs_crypto_ctx_t; + +static ntfs_crypto_ctx_t ntfs_crypto_ctx = { + .desx_alg_id = -1, + .desx_module = NULL, +}; + +/** + * ntfs_pkcs12_load_pfxfile + */ +static int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx, + unsigned *pfx_size) +{ + int f, to_read, total, attempts, br; + struct stat key_stat; + + if (!keyfile || !pfx || !pfx_size) { + ntfs_log_error("You have to specify the key file, a pointer " + "to hold the key file contents, and a pointer " + "to hold the size of the key file contents.\n"); + return -1; + } + f = open(keyfile, O_RDONLY); + if (f == -1) { + ntfs_log_perror("Failed to open key file"); + return -1; + } + if (fstat(f, &key_stat) == -1) { + ntfs_log_perror("Failed to stat key file"); + goto file_out; + } + if (!S_ISREG(key_stat.st_mode)) { + ntfs_log_error("Key file is not a regular file, cannot read " + "it.\n"); + goto file_out; + } + if (!key_stat.st_size) { + ntfs_log_error("Key file has zero size.\n"); + goto file_out; + } + *pfx = malloc(key_stat.st_size + 1); + if (!*pfx) { + ntfs_log_perror("Failed to allocate buffer for key file " + "contents"); + goto file_out; + } + to_read = key_stat.st_size; + total = attempts = 0; + do { + br = read(f, *pfx + total, to_read); + if (br == -1) { + ntfs_log_perror("Failed to read from key file"); + goto free_out; + } + if (!br) + attempts++; + to_read -= br; + total += br; + } while (to_read > 0 && attempts < 3); + close(f); + /* Make sure it is zero terminated. */ + (*pfx)[key_stat.st_size] = 0; + *pfx_size = key_stat.st_size; + return 0; +free_out: + free(*pfx); +file_out: + close(f); + return -1; +} + +/** + * ntfs_rsa_private_key_import_from_gnutls + */ +static gcry_sexp_t ntfs_rsa_private_key_import_from_gnutls( + gnutls_x509_privkey_t priv_key) +{ + int i, j; + size_t tmp_size; + gnutls_datum_t rd[6]; + gcry_mpi_t rm[6]; + gcry_sexp_t rsa_key; + + /* Extract the RSA parameters from the GNU TLS private key. */ + if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1], + &rd[2], &rd[3], &rd[4], &rd[5])) { + ntfs_log_error("Failed to export rsa parameters. (Is the " + "key an RSA private key?)\n"); + return NULL; + } + /* Convert each RSA parameter to MPI format. */ + for (i = 0; i < 6; i++) { + if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data, + rd[i].size, &tmp_size) != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to convert RSA parameter %i " + "to mpi format (size %d)\n", i, + rd[i].size); + rsa_key = NULL; + break; + } + } + /* Release the no longer needed datum values. */ + for (j = 0; j < 6; j++) { + if (rd[j].data && rd[j].size) + gnutls_free(rd[j].data); + } + /* + * Build the gcrypt private key, note libgcrypt uses p and q inversed + * to what gnutls uses. + */ + if (i == 6 && gcry_sexp_build(&rsa_key, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + rm[0], rm[1], rm[2], rm[4], rm[3], rm[5]) != + GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to build RSA private key s-exp.\n"); + rsa_key = NULL; + } + /* Release the no longer needed MPI values. */ + for (j = 0; j < i; j++) + gcry_mpi_release(rm[j]); + return rsa_key; +} + +/** + * ntfs_rsa_private_key_release + */ +static void ntfs_rsa_private_key_release(ntfs_rsa_private_key_t *rsa_key) +{ + if (rsa_key) { + if (rsa_key->key) + gcry_sexp_release(rsa_key->key); + free(rsa_key); + } +} + +/** + * ntfs_pkcs12_extract_rsa_key + */ +static ntfs_rsa_private_key_t *ntfs_pkcs12_extract_rsa_key(u8 *pfx, + int pfx_size, const char *password) +{ + int err, bag_index, flags; + gnutls_datum_t dpfx, dkey; + gnutls_pkcs12_t pkcs12 = NULL; + gnutls_pkcs12_bag_t bag = NULL; + gnutls_x509_privkey_t pkey = NULL; + gnutls_x509_crt_t crt = NULL; + ntfs_rsa_private_key_t *rsa_key = NULL; + char purpose_oid[100]; + size_t purpose_oid_size = sizeof(purpose_oid); + size_t tp_size; + BOOL have_thumbprint = FALSE; + + rsa_key = malloc(sizeof(ntfs_rsa_private_key_t)); + if (!rsa_key) { + ntfs_log_perror("%s", __FUNCTION__); + return NULL; + } + rsa_key->df_type = DF_TYPE_UNKNOWN; + rsa_key->key = NULL; + tp_size = sizeof(rsa_key->thumbprint); + /* Create a pkcs12 structure. */ + err = gnutls_pkcs12_init(&pkcs12); + if (err) { + ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n", + gnutls_strerror(err)); + goto err; + } + /* Convert the PFX file (DER format) to native pkcs12 format. */ + dpfx.data = pfx; + dpfx.size = pfx_size; + err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0); + if (err) { + ntfs_log_error("Failed to convert the PFX file from DER to " + "native PKCS#12 format: %s\n", + gnutls_strerror(err)); + goto err; + } + /* + * Verify that the password is correct and that the key file has not + * been tampered with. Note if the password has zero length and the + * verification fails, retry with password set to NULL. This is needed + * to get password less .pfx files generated with Windows XP SP1 (and + * probably earlier versions of Windows) to work. + */ +retry_verify: + err = gnutls_pkcs12_verify_mac(pkcs12, password); + if (err) { + if (err == GNUTLS_E_MAC_VERIFY_FAILED && + password && !strlen(password)) { + password = NULL; + goto retry_verify; + } + ntfs_log_error("You are probably misspelled password to PFX " + "file.\n"); + goto err; + } + for (bag_index = 0; ; bag_index++) { + err = gnutls_pkcs12_bag_init(&bag); + if (err) { + ntfs_log_error("Failed to initialize PKCS#12 Bag " + "structure: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); + if (err) { + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + err = 0; + break; + } + ntfs_log_error("Failed to obtain Bag from PKCS#12 " + "structure: %s\n", + gnutls_strerror(err)); + goto err; + } +check_again: + err = gnutls_pkcs12_bag_get_count(bag); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag count: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_pkcs12_bag_get_type(bag, 0); + if (err < 0) { + ntfs_log_error("Failed to determine Bag type: %s\n", + gnutls_strerror(err)); + goto err; + } + flags = 0; + switch (err) { + case GNUTLS_BAG_PKCS8_KEY: + flags = GNUTLS_PKCS_PLAIN; + case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_privkey_init(&pkey); + if (err) { + ntfs_log_error("Failed to initialized " + "private key structure: %s\n", + gnutls_strerror(err)); + goto err; + } + /* Decrypt the private key into GNU TLS format. */ + err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, + GNUTLS_X509_FMT_DER, password, flags); + if (err) { + ntfs_log_error("Failed to convert private " + "key from DER to GNU TLS " + "format: %s\n", + gnutls_strerror(err)); + goto err; + } +#if 0 + /* + * Export the key again, but unencrypted, and output it + * to stderr. Note the output has an RSA header so to + * compare to openssl pkcs12 -nodes -in myfile.pfx + * output need to ignore the part of the key between + * the first "MII..." up to the second "MII...". The + * actual RSA private key begins at the second "MII..." + * and in my testing at least was identical to openssl + * output and was also identical both on big and little + * endian so gnutls should be endianness safe. + */ + char *buf = malloc(8192); + size_t bufsize = 8192; + err = gnutls_x509_privkey_export_pkcs8(pkey, + GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf, + &bufsize); + if (err) { + ntfs_log_error("eek1\n"); + exit(1); + } + ntfs_log_error("%s\n", buf); + free(buf); +#endif + /* Convert the private key to our internal format. */ + rsa_key->key = + ntfs_rsa_private_key_import_from_gnutls(pkey); + if (!rsa_key->key) + goto err; + break; + case GNUTLS_BAG_ENCRYPTED: + err = gnutls_pkcs12_bag_decrypt(bag, password); + if (err) { + ntfs_log_error("Failed to decrypt Bag: %s\n", + gnutls_strerror(err)); + goto err; + } + goto check_again; + case GNUTLS_BAG_CERTIFICATE: + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_init(&crt); + if (err) { + ntfs_log_error("Failed to initialize " + "certificate structure: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_import(crt, &dkey, + GNUTLS_X509_FMT_DER); + if (err) { + ntfs_log_error("Failed to convert certificate " + "from DER to GNU TLS format: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_get_key_purpose_oid(crt, 0, + purpose_oid, &purpose_oid_size, NULL); + if (err) { + ntfs_log_error("Failed to get key purpose " + "OID: %s\n", + gnutls_strerror(err)); + goto err; + } + purpose_oid[purpose_oid_size - 1] = 0; + if (!strcmp(purpose_oid, + NTFS_EFS_CERT_PURPOSE_OID_DRF)) + rsa_key->df_type = DF_TYPE_DRF; + else if (!strcmp(purpose_oid, + NTFS_EFS_CERT_PURPOSE_OID_DDF)) + rsa_key->df_type = DF_TYPE_DDF; + else { + ntfs_log_error("Certificate has unknown " + "purpose OID %s.\n", + purpose_oid); + err = EINVAL; + goto err; + } + /* Return the thumbprint to the caller. */ + err = gnutls_x509_crt_get_fingerprint(crt, + GNUTLS_DIG_SHA1, rsa_key->thumbprint, + &tp_size); + if (err) { + ntfs_log_error("Failed to get thumbprint: " + "%s\n", gnutls_strerror(err)); + goto err; + } + if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) { + ntfs_log_error("Invalid thumbprint size %zd. " + "Should be %d.\n", tp_size, + sizeof(rsa_key->thumbprint)); + err = EINVAL; + goto err; + } + have_thumbprint = TRUE; + gnutls_x509_crt_deinit(crt); + crt = NULL; + break; + default: + /* We do not care about other types. */ + break; + } + gnutls_pkcs12_bag_deinit(bag); + } +err: + if (err || !rsa_key->key || rsa_key->df_type == DF_TYPE_UNKNOWN || + !have_thumbprint) { + if (!err) + ntfs_log_error("Key type or thumbprint not found, " + "aborting.\n"); + ntfs_rsa_private_key_release(rsa_key); + rsa_key = NULL; + } + if (crt) + gnutls_x509_crt_deinit(crt); + if (pkey) + gnutls_x509_privkey_deinit(pkey); + if (bag) + gnutls_pkcs12_bag_deinit(bag); + if (pkcs12) + gnutls_pkcs12_deinit(pkcs12); + return rsa_key; +} + +/** + * ntfs_buffer_reverse - + * + * This is a utility function for reversing the order of a buffer in place. + * Users of this function should be very careful not to sweep byte order + * problems under the rug. + */ +static inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size) +{ + unsigned i; + u8 t; + + for (i = 0; i < buf_size / 2; i++) { + t = buf[i]; + buf[i] = buf[buf_size - i - 1]; + buf[buf_size - i - 1] = t; + } +} + +#ifndef HAVE_STRNLEN +/** + * strnlen - strnlen is a gnu extension so emulate it if not present + */ +static size_t strnlen(const char *s, size_t maxlen) +{ + const char *p, *end; + + /* Look for a '\0' character. */ + for (p = s, end = s + maxlen; p < end && *p; p++) + ; + return p - s; +} +#endif /* ! HAVE_STRNLEN */ + +/** + * ntfs_raw_fek_decrypt - + * + * Note: decrypting into the input buffer. + */ +static unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size, + ntfs_rsa_private_key_t *rsa_key) +{ + gcry_mpi_t fek_mpi; + gcry_sexp_t fek_sexp, fek_sexp2; + gcry_error_t err; + size_t size, padding; + + /* Reverse the raw FEK. */ + ntfs_buffer_reverse(fek, fek_size); + /* Convert the FEK to internal MPI format. */ + err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to convert file encryption key to " + "internal MPI format: %s\n", + gcry_strerror(err)); + return 0; + } + /* Create an internal S-expression from the FEK. */ + err = gcry_sexp_build(&fek_sexp, NULL, + "(enc-val (flags) (rsa (a %m)))", fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to create internal S-expression of " + "the file encryption key: %s\n", + gcry_strerror(err)); + return 0; + } + /* Decrypt the FEK. */ + err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, rsa_key->key); + gcry_sexp_release(fek_sexp); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to decrypt the file encryption key: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* Extract the actual FEK from the decrypted raw S-expression. */ + fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0); + gcry_sexp_release(fek_sexp2); + if (!fek_sexp) { + ntfs_log_error("Failed to find the decrypted file encryption " + "key in the internal S-expression.\n"); + return 0; + } + /* Convert the decrypted FEK S-expression into MPI format. */ + fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG); + gcry_sexp_release(fek_sexp); + if (!fek_mpi) { + ntfs_log_error("Failed to convert the decrypted file " + "encryption key S-expression to internal MPI " + "format.\n"); + return 0; + } + /* Convert the decrypted FEK from MPI format to binary data. */ + err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi); + gcry_mpi_release(fek_mpi); + if (err != GPG_ERR_NO_ERROR || !size) { + ntfs_log_error("Failed to convert decrypted file encryption " + "key from internal MPI format to binary data: " + "%s\n", gcry_strerror(err)); + return 0; + } + /* + * Finally, remove the PKCS#1 padding and return the size of the + * decrypted FEK. + */ + padding = strnlen((char *)fek, size) + 1; + if (padding > size) { + ntfs_log_error("Failed to remove PKCS#1 padding from " + "decrypted file encryption key.\n"); + return 0; + } + size -= padding; + memmove(fek, fek + padding, size); + return size; +} + +/** + * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key + * @src: source buffer containing 128-bit key + * + * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the + * out-whitening keys required to perform desx {de,en}cryption. + */ +static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key, + u64 *out_whitening, u64 *in_whitening) +{ + static const u8 *salt1 = (const u8*)"Dan Simon "; + static const u8 *salt2 = (const u8*)"Scott Field"; + static const int salt_len = 12; + gcry_md_hd_t hd1, hd2; + u32 *md; + gcry_error_t err; + + err = gcry_md_open(&hd1, GCRY_MD_MD5, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open MD5 digest.\n"); + return err; + } + /* Hash the on-disk key. */ + gcry_md_write(hd1, src, 128 / 8); + /* Copy the current hash for efficiency. */ + err = gcry_md_copy(&hd2, hd1); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to copy MD5 digest object.\n"); + goto out; + } + /* Hash with the first salt and store the result. */ + gcry_md_write(hd1, salt1, salt_len); + md = (u32*)gcry_md_read(hd1, 0); + des_key[0] = md[0] ^ md[1]; + des_key[1] = md[2] ^ md[3]; + /* Hash with the second salt and store the result. */ + gcry_md_write(hd2, salt2, salt_len); + md = (u32*)gcry_md_read(hd2, 0); + *out_whitening = *(u64*)md; + *in_whitening = *(u64*)(md + 2); + gcry_md_close(hd2); +out: + gcry_md_close(hd1); + return err; +} + +/** + * ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128 + * @context: pointer to a variable of type ntfs_desx_ctx + * @key: the 128 bit DES-X-MS128 key, concated with the DES handle + * @keylen: must always be 16 + * + * This is the libgcrypt set_key implementation for DES-X-MS128. + */ +static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key, + unsigned keylen) +{ + ntfs_desx_ctx *ctx = context; + gcry_error_t err; + u8 des_key[8]; + + if (keylen != 16) { + ntfs_log_error("Key length for desx must be 16.\n"); + return GPG_ERR_INV_KEYLEN; + } + err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open des cipher (error 0x%x).\n", + err); + return err; + } + err = ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening, + &ctx->in_whitening); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to expand desx key (error 0x%x).\n", + err); + gcry_cipher_close(ctx->gcry_cipher_hd); + return err; + } + err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, sizeof(des_key)); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to set des key (error 0x%x).\n", err); + gcry_cipher_close(ctx->gcry_cipher_hd); + return err; + } + /* + * Take a note of the ctx->gcry_cipher_hd since we need to close it at + * ntfs_decrypt_data_key_close() time. + */ + **(gcry_cipher_hd_t***)(key + ((keylen + 7) & ~7)) = + &ctx->gcry_cipher_hd; + return GPG_ERR_NO_ERROR; +} + +/** + * ntfs_desx_decrypt + */ +static void ntfs_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf) +{ + ntfs_desx_ctx *ctx = context; + gcry_error_t err; + + err = gcry_cipher_reset(ctx->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Failed to reset des cipher (error 0x%x).\n", + err); + *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening; + err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + ntfs_log_error("Des decryption failed (error 0x%x).\n", err); + *(u64*)outbuf ^= ctx->in_whitening; +} + +static gcry_cipher_spec_t ntfs_desx_cipher = { + .name = "DES-X-MS128", + .blocksize = 8, + .keylen = 128, + .contextsize = sizeof(ntfs_desx_ctx), + .setkey = ntfs_desx_setkey, + .decrypt = ntfs_desx_decrypt, +}; + +#ifdef NTFS_TEST +/* + * Do not remove this test code from this file! (AIA) + * It would be nice to move all tests (these and runlist) out of the library + * (at least, into the separate file{,s}), so they would not annoy eyes. (Yura) + */ + +/** + * ntfs_desx_key_expand_test + */ +static BOOL ntfs_desx_key_expand_test(void) +{ + const u8 known_desx_on_disk_key[16] = { + 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f, + 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30 + }; + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f, + }; + const u8 known_out_whitening[8] = { + 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d, + }; + const u8 known_in_whitening[8] = { + 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e + }; + u64 test_out_whitening, test_in_whitening; + union { + u64 u64; + u32 u32[2]; + } test_des_key; + gcry_error_t err; + BOOL res; + + err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32, + &test_out_whitening, &test_in_whitening); + if (err != GPG_ERR_NO_ERROR) + res = FALSE; + else + res = test_des_key.u64 == *(u64*)known_des_key && + test_out_whitening == + *(u64*)known_out_whitening && + test_in_whitening == + *(u64*)known_in_whitening; + ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +/** + * ntfs_des_test + */ +static BOOL ntfs_des_test(void) +{ + const u8 known_des_key[8] = { + 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f + }; + const u8 known_des_encrypted_data[8] = { + 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f + }; + const u8 known_decrypted_data[8] = { + 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09 + }; + u8 test_decrypted_data[8]; + int res; + gcry_error_t err; + gcry_cipher_hd_t gcry_cipher_hd; + + err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES, + GCRY_CIPHER_MODE_ECB, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to open des cipher (error 0x%x).\n", + err); + return FALSE; + } + err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key, + sizeof(known_des_key)); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to set des key (error 0x%x.\n", err); + gcry_cipher_close(gcry_cipher_hd); + return FALSE; + } + /* + * Apply DES decryption (ntfs actually uses encryption when decrypting). + */ + err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data, + sizeof(test_decrypted_data), known_des_encrypted_data, + sizeof(known_des_encrypted_data)); + gcry_cipher_close(gcry_cipher_hd); + if (err) { + ntfs_log_error("Failed to des decrypt test data (error " + "0x%x).\n", err); + return FALSE; + } + res = !memcmp(test_decrypted_data, known_decrypted_data, + sizeof(known_decrypted_data)); + ntfs_log_error("Testing whether des decryption works: %s\n", + res ? "SUCCESS" : "FAILED"); + return res; +} + +#else /* !defined(NTFS_TEST) */ + +/** + * ntfs_desx_key_expand_test + */ +static inline BOOL ntfs_desx_key_expand_test(void) +{ + return TRUE; +} + +/** + * ntfs_des_test + */ +static inline BOOL ntfs_des_test(void) +{ + return TRUE; +} + +#endif /* !defined(NTFS_TEST) */ + +/** + * ntfs_fek_import_from_raw + */ +static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, + unsigned fek_size) +{ + ntfs_fek *fek; + u32 key_size, wanted_key_size, gcry_algo; + gcry_error_t err; + + key_size = le32_to_cpup(fek_buf); + ntfs_log_debug("key_size 0x%x\n", key_size); + if (key_size + 16 > fek_size) { + ntfs_log_debug("Invalid FEK. It was probably decrypted with " + "the incorrect RSA key."); + errno = EINVAL; + return NULL; + } + fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + + sizeof(gcry_cipher_hd_t)); + if (!fek) { + errno = ENOMEM; + return NULL; + } + fek->alg_id = *(le32*)(fek_buf + 8); + ntfs_log_debug("algorithm_id 0x%x\n", le32_to_cpu(fek->alg_id)); + fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); + memcpy(fek->key_data, fek_buf + 16, key_size); + fek->des_gcry_cipher_hd_ptr = NULL; + *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) = + &fek->des_gcry_cipher_hd_ptr; + switch (fek->alg_id) { + case CALG_DESX: + if (!ntfs_crypto_ctx.desx_module) { + if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) { + err = EINVAL; + goto out; + } + err = gcry_cipher_register(&ntfs_desx_cipher, + &ntfs_crypto_ctx.desx_alg_id, + &ntfs_crypto_ctx.desx_module); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to register desx " + "cipher: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + } + wanted_key_size = 16; + gcry_algo = ntfs_crypto_ctx.desx_alg_id; + break; + case CALG_3DES: + wanted_key_size = 24; + gcry_algo = GCRY_CIPHER_3DES; + break; + case CALG_AES_256: + wanted_key_size = 32; + gcry_algo = GCRY_CIPHER_AES256; + break; + default: + wanted_key_size = 8; + gcry_algo = GCRY_CIPHER_DES; + if (fek->alg_id == CALG_DES) + ntfs_log_error("DES is not supported at present\n"); + else + ntfs_log_error("Unknown crypto algorithm 0x%x\n", + le32_to_cpu(fek->alg_id)); + ntfs_log_error(". Please email %s and say that you saw this " + "message. We will then try to implement " + "support for this algorithm.\n", NTFS_DEV_LIST); + err = EOPNOTSUPP; + goto out; + } + if (key_size != wanted_key_size) { + ntfs_log_error("%s key of %u bytes but needed size is %u " + "bytes, assuming corrupt or incorrect key. " + "Aborting.\n", + gcry_cipher_algo_name(gcry_algo), + (unsigned)key_size, (unsigned)wanted_key_size); + err = EIO; + goto out; + } + err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo, + GCRY_CIPHER_MODE_CBC, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("gcry_cipher_open() failed: %s\n", + gcry_strerror(err)); + err = EINVAL; + goto out; + } + err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, key_size); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("gcry_cipher_setkey() failed: %s\n", + gcry_strerror(err)); + gcry_cipher_close(fek->gcry_cipher_hd); + err = EINVAL; + goto out; + } + return fek; +out: + free(fek); + errno = err; + return NULL; +} + +/** + * ntfs_fek_release + */ +static void ntfs_fek_release(ntfs_fek *fek) +{ + if (fek->des_gcry_cipher_hd_ptr) + gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr); + gcry_cipher_close(fek->gcry_cipher_hd); + free(fek); +} + +/** + * ntfs_df_array_fek_get + */ +static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, + ntfs_rsa_private_key_t *rsa_key) +{ + EFS_DF_HEADER *df_header; + EFS_DF_CREDENTIAL_HEADER *df_cred; + EFS_DF_CERT_THUMBPRINT_HEADER *df_cert; + u8 *fek_buf; + ntfs_fek *fek; + u32 df_count, fek_size; + unsigned i, thumbprint_size = sizeof(rsa_key->thumbprint); + + df_count = le32_to_cpu(df_array->df_count); + if (!df_count) + ntfs_log_error("There are no elements in the DF array.\n"); + df_header = (EFS_DF_HEADER*)(df_array + 1); + for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)( + (u8*)df_header + le32_to_cpu(df_header->df_length))) { + df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + + le32_to_cpu(df_header->cred_header_offset)); + if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) { + ntfs_log_debug("Credential type is not certificate " + "thumbprint, skipping DF entry.\n"); + continue; + } + df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred + + le32_to_cpu( + df_cred->cert_thumbprint_header_offset)); + if (le32_to_cpu(df_cert->thumbprint_size) != thumbprint_size) { + ntfs_log_error("Thumbprint size %d is not valid " + "(should be %d), skipping this DF " + "entry.\n", + le32_to_cpu(df_cert->thumbprint_size), + thumbprint_size); + continue; + } + if (memcmp((u8*)df_cert + + le32_to_cpu(df_cert->thumbprint_offset), + rsa_key->thumbprint, thumbprint_size)) { + ntfs_log_debug("Thumbprints do not match, skipping " + "this DF entry.\n"); + continue; + } + /* + * The thumbprints match so this is probably the DF entry + * matching the RSA key. Try to decrypt the FEK with it. + */ + fek_size = le32_to_cpu(df_header->fek_size); + fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); + /* Decrypt the FEK. Note: This is done in place. */ + fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key); + if (fek_size) { + /* Convert the FEK to our internal format. */ + fek = ntfs_fek_import_from_raw(fek_buf, fek_size); + if (fek) + return fek; + ntfs_log_error("Failed to convert the decrypted file " + "encryption key to internal format.\n"); + } else + ntfs_log_error("Failed to decrypt the file " + "encryption key.\n"); + } + return NULL; +} + +/** + * ntfs_inode_fek_get - + */ +static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, + ntfs_rsa_private_key_t *rsa_key) +{ + EFS_ATTR_HEADER *efs; + EFS_DF_ARRAY_HEADER *df_array = NULL; + ntfs_fek *fek = NULL; + + /* Obtain the $EFS contents. */ + efs = ntfs_attr_readall(inode, AT_LOGGED_UTILITY_STREAM, NTFS_EFS, 4, + NULL); + if (!efs) { + ntfs_log_perror("Failed to read $EFS attribute"); + return NULL; + } + /* + * Depending on whether the key is a normal key or a data recovery key, + * iterate through the DDF or DRF array, respectively. + */ + if (rsa_key->df_type == DF_TYPE_DDF) { + if (efs->offset_to_ddf_array) + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_ddf_array)); + else + ntfs_log_error("There are no entries in the DDF " + "array.\n"); + } else if (rsa_key->df_type == DF_TYPE_DRF) { + if (efs->offset_to_drf_array) + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_drf_array)); + else + ntfs_log_error("There are no entries in the DRF " + "array.\n"); + } else + ntfs_log_error("Invalid DF type.\n"); + if (df_array) + fek = ntfs_df_array_fek_get(df_array, rsa_key); + free(efs); + return fek; +} + +/** + * ntfs_fek_decrypt_sector + */ +static int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) +{ + gcry_error_t err; + + err = gcry_cipher_reset(fek->gcry_cipher_hd); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to reset cipher: %s\n", + gcry_strerror(err)); + return -1; + } + /* + * Note: You may wonder why we are not calling gcry_cipher_setiv() here + * instead of doing it by hand after the decryption. The answer is + * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give + * it a length of 16 for AES256 so it does not like it. + */ + err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0); + if (err != GPG_ERR_NO_ERROR) { + ntfs_log_error("Decryption failed: %s\n", gcry_strerror(err)); + return -1; + } + /* Apply the IV. */ + if (fek->alg_id == CALG_AES_256) { + ((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); + ((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); + } else { + /* All other algorithms (Des, 3Des, DesX) use the same IV. */ + ((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); + } + return 512; +} + +/** + * ntfs_crypto_deinit - perform library-wide crypto deinitialization + */ +static void ntfs_crypto_deinit(void) +{ + int i; + + if (!ntfs_crypto_ctx.initialized) + return; + + for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++) + ntfs_rsa_private_key_release(ntfs_crypto_ctx.rsa_key[i]); + free(ntfs_crypto_ctx.rsa_key); + ntfs_crypto_ctx.rsa_key = NULL; + ntfs_crypto_ctx.nr_rsa_keys = 0; + gnutls_global_deinit(); + if (ntfs_crypto_ctx.desx_module) { + gcry_cipher_unregister(ntfs_crypto_ctx.desx_module); + ntfs_crypto_ctx.desx_module = NULL; + ntfs_crypto_ctx.desx_alg_id = -1; + } + ntfs_crypto_ctx.initialized = 0; +} + + +static void ntfs_crypto_parse_config(struct config_t *cfg) +{ + ntfs_crypto_ctx_t *ctx = &ntfs_crypto_ctx; + config_setting_t *cfg_keys, *cfg_key; + const char *pfx_file, *pfx_pwd; + ntfs_rsa_private_key_t *key; + u8 *pfx_buf; + unsigned pfx_size; + int i; + + /* Search for crypto.keys list. */ + cfg_keys = config_lookup(cfg, "crypto.keys"); + if (!cfg_keys) { + ntfs_log_error("Unable to find crypto.keys in config file.\n"); + return; + } + /* Iterate trough list of records about keys. */ + for (i = 0; (cfg_key = config_setting_get_elem(cfg_keys, i)); i++) { + /* Get path and password to key. */ + pfx_file = config_setting_get_string_elem(cfg_key, 0); + pfx_pwd = config_setting_get_string_elem(cfg_key, 1); + if (!pfx_file) { + ntfs_log_error("Entry number %d in section crypto.keys " + "of configuration file formed " + "incorrectly.\n", i + 1); + continue; + } + if (!pfx_pwd) + pfx_pwd = ""; + /* Load the PKCS#12 file containing the user's private key. */ + if (ntfs_pkcs12_load_pfxfile(pfx_file, &pfx_buf, &pfx_size)) { + ntfs_log_error("Failed to load key file %s.\n", + pfx_file); + continue; + } + /* + * Check whether we need to allocate memory for new key pointer. + * If yes, allocate memory for it and for 3 more pointers. + */ + if (!(ctx->nr_rsa_keys % 4)) { + ntfs_rsa_private_key_t **new; + + new = realloc(ctx->rsa_key, + sizeof(ntfs_rsa_private_key_t *) * + (ctx->nr_rsa_keys + 4)); + if (!new) { + ntfs_log_perror("Unable to store all keys"); + break; + } + ctx->rsa_key = new; + } + /* Obtain the user's private RSA key from the key file. */ + key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, pfx_pwd); + if (key) + ctx->rsa_key[ctx->nr_rsa_keys++] = key; + else + ntfs_log_error("Failed to obtain RSA key from %s\n", + pfx_file); + /* No longer need the pfx file contents. */ + free(pfx_buf); + } +} + + +static void ntfs_crypto_read_configs(void) +{ + struct config_t cfg; + char *home; + int fd = -1; + + config_init(&cfg); + /* Load system configuration file. */ + if (config_read_file(&cfg, NTFS_CONFIG_PATH_SYSTEM)) + ntfs_crypto_parse_config(&cfg); + else + if (config_error_line(&cfg)) /* Do not cry if file absent. */ + ntfs_log_error("Failed to read system configuration " + "file: %s (line %d).\n", + config_error_text(&cfg), + config_error_line(&cfg)); + /* Load user configuration file. */ + fd = open(".", O_RDONLY); /* Save current working directory. */ + if (fd == -1) { + ntfs_log_error("Failed to open working directory.\n"); + goto out; + } + home = getenv("HOME"); + if (!home) { + ntfs_log_error("Environment variable HOME is not set.\n"); + goto out; + } + if (chdir(home) == -1) { + ntfs_log_perror("chdir() to home directory failed"); + goto out; + } + if (config_read_file(&cfg, NTFS_CONFIG_PATH_USER)) + ntfs_crypto_parse_config(&cfg); + else + if (config_error_line(&cfg)) /* Do not cry if file absent. */ + ntfs_log_error("Failed to read user configuration " + "file: %s (line %d).\n", + config_error_text(&cfg), + config_error_line(&cfg)); + if (fchdir(fd) == -1) + ntfs_log_error("Failed to restore original working " + "directory.\n"); +out: + if (fd != -1) + close(fd); + config_destroy(&cfg); +} + +/** + * ntfs_crypto_init - perform library-wide crypto initializations + * + * This function is called during first call of ntfs_crypto_attr_open and + * performs gcrypt and GNU TLS initializations, then read list of PFX files + * from configuration files and load RSA keys from them. + */ +static int ntfs_crypto_init(void) +{ + int err; + + if (ntfs_crypto_ctx.initialized) + return 0; + + /* Initialize gcrypt library. Note: Must come before GNU TLS init. */ + if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) { + ntfs_log_error("Failed to initialize the gcrypt library.\n"); + return -1; + } + /* Initialize GNU TLS library. Note: Must come after libgcrypt init. */ + err = gnutls_global_init(); + if (err < 0) { + ntfs_log_error("Failed to initialize GNU TLS library: %s\n", + gnutls_strerror(err)); + return -1; + } + /* Read crypto related sections of libntfs configuration files. */ + ntfs_crypto_read_configs(); + + ntfs_crypto_ctx.initialized = 1; + atexit(ntfs_crypto_deinit); + return 0; +} + + +/** + * ntfs_crypto_attr_open - perform crypto related initialization for attribute + * @na: ntfs attribute to perform initialization for + * + * This function is called from ntfs_attr_open for encrypted attributes and + * tries to decrypt FEK enumerating all user submitted RSA keys. If we + * successfully obtained FEK, then @na->crypto is allocated and FEK stored + * inside. In the other case @na->crypto is set to NULL. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +int ntfs_crypto_attr_open(ntfs_attr *na) +{ + ntfs_fek *fek; + int i; + + na->crypto = NULL; + if (!na || !NAttrEncrypted(na)) { + errno = EINVAL; + return -1; + } + if (ntfs_crypto_init()) { + errno = EACCES; + return -1; + } + + for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++) { + fek = ntfs_inode_fek_get(na->ni, ntfs_crypto_ctx.rsa_key[i]); + if (fek) { + na->crypto = ntfs_malloc(sizeof(ntfs_crypto_attr)); + if (!na->crypto) + return -1; + na->crypto->fek = fek; + return 0; + } + } + + errno = EACCES; + return -1; +} + + +/** + * ntfs_crypto_attr_close - perform crypto related deinit for attribute + * @na: ntfs attribute to perform deinitialization for + * + * This function is called from ntfs_attr_close for encrypted attributes and + * frees memory that were allocated for it handling. + */ +void ntfs_crypto_attr_close(ntfs_attr *na) +{ + if (!na || !NAttrEncrypted(na)) + return; + + if (na->crypto) { + ntfs_fek_release(na->crypto->fek); + free(na->crypto); + } +} + + +/** + * ntfs_crypto_attr_pread - read from an encrypted attribute + * @na: ntfs attribute to read from + * @pos: byte position in the attribute to begin reading from + * @count: number of bytes to read + * @b: output data buffer + * + * This function is called from ntfs_attr_pread for encrypted attributes and + * should behave as described in ntfs_attr_pread description. + */ +s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) +{ + unsigned char *buffer; + s64 bytes_read, offset, total, length; + int i; + + if (!na || pos < 0 || count < 0 || !b || !NAttrEncrypted(na)) { + errno = EINVAL; + return -1; + } + if (!count) + return 0; + + if (!na->crypto) { + errno = EACCES; + return -1; + } + + buffer = malloc(NTFS_EFS_SECTOR_SIZE); + if (!buffer) + return -1; + + ntfs_attr_map_runlist_range(na, pos >> na->ni->vol->cluster_size_bits, + (pos + count - 1) >> na->ni->vol->cluster_size_bits); + + total = 0; + offset = ROUND_DOWN(pos, 9); + while (total < count && offset < na->data_size) { + /* Calculate number of bytes we actually want. */ + length = NTFS_EFS_SECTOR_SIZE; + if (offset + length > pos + count) + length = pos + count - offset; + if (offset + length > na->data_size) + length = na->data_size - offset; + + if (length < 0) { + total = -1; + errno = EIO; + ntfs_log_error("LIBRARY BUG!!! Please report that you " + "saw this message to %s. Thanks!", + NTFS_DEV_LIST); + break; + } + + /* Just write zeros if @offset fully beyond initialized size. */ + if (offset >= na->initialized_size) { + memset(b + total, 0, length); + total += length; + continue; + } + + bytes_read = ntfs_rl_pread(na->ni->vol, na->rl, offset, + NTFS_EFS_SECTOR_SIZE, buffer); + if (!bytes_read) + break; + if (bytes_read != NTFS_EFS_SECTOR_SIZE) { + ntfs_log_perror("%s(): ntfs_rl_pread returned %lld " + "bytes", __FUNCTION__, bytes_read); + break; + } + if ((i = ntfs_fek_decrypt_sector(na->crypto->fek, buffer, + offset)) < bytes_read) { + ntfs_log_error("%s(): Couldn't decrypt all data " + "(%u/%lld/%lld/%lld)!", __FUNCTION__, + i, (long long)bytes_read, + (long long)offset, (long long)total); + break; + } + + /* Handle partially in initialized size situation. */ + if (offset + length > na->initialized_size) + memset(buffer + (na->initialized_size - offset), 0, + offset + length - na->initialized_size); + + if (offset >= pos) + memcpy(b + total, buffer, length); + else { + length -= (pos - offset); + memcpy(b + total, buffer + (pos - offset), length); + } + total += length; + offset += bytes_read; + } + + free(buffer); + return total; +} + +#else /* !ENABLE_CRYPTO */ + +/* Stubs for crypto-disabled version of libntfs. */ + +int ntfs_crypto_attr_open(ntfs_attr *na) +{ + na->crypto = NULL; + errno = EACCES; + return -1; +} + +void ntfs_crypto_attr_close(ntfs_attr *na) +{ +} + +s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, + void *b) +{ + errno = EACCES; + return -1; +} + +#endif /* !ENABLE_CRYPTO */ + diff --git a/libntfs/freebsd_io.c b/libntfs/freebsd_io.c new file mode 100644 index 00000000..f5e96c00 --- /dev/null +++ b/libntfs/freebsd_io.c @@ -0,0 +1,546 @@ +/** + * freebsd_io.c - FreeBSD disk io functions. Part of the Linux-NTFS project. + * + * FreeBSD 5.0+ does not have block devices and requires read/writes from/to + * character devices to be sector aligned. + * + * Copyright (c) 2006 Max Khon + * Copyright (c) 2006 Anton Altaparmakov + * Copyright (c) 2006 Yura Pakhuchiy + * + * This program/include file 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/include file 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 NTFS-3G + * distribution 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 +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_LINUX_FD_H +#include +#endif +#include + +#include "types.h" +#include "mst.h" +#include "debug.h" +#include "device.h" +#include "logging.h" + +typedef struct { + int fd; + s64 pos; + s32 block_size; + s64 media_size; +} freebsd_fd; + +#define DEV_FD(dev) (((freebsd_fd *) dev->d_private)) +#define RAW_IO_ALIGNED(dev, offset, count) \ + (DEV_FD(dev)->block_size == 0 || \ + ((offset) % DEV_FD(dev)->block_size == 0 && \ + (count) % DEV_FD(dev)->block_size == 0)) +#define RAW_IO_ALIGN(dev, offset) \ + ((offset) / DEV_FD(dev)->block_size * DEV_FD(dev)->block_size) +#define RAW_IO_MAX_SIZE (128 * 1024 * 1024) + +/* Define to nothing if not present on this system. */ +#ifndef O_EXCL +# define O_EXCL 0 +#endif + +/** + * Get block_size and media_size + */ +static int freebsd_get_size(struct ntfs_device *dev) +{ + off_t ms; + int bs; + struct stat sb; + + if (fstat(DEV_FD(dev)->fd, &sb) < 0) { + ntfs_log_perror("Failed to stat '%s'", dev->d_name); + return -1; + } + if (S_ISREG(sb.st_mode)) { + DEV_FD(dev)->media_size = sb.st_size; + ntfs_log_trace("%s: regular file (media_size %lld)\n", + dev->d_name, DEV_FD(dev)->media_size); + return 0; + } + if (ioctl(DEV_FD(dev)->fd, DIOCGSECTORSIZE, &bs) < 0) { + ntfs_log_perror("Failed to ioctl(DIOCGSECTORSIZE) '%s'", + dev->d_name); + return -1; + } + DEV_FD(dev)->block_size = bs; + ntfs_log_trace("%s: block size %d\n", dev->d_name, bs); + if (ioctl(DEV_FD(dev)->fd, DIOCGMEDIASIZE, &ms) < 0) { + ntfs_log_perror("Failed to ioctl(DIOCGMEDIASIZE) '%s'", + dev->d_name); + return -1; + } + DEV_FD(dev)->media_size = ms; + ntfs_log_trace("%s: media size %lld\n", dev->d_name, ms); + return 0; +} + +/** + * freebsd_pread - Aligned read + */ +static inline ssize_t freebsd_pread(struct ntfs_device *dev, char *buf, + size_t count, s64 offset) +{ + return pread(DEV_FD(dev)->fd, buf, count, offset); +} + +/** + * freebsd_pwrite - Aligned write + */ +static inline ssize_t freebsd_pwrite(struct ntfs_device *dev, const char *buf, + size_t count, s64 offset) +{ + return pwrite(DEV_FD(dev)->fd, buf, count, offset); +} + +/** + * ntfs_device_freebsd_io_open - Open a device and lock it exclusively + * @dev: + * @flags: + * + * Description... + * + * Returns: + */ +static int ntfs_device_freebsd_io_open(struct ntfs_device *dev, int flags) +{ +#if 0 + struct flock flk; +#endif + struct stat sbuf; + int err; + + if (NDevOpen(dev)) { + errno = EBUSY; + return -1; + } + if (stat(dev->d_name, &sbuf)) { + ntfs_log_perror("Failed to access '%s'", dev->d_name); + return -1; + } + if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode)) + NDevSetBlock(dev); + + dev->d_private = malloc(sizeof(freebsd_fd)); + if (!dev->d_private) + return -1; + DEV_FD(dev)->fd = -1; + DEV_FD(dev)->pos = 0; + DEV_FD(dev)->block_size = 0; + DEV_FD(dev)->media_size = 0; + + DEV_FD(dev)->fd = open(dev->d_name, flags); + if (DEV_FD(dev)->fd == -1) { + err = errno; + goto err_out; + } + + if ((flags & O_RDWR) != O_RDWR) + NDevSetReadOnly(dev); + +#if 0 + memset(&flk, 0, sizeof(flk)); + if (NDevReadOnly(dev)) + flk.l_type = F_RDLCK; + else + flk.l_type = F_WRLCK; + flk.l_whence = SEEK_SET; + flk.l_start = flk.l_len = 0LL; + if (fcntl(DEV_FD(dev)->fd, F_SETLK, &flk)) { + err = errno; + ntfs_log_perror("Failed to %s lock '%s'", NDevReadOnly(dev) ? + "read" : "write", dev->d_name); + if (close(DEV_FD(dev)->fd)) + ntfs_log_perror("Failed to close '%s'", dev->d_name); + goto err_out; + } +#endif + + if (freebsd_get_size(dev) < 0) { + err = errno; + close(DEV_FD(dev)->fd); + goto err_out; + } + + NDevSetOpen(dev); + return 0; +err_out: + free(dev->d_private); + dev->d_private = NULL; + errno = err; + return -1; +} + +/** + * ntfs_device_freebsd_io_close - Close the device, releasing the lock + * @dev: + * + * Description... + * + * Returns: + */ +static int ntfs_device_freebsd_io_close(struct ntfs_device *dev) +{ +#if 0 + struct flock flk; +#endif + + if (!NDevOpen(dev)) { + errno = EBADF; + return -1; + } + if (NDevDirty(dev)) + fsync(DEV_FD(dev)->fd); + +#if 0 + /* Release exclusive (mandatory) lock on the whole device. */ + memset(&flk, 0, sizeof(flk)); + flk.l_type = F_UNLCK; + flk.l_whence = SEEK_SET; + flk.l_start = flk.l_len = 0LL; + if (fcntl(DEV_FD(dev)->fd, F_SETLK, &flk)) + ntfs_log_perror("ntfs_device_freebsd_io_close: Warning: Could not " + "unlock %s", dev->d_name); +#endif + + /* Close the file descriptor and clear our open flag. */ + if (close(DEV_FD(dev)->fd)) + return -1; + NDevClearOpen(dev); + free(dev->d_private); + dev->d_private = NULL; + return 0; +} + +/** + * ntfs_device_freebsd_io_seek - Seek to a place on the device + * @dev: + * @offset: + * @whence: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_freebsd_io_seek(struct ntfs_device *dev, s64 offset, + int whence) +{ + s64 abs_pos; + + ntfs_log_trace("seek offset = 0x%llx, whence = %d.\n", offset, whence); + switch (whence) { + case SEEK_SET: + abs_pos = offset; + break; + + case SEEK_CUR: + abs_pos = DEV_FD(dev)->pos + offset; + break; + + case SEEK_END: + abs_pos = DEV_FD(dev)->media_size + offset; + break; + + default: + ntfs_log_trace("Wrong mode %d.\n", whence); + errno = EINVAL; + return -1; + } + + if (abs_pos < 0 || abs_pos > DEV_FD(dev)->media_size) { + ntfs_log_trace("Seeking outsize seekable area.\n"); + errno = EINVAL; + return -1; + } + DEV_FD(dev)->pos = abs_pos; + return abs_pos; +} + +/** + * ntfs_device_freebsd_io_read - Read from the device, from the current location + * @dev: + * @buf: + * @count: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_freebsd_io_read(struct ntfs_device *dev, void *buf, + s64 count) +{ + s64 start, start_aligned; + s64 end, end_aligned; + size_t count_aligned; + char *buf_aligned; + ssize_t nr; + + /* short-circuit for regular files */ + start = DEV_FD(dev)->pos; + if (count > RAW_IO_MAX_SIZE) + count = RAW_IO_MAX_SIZE; + if (RAW_IO_ALIGNED(dev, start, count)) { + nr = freebsd_pread(dev, buf, count, start); + if (nr <= 0) + return nr; + + DEV_FD(dev)->pos += nr; + return nr; + } + + /* + * +- start_aligned +- end_aligned + * | | + * | +- start +- end | + * v v v v + * |----------|----------|----------| + * ^ ^ + * +----- count ------+ + * ^ ^ + * +-------- count_aligned ---------+ + */ + start_aligned = RAW_IO_ALIGN(dev, start); + end = start + count; + end_aligned = RAW_IO_ALIGN(dev, end) + + (RAW_IO_ALIGNED(dev, end, 0) ? 0 : DEV_FD(dev)->block_size); + count_aligned = end_aligned - start_aligned; + ntfs_log_trace( + "%s: count = 0x%llx/0x%x, start = 0x%llx/0x%llx, end = 0x%llx/0x%llx\n", + dev->d_name, count, count_aligned, + start, start_aligned, end, end_aligned); + + /* allocate buffer */ + buf_aligned = malloc(count_aligned); + if (buf_aligned == NULL) { + ntfs_log_trace("malloc(%d) failed\n", count_aligned); + return -1; + } + + /* read aligned data */ + nr = freebsd_pread(dev, buf_aligned, count_aligned, start_aligned); + if (nr == 0) + return 0; + if (nr < 0 || nr < start - start_aligned) { + free(buf_aligned); + return -1; + } + + /* copy out */ + memcpy(buf, buf_aligned + (start - start_aligned), count); + free(buf_aligned); + + nr -= start - start_aligned; + if (nr > count) + nr = count; + DEV_FD(dev)->pos += nr; + return nr; +} + +/** + * ntfs_device_freebsd_io_write - Write to the device, at the current location + * @dev: + * @buf: + * @count: + * + * Description... + * + * Returns: + */ +static s64 ntfs_device_freebsd_io_write(struct ntfs_device *dev, const void *buf, + s64 count) +{ + s64 start, start_aligned; + s64 end, end_aligned; + size_t count_aligned; + char *buf_aligned; + ssize_t nw; + + if (NDevReadOnly(dev)) { + errno = EROFS; + return -1; + } + NDevSetDirty(dev); + + /* short-circuit for regular files */ + start = DEV_FD(dev)->pos; + if (count > RAW_IO_MAX_SIZE) + count = RAW_IO_MAX_SIZE; + if (RAW_IO_ALIGNED(dev, start, count)) { + nw = freebsd_pwrite(dev, buf, count, start); + if (nw <= 0) + return nw; + + DEV_FD(dev)->pos += nw; + return nw; + } + + /* + * +- start_aligned +- end_aligned + * | | + * | +- start +- end | + * v v v v + * |----------|----------|----------| + * ^ ^ + * +----- count ------+ + * ^ ^ + * +-------- count_aligned ---------+ + */ + start_aligned = RAW_IO_ALIGN(dev, start); + end = start + count; + end_aligned = RAW_IO_ALIGN(dev, end) + + (RAW_IO_ALIGNED(dev, end, 0) ? 0 : DEV_FD(dev)->block_size); + count_aligned = end_aligned - start_aligned; + ntfs_log_trace( + "%s: count = 0x%llx/0x%x, start = 0x%llx/0x%llx, end = 0x%llx/0x%llx\n", + dev->d_name, count, count_aligned, + start, start_aligned, end, end_aligned); + + /* allocate buffer */ + buf_aligned = malloc(count_aligned); + if (buf_aligned == NULL) { + ntfs_log_trace("malloc(%d) failed\n", count_aligned); + return -1; + } + + /* read aligned lead-in */ + if (freebsd_pread(dev, buf_aligned, DEV_FD(dev)->block_size, start_aligned) != DEV_FD(dev)->block_size) { + ntfs_log_trace("read lead-in failed\n"); + free(buf_aligned); + return -1; + } + + /* read aligned lead-out */ + if (end != end_aligned && count_aligned > DEV_FD(dev)->block_size) { + if (freebsd_pread(dev, buf_aligned + count_aligned - DEV_FD(dev)->block_size, DEV_FD(dev)->block_size, end_aligned - DEV_FD(dev)->block_size) != DEV_FD(dev)->block_size) { + ntfs_log_trace("read lead-out failed\n"); + free(buf_aligned); + return -1; + } + } + + /* copy data to write */ + memcpy(buf_aligned + (start - start_aligned), buf, count); + + /* write aligned data */ + nw = freebsd_pwrite(dev, buf_aligned, count_aligned, start_aligned); + free(buf_aligned); + if (nw < 0 || nw < start - start_aligned) + return -1; + + nw -= start - start_aligned; + if (nw > count) + nw = count; + DEV_FD(dev)->pos += nw; + return nw; +} + +/** + * ntfs_device_freebsd_io_sync - Flush any buffered changes to the device + * @dev: + * + * Description... + * + * Returns: + */ +static int ntfs_device_freebsd_io_sync(struct ntfs_device *dev) +{ + if (!NDevReadOnly(dev)) { + int res = fsync(DEV_FD(dev)->fd); + if (!res) + NDevClearDirty(dev); + return res; + } + return 0; +} + +/** + * ntfs_device_freebsd_io_stat - Get information about the device + * @dev: + * @buf: + * + * Description... + * + * Returns: + */ +static int ntfs_device_freebsd_io_stat(struct ntfs_device *dev, struct stat *buf) +{ + return fstat(DEV_FD(dev)->fd, buf); +} + +/** + * ntfs_device_freebsd_io_ioctl - Perform an ioctl on the device + * @dev: + * @request: + * @argp: + * + * Description... + * + * Returns: + */ +static int ntfs_device_freebsd_io_ioctl(struct ntfs_device *dev, int request, + void *argp) +{ + return ioctl(DEV_FD(dev)->fd, request, argp); +} + +/** + * Device operations for working with unix style devices and files. + */ +struct ntfs_device_operations ntfs_device_unix_io_ops = { + .open = ntfs_device_freebsd_io_open, + .close = ntfs_device_freebsd_io_close, + .seek = ntfs_device_freebsd_io_seek, + .read = ntfs_device_freebsd_io_read, + .write = ntfs_device_freebsd_io_write, + .sync = ntfs_device_freebsd_io_sync, + .stat = ntfs_device_freebsd_io_stat, + .ioctl = ntfs_device_freebsd_io_ioctl, +}; diff --git a/libntfs/libntfs.8.in b/libntfs/libntfs.8.in new file mode 100644 index 00000000..f908f7cb --- /dev/null +++ b/libntfs/libntfs.8.in @@ -0,0 +1,34 @@ +.\" Copyright (c) 2007 Yura Pakhuchiy +.\" This file may be copied under the terms of the GNU Public License. +.TH LIBNTFS 8 "September 2007" "ntfsprogs @VERSION@" +.SH NAME +libntfs \- library for accessing and managing NTFS volumes +.SH OVERVIEW +\fBlibntfs\fR is GPL licensed library for accessing and managing NTFS volumes. +It is used by\fB ntfsprogs\fR and some other projects. +.SH ACCESSING ENCRYPTED FILES +Programs that uses\fB libntfs\fR can transparently access encrypted files on +NTFS volumes if\fB libntfs\fR was compiled with \fB--enable-crypto\fR option +(it depends on\fB libgcrypt\fR,\fB GNU TLS\fR and \fBlibconfig\fR) and user +wrote configuration file. Configuration file should be placed in +\fB/etc/libntfs/config\fR or \fB$(HOME)/.libntfs/config\fR and contain list +of .PFX key files (see ntfsprogs-/libntfs/config in ntfsprogs source +tarball for sample configuration file). Key files can be created/exported +using\fB cipher\fR tool under windows. +.SH AUTHORS +\fBlibntfs\fR was written by Anton Altaparmakov, Richard Russon, Szabolcs Szakacsits, Yuval Fledel and Yura Pakhuchiy. +.SH AVAILABILITY +The \fBntfsprogs\fR can be downloaded from: +.br +.nh +http://www.linux\-ntfs.org/content/view/19/37 +.hy +.sp +These manual pages can be viewed online at: +.br +.nh +http://man.linux-ntfs.org/ +.hy +.SH SEE ALSO +.BR ntfsprogs (8), +.BR libntfs\-gnomevfs (8) diff --git a/libntfs/misc.c b/libntfs/misc.c new file mode 100644 index 00000000..22d3784c --- /dev/null +++ b/libntfs/misc.c @@ -0,0 +1,63 @@ +/** + * misc.c - Miscellaneous functions. Part of the Linux-NTFS project. + * + * Copyright (c) 2006 Szabolcs Szakacsits + * + * This program/include file 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/include file 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 + * distribution 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_STDLIB_H +#include +#endif + +#include "support.h" +#include "logging.h" + +/** + * ntfs_calloc - A logging supported calloc(3) + * + * Return a pointer to the allocated memory or NULL if the request fails. + * Memory is initialized with zeros. + */ +void *ntfs_calloc(size_t size) +{ + void *p; + + p = calloc(1, size); + if (!p) + ntfs_log_perror("Failed to calloc %lld bytes", (long long)size); + return p; +} + +/** + * ntfs_malloc - A logging supported malloc(3) + * + * Return a pointer to the allocated memory or NULL if the request fails. + * Memory is uninitialized. + */ +void *ntfs_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if (!p) + ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); + return p; +} diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am index 15f7eaf6..b36d531b 100644 --- a/ntfsprogs/Makefile.am +++ b/ntfsprogs/Makefile.am @@ -17,7 +17,7 @@ bin_PROGRAMS = ntfsfix ntfsinfo ntfscluster ntfsls ntfscat ntfscmp sbin_PROGRAMS = mkntfs ntfslabel ntfsundelete ntfsresize ntfsclone \ ntfscp EXTRA_PROGRAMS = ntfsdump_logfile ntfswipe ntfstruncate ntfsmove \ - ntfsmftalloc + ntfsmftalloc ntfsck man_MANS = mkntfs.8 ntfsfix.8 ntfslabel.8 ntfsinfo.8 \ ntfsundelete.8 ntfsresize.8 ntfsprogs.8 ntfsls.8 \ @@ -40,8 +40,9 @@ ntfsfix_SOURCES = ntfsfix.c utils.c utils.h ntfsfix_LDADD = $(AM_LIBS) ntfsfix_LDFLAGS = $(AM_LFLAGS) -mkntfs_SOURCES = attrdef.c attrdef.h upcase.c upcase.h boot.c boot.h sd.c sd.h mkntfs.c utils.c utils.h -mkntfs_LDADD = $(AM_LIBS) +mkntfs_CPPFLAGS = $(AM_CPPFLAGS) $(MKNTFS_CPPFLAGS) +mkntfs_SOURCES = attrdef.c attrdef.h boot.c boot.h sd.c sd.h mkntfs.c utils.c utils.h +mkntfs_LDADD = $(AM_LIBS) $(MKNTFS_LIBS) mkntfs_LDFLAGS = $(AM_LFLAGS) ntfslabel_SOURCES = ntfslabel.c utils.c utils.h @@ -80,6 +81,10 @@ ntfscp_SOURCES = ntfscp.c utils.c utils.h ntfscp_LDADD = $(AM_LIBS) ntfscp_LDFLAGS = $(AM_LFLAGS) +ntfsck_SOURCES = ntfsck.c utils.c utils.h +ntfsck_LDADD = $(AM_LIBS) +ntfsck_LDFLAGS = $(AM_LFLAGS) + ntfscmp_SOURCES = ntfscmp.c utils.c utils.h ntfscmp_LDADD = $(AM_LIBS) ntfscmp_LDFLAGS = $(AM_LFLAGS) @@ -108,8 +113,9 @@ ntfsdump_logfile_LDFLAGS= $(AM_LFLAGS) if ENABLE_CRYPTO ntfsdecrypt_SOURCES = ntfsdecrypt.c utils.c utils.h -ntfsdecrypt_LDADD = $(AM_LIBS) -ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) -lgcrypt -lgnutls +ntfsdecrypt_LDADD = $(AM_LIBS) $(GNUTLS_LIBS) $(LIBGCRYPT_LIBS) +ntfsdecrypt_LDFLAGS = $(AM_LFLAGS) +ntfsdecrypt_CFLAGS = $(GNUTLS_CFLAGS) $(LIBGCRYPT_CFLAGS) endif # Extra targets diff --git a/ntfsprogs/attrdef.c b/ntfsprogs/attrdef.c index 4ffa5eeb..36501e5c 100644 --- a/ntfsprogs/attrdef.c +++ b/ntfsprogs/attrdef.c @@ -1,158 +1,4 @@ -/** - * attrdef_ntfs12_array - */ -const unsigned char attrdef_ntfs12_array[2400] = { - 36, 0, 83, 0, 84, 0, 65, 0, 78, 0, 68, 0, 65, 0, 82, 0, - 68, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, 82, 0, 77, 0, - 65, 0, 84, 0, 73, 0, 79, 0, 78, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 48, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 65, 0, 84, 0, 84, 0, 82, 0, 73, 0, 66, 0, 85, 0, - 84, 0, 69, 0, 95, 0, 76, 0, 73, 0, 83, 0, 84, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 70, 0, 73, 0, 76, 0, 69, 0, 95, 0, 78, 0, 65, 0, - 77, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, - 68, 0, 0, 0, 0, 0, 0, 0, 66, 2, 0, 0, 0, 0, 0, 0, - 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, - 86, 0, 69, 0, 82, 0, 83, 0, 73, 0, 79, 0, 78, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 83, 0, 69, 0, 67, 0, 85, 0, 82, 0, 73, 0, 84, 0, - 89, 0, 95, 0, 68, 0, 69, 0, 83, 0, 67, 0, 82, 0, 73, 0, - 80, 0, 84, 0, 79, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, - 78, 0, 65, 0, 77, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 36, 0, 86, 0, 79, 0, 76, 0, 85, 0, 77, 0, 69, 0, 95, 0, - 73, 0, 78, 0, 70, 0, 79, 0, 82, 0, 77, 0, 65, 0, 84, 0, - 73, 0, 79, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 68, 0, 65, 0, 84, 0, 65, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 73, 0, 78, 0, 68, 0, 69, 0, 88, 0, 95, 0, 82, 0, - 79, 0, 79, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 73, 0, 78, 0, 68, 0, 69, 0, 88, 0, 95, 0, 65, 0, - 76, 0, 76, 0, 79, 0, 67, 0, 65, 0, 84, 0, 73, 0, 79, 0, - 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 66, 0, 73, 0, 84, 0, 77, 0, 65, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 83, 0, 89, 0, 77, 0, 66, 0, 79, 0, 76, 0, 73, 0, - 67, 0, 95, 0, 76, 0, 73, 0, 78, 0, 75, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 36, 0, 69, 0, 65, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, - 82, 0, 77, 0, 65, 0, 84, 0, 73, 0, 79, 0, 78, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, - 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, - 36, 0, 69, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; +#include "attrdef.h" /** * attrdef_ntfs3x_array diff --git a/ntfsprogs/attrdef.h b/ntfsprogs/attrdef.h index 09740fab..0c664394 100644 --- a/ntfsprogs/attrdef.h +++ b/ntfsprogs/attrdef.h @@ -1,7 +1,6 @@ #ifndef _NTFS_ATTRDEF_H_ #define _NTFS_ATTRDEF_H_ -extern const unsigned char attrdef_ntfs12_array[2400]; extern const unsigned char attrdef_ntfs3x_array[2560]; #endif /* _NTFS_ATTRDEF_H_ */ diff --git a/ntfsprogs/boot.c b/ntfsprogs/boot.c index f0535ffd..9272be9f 100644 --- a/ntfsprogs/boot.c +++ b/ntfsprogs/boot.c @@ -1,222 +1,268 @@ -/** - * boot_array - the first 3429 bytes of $Boot - * The first 3429 bytes of $Boot. The rest is just zero. Total 8192 bytes. - */ -const unsigned char boot_array[3429] = { -235, 91, 144, 78, 84, 70, 83, 32, 32, 32, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 51, 192, -142, 208, 188, 0, 124, 251, 184, 192, 7, 142, 216, 199, 6, 84, 0, 0, - 0, 199, 6, 86, 0, 0, 0, 199, 6, 91, 0, 16, 0, 184, 0, 13, -142, 192, 43, 219, 232, 7, 0, 104, 0, 13, 104, 102, 2, 203, 80, 83, - 81, 82, 6, 102, 161, 84, 0, 102, 3, 6, 28, 0, 102, 51, 210, 102, - 15, 183, 14, 24, 0, 102, 247, 241, 254, 194, 136, 22, 90, 0, 102, 139, -208, 102, 193, 234, 16, 247, 54, 26, 0, 136, 22, 37, 0, 163, 88, 0, -161, 24, 0, 42, 6, 90, 0, 64, 59, 6, 91, 0, 118, 3, 161, 91, - 0, 80, 180, 2, 139, 22, 88, 0, 177, 6, 210, 230, 10, 54, 90, 0, -139, 202, 134, 233, 138, 54, 37, 0, 178, 128, 205, 19, 88, 114, 42, 1, - 6, 84, 0, 131, 22, 86, 0, 0, 41, 6, 91, 0, 118, 11, 193, 224, - 5, 140, 194, 3, 208, 142, 194, 235, 138, 7, 90, 89, 91, 88, 195, 190, - 89, 1, 235, 8, 190, 227, 1, 235, 3, 190, 57, 1, 232, 9, 0, 190, -173, 1, 232, 3, 0, 251, 235, 254, 172, 60, 0, 116, 9, 180, 14, 187, - 7, 0, 205, 16, 235, 242, 195, 29, 0, 65, 32, 100, 105, 115, 107, 32, -114, 101, 97, 100, 32, 101, 114, 114, 111, 114, 32, 111, 99, 99, 117, 114, -114, 101, 100, 46, 13, 10, 0, 41, 0, 65, 32, 107, 101, 114, 110, 101, -108, 32, 102, 105, 108, 101, 32, 105, 115, 32, 109, 105, 115, 115, 105, 110, -103, 32, 102, 114, 111, 109, 32, 116, 104, 101, 32, 100, 105, 115, 107, 46, - 13, 10, 0, 37, 0, 65, 32, 107, 101, 114, 110, 101, 108, 32, 102, 105, -108, 101, 32, 105, 115, 32, 116, 111, 111, 32, 100, 105, 115, 99, 111, 110, -116, 105, 103, 117, 111, 117, 115, 46, 13, 10, 0, 51, 0, 73, 110, 115, -101, 114, 116, 32, 97, 32, 115, 121, 115, 116, 101, 109, 32, 100, 105, 115, -107, 101, 116, 116, 101, 32, 97, 110, 100, 32, 114, 101, 115, 116, 97, 114, -116, 13, 10, 116, 104, 101, 32, 115, 121, 115, 116, 101, 109, 46, 13, 10, - 0, 23, 0, 92, 78, 84, 76, 68, 82, 32, 105, 115, 32, 99, 111, 109, -112, 114, 101, 115, 115, 101, 100, 46, 13, 10, 0, 0, 0, 0, 85, 170, - 5, 0, 78, 0, 84, 0, 76, 0, 68, 0, 82, 0, 4, 0, 36, 0, - 73, 0, 51, 0, 48, 0, 0, 224, 0, 0, 0, 48, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 140, 200, 142, 216, 193, 224, 4, 250, 139, 224, -251, 102, 15, 183, 6, 11, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, -102, 163, 78, 2, 102, 139, 14, 64, 0, 128, 249, 0, 15, 143, 14, 0, -246, 217, 102, 184, 1, 0, 0, 0, 102, 211, 224, 235, 8, 144, 102, 161, - 78, 2, 102, 247, 225, 102, 163, 82, 2, 102, 15, 183, 30, 11, 0, 102, - 51, 210, 102, 247, 243, 102, 163, 86, 2, 232, 44, 4, 102, 139, 14, 74, - 2, 102, 137, 14, 34, 2, 102, 3, 14, 82, 2, 102, 137, 14, 38, 2, -102, 3, 14, 82, 2, 102, 137, 14, 42, 2, 102, 3, 14, 82, 2, 102, -137, 14, 58, 2, 102, 3, 14, 82, 2, 102, 137, 14, 66, 2, 102, 184, -144, 0, 0, 0, 102, 139, 14, 34, 2, 232, 65, 9, 102, 11, 192, 15, -132, 22, 254, 102, 163, 46, 2, 102, 184, 160, 0, 0, 0, 102, 139, 14, - 38, 2, 232, 40, 9, 102, 163, 50, 2, 102, 184, 176, 0, 0, 0, 102, -139, 14, 42, 2, 232, 22, 9, 102, 163, 54, 2, 102, 161, 46, 2, 102, - 11, 192, 15, 132, 227, 253, 103, 128, 120, 8, 0, 15, 133, 218, 253, 103, -102, 141, 80, 16, 103, 3, 66, 4, 103, 102, 15, 182, 72, 12, 102, 137, - 14, 94, 2, 103, 102, 139, 72, 8, 102, 137, 14, 90, 2, 102, 161, 90, - 2, 102, 15, 183, 14, 11, 0, 102, 51, 210, 102, 247, 241, 102, 163, 98, - 2, 102, 161, 66, 2, 102, 3, 6, 90, 2, 102, 163, 70, 2, 102, 131, - 62, 50, 2, 0, 15, 132, 25, 0, 102, 131, 62, 54, 2, 0, 15, 132, -135, 253, 102, 139, 30, 54, 2, 30, 7, 102, 139, 62, 70, 2, 232, 177, - 1, 102, 15, 183, 14, 0, 2, 102, 184, 2, 2, 0, 0, 232, 153, 6, -102, 11, 192, 15, 132, 88, 253, 103, 102, 139, 0, 30, 7, 102, 139, 62, - 58, 2, 232, 209, 4, 102, 161, 58, 2, 102, 187, 128, 0, 0, 0, 102, -185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 203, 0, 102, 11, -192, 15, 132, 42, 253, 103, 102, 15, 183, 88, 12, 102, 129, 227, 255, 0, - 0, 0, 15, 133, 30, 253, 102, 139, 216, 104, 0, 32, 7, 102, 43, 255, -232, 79, 1, 138, 22, 36, 0, 184, 232, 3, 142, 192, 141, 54, 11, 0, - 43, 192, 104, 0, 32, 80, 203, 80, 83, 81, 82, 6, 255, 54, 91, 0, -255, 54, 84, 0, 255, 54, 86, 0, 139, 195, 193, 232, 4, 140, 193, 3, -193, 37, 255, 15, 45, 0, 16, 247, 216, 139, 14, 91, 0, 193, 225, 5, - 81, 59, 193, 118, 2, 139, 193, 80, 193, 232, 5, 163, 91, 0, 232, 61, -252, 88, 89, 43, 200, 118, 11, 140, 194, 3, 208, 142, 194, 184, 0, 16, -235, 222, 143, 6, 86, 0, 143, 6, 84, 0, 143, 6, 91, 0, 7, 90, - 89, 91, 88, 195, 6, 30, 102, 96, 102, 139, 218, 102, 15, 182, 14, 13, - 0, 102, 247, 225, 102, 163, 84, 0, 102, 139, 195, 102, 247, 225, 163, 91, - 0, 139, 223, 131, 227, 15, 140, 192, 102, 193, 239, 4, 3, 199, 80, 7, -232, 116, 255, 102, 97, 144, 31, 7, 195, 103, 3, 64, 20, 103, 102, 131, - 56, 255, 15, 132, 76, 0, 103, 102, 57, 24, 15, 133, 51, 0, 102, 11, -201, 15, 133, 10, 0, 103, 128, 120, 9, 0, 15, 133, 35, 0, 195, 103, - 58, 72, 9, 15, 133, 26, 0, 102, 139, 240, 103, 3, 112, 10, 232, 61, - 5, 102, 81, 30, 7, 102, 139, 250, 243, 167, 102, 89, 15, 133, 1, 0, -195, 103, 102, 131, 120, 4, 0, 15, 132, 7, 0, 103, 102, 3, 64, 4, -235, 171, 102, 43, 192, 195, 102, 139, 243, 232, 18, 5, 103, 102, 3, 0, -103, 247, 64, 12, 2, 0, 15, 133, 52, 0, 103, 102, 141, 80, 16, 103, - 58, 74, 64, 15, 133, 24, 0, 103, 102, 141, 114, 66, 232, 239, 4, 102, - 81, 30, 7, 102, 139, 251, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, -131, 120, 8, 0, 15, 132, 6, 0, 103, 3, 64, 8, 235, 194, 102, 51, -192, 195, 103, 128, 123, 8, 0, 15, 133, 28, 0, 6, 30, 102, 96, 103, -102, 141, 83, 16, 103, 102, 139, 10, 102, 139, 243, 103, 3, 114, 4, 243, -164, 102, 97, 144, 31, 7, 195, 103, 102, 141, 83, 16, 103, 102, 139, 74, - 8, 102, 65, 102, 43, 192, 232, 1, 0, 195, 6, 30, 102, 96, 103, 128, -123, 8, 1, 15, 132, 3, 0, 233, 127, 251, 102, 131, 249, 0, 15, 133, - 6, 0, 102, 97, 144, 31, 7, 195, 102, 83, 102, 80, 102, 81, 102, 87, - 6, 232, 87, 3, 102, 139, 209, 7, 102, 95, 102, 89, 102, 59, 202, 15, -141, 3, 0, 102, 139, 209, 232, 171, 254, 102, 43, 202, 102, 139, 218, 102, -139, 194, 102, 15, 182, 22, 13, 0, 102, 247, 226, 102, 15, 183, 22, 11, - 0, 102, 247, 226, 102, 3, 248, 102, 88, 102, 3, 195, 102, 91, 235, 170, - 6, 30, 102, 96, 103, 128, 123, 8, 1, 15, 132, 3, 0, 233, 25, 251, -102, 131, 249, 0, 15, 133, 6, 0, 102, 97, 144, 31, 7, 195, 102, 83, -102, 80, 102, 81, 102, 87, 6, 102, 81, 102, 51, 210, 102, 15, 182, 14, - 13, 0, 102, 247, 241, 102, 82, 232, 225, 2, 102, 15, 182, 30, 13, 0, -102, 247, 227, 102, 90, 102, 3, 194, 102, 80, 102, 15, 182, 6, 13, 0, -102, 247, 225, 102, 139, 208, 102, 88, 102, 89, 7, 102, 95, 102, 89, 102, - 59, 202, 15, 141, 3, 0, 102, 139, 209, 102, 163, 84, 0, 137, 22, 91, - 0, 6, 30, 102, 96, 139, 223, 131, 227, 15, 140, 192, 102, 193, 239, 4, - 3, 199, 80, 7, 232, 160, 253, 102, 97, 144, 31, 7, 102, 43, 202, 102, -139, 218, 102, 139, 194, 102, 15, 183, 22, 11, 0, 102, 247, 226, 102, 3, -248, 102, 88, 102, 3, 195, 102, 91, 233, 101, 255, 6, 30, 102, 96, 38, -103, 102, 15, 183, 95, 4, 38, 103, 102, 15, 183, 79, 6, 102, 11, 201, - 15, 132, 101, 250, 102, 3, 223, 102, 131, 195, 2, 102, 129, 199, 254, 1, - 0, 0, 102, 73, 102, 11, 201, 15, 132, 23, 0, 38, 103, 139, 3, 38, -103, 137, 7, 102, 131, 195, 2, 102, 129, 199, 0, 2, 0, 0, 102, 73, -235, 226, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 184, 1, 0, - 0, 0, 102, 163, 30, 2, 102, 161, 26, 2, 102, 3, 6, 82, 2, 102, -163, 74, 2, 102, 161, 48, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, -102, 163, 84, 0, 102, 161, 86, 2, 163, 91, 0, 102, 139, 30, 26, 2, - 30, 7, 232, 242, 252, 102, 15, 183, 251, 232, 111, 255, 102, 161, 26, 2, -102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, - 0, 0, 232, 100, 253, 102, 11, 192, 15, 132, 87, 0, 102, 139, 216, 30, - 7, 102, 139, 62, 22, 2, 232, 249, 253, 102, 139, 30, 22, 2, 103, 102, -129, 59, 128, 0, 0, 0, 15, 132, 6, 0, 103, 3, 91, 4, 235, 238, -103, 102, 129, 59, 128, 0, 0, 0, 15, 133, 39, 0, 102, 83, 103, 102, -139, 67, 16, 102, 139, 62, 74, 2, 30, 7, 232, 9, 1, 102, 91, 102, -161, 82, 2, 102, 1, 6, 74, 2, 102, 255, 6, 30, 2, 103, 3, 91, - 4, 235, 205, 102, 97, 144, 31, 7, 195, 102, 139, 208, 102, 139, 14, 30, - 2, 102, 161, 26, 2, 102, 82, 102, 80, 102, 81, 102, 82, 102, 187, 128, - 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, -215, 252, 102, 11, 192, 15, 132, 64, 249, 102, 139, 216, 102, 88, 232, 42, - 1, 102, 11, 192, 15, 132, 7, 0, 102, 91, 102, 91, 102, 91, 195, 102, - 89, 102, 88, 102, 90, 102, 3, 6, 82, 2, 226, 185, 102, 51, 192, 195, - 6, 30, 102, 96, 102, 80, 102, 81, 102, 51, 210, 102, 15, 182, 30, 13, - 0, 102, 247, 243, 102, 82, 232, 144, 255, 102, 11, 192, 15, 132, 249, 248, -102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, 163, - 84, 0, 102, 89, 102, 15, 182, 30, 13, 0, 102, 59, 203, 15, 142, 19, - 0, 137, 30, 91, 0, 102, 43, 203, 102, 88, 102, 3, 195, 102, 80, 102, - 81, 235, 20, 144, 102, 88, 102, 3, 193, 102, 80, 137, 14, 91, 0, 102, -185, 0, 0, 0, 0, 102, 81, 6, 102, 87, 139, 223, 131, 227, 15, 140, -192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 155, 251, 102, 95, 7, 102, - 3, 62, 78, 2, 102, 89, 102, 88, 102, 131, 249, 0, 15, 143, 116, 255, -102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 247, 38, 86, 2, 102, -139, 14, 86, 2, 232, 89, 255, 232, 241, 253, 102, 97, 144, 31, 7, 195, - 6, 30, 102, 96, 102, 247, 38, 98, 2, 102, 139, 30, 50, 2, 102, 139, - 14, 98, 2, 30, 7, 102, 139, 62, 66, 2, 232, 35, 253, 232, 203, 253, -102, 97, 144, 31, 7, 195, 102, 80, 102, 83, 102, 81, 102, 139, 30, 70, - 2, 102, 139, 200, 102, 193, 232, 3, 102, 131, 225, 7, 102, 3, 216, 102, -184, 1, 0, 0, 0, 102, 211, 224, 103, 132, 3, 15, 132, 4, 0, 248, -235, 2, 144, 249, 102, 89, 102, 91, 102, 88, 195, 103, 128, 123, 8, 1, - 15, 132, 4, 0, 102, 43, 192, 195, 103, 102, 141, 115, 16, 103, 102, 139, - 86, 8, 102, 59, 194, 15, 135, 11, 0, 103, 102, 139, 22, 102, 59, 194, - 15, 131, 4, 0, 102, 43, 192, 195, 103, 3, 94, 16, 102, 43, 246, 103, -128, 59, 0, 15, 132, 62, 0, 232, 129, 0, 102, 3, 241, 232, 57, 0, -102, 3, 202, 102, 59, 193, 15, 140, 33, 0, 102, 139, 209, 102, 80, 103, -102, 15, 182, 11, 102, 139, 193, 102, 131, 224, 15, 102, 193, 233, 4, 102, - 3, 217, 102, 3, 216, 102, 67, 102, 88, 235, 196, 102, 43, 200, 102, 43, -194, 102, 3, 198, 195, 102, 43, 192, 195, 102, 43, 201, 103, 138, 11, 128, -225, 15, 102, 131, 249, 0, 15, 133, 4, 0, 102, 43, 201, 195, 102, 83, -102, 82, 102, 3, 217, 103, 102, 15, 190, 19, 102, 73, 102, 75, 102, 131, -249, 0, 15, 132, 13, 0, 102, 193, 226, 8, 103, 138, 19, 102, 75, 102, - 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, 195, 102, 83, 102, 82, 102, - 43, 210, 103, 138, 19, 102, 131, 226, 15, 102, 43, 201, 103, 138, 11, 192, -233, 4, 102, 131, 249, 0, 15, 133, 8, 0, 102, 43, 201, 102, 90, 102, - 91, 195, 102, 3, 218, 102, 3, 217, 103, 102, 15, 190, 19, 102, 73, 102, - 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, 193, 226, 8, 103, 138, 19, -102, 75, 102, 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, 195, 102, 11, -201, 15, 133, 1, 0, 195, 102, 81, 102, 86, 103, 131, 62, 97, 15, 140, - 12, 0, 103, 131, 62, 122, 15, 143, 4, 0, 103, 131, 46, 32, 102, 131, -198, 2, 226, 230, 102, 94, 102, 89, 195, 102, 80, 102, 81, 102, 139, 208, -102, 161, 46, 2, 103, 102, 141, 88, 16, 103, 3, 67, 4, 103, 102, 141, - 64, 16, 102, 139, 218, 232, 158, 250, 102, 11, 192, 15, 132, 5, 0, 102, - 89, 102, 89, 195, 102, 161, 50, 2, 102, 11, 192, 15, 133, 8, 0, 102, - 89, 102, 89, 102, 51, 192, 195, 102, 139, 22, 50, 2, 103, 102, 141, 82, - 16, 103, 102, 139, 66, 8, 102, 64, 102, 139, 30, 78, 2, 102, 247, 227, -102, 51, 210, 102, 247, 54, 90, 2, 102, 80, 102, 88, 102, 11, 192, 15, -132, 48, 0, 102, 72, 102, 80, 232, 28, 254, 114, 238, 232, 241, 253, 102, - 90, 102, 89, 102, 91, 102, 83, 102, 81, 102, 82, 102, 161, 66, 2, 103, -102, 141, 64, 24, 232, 47, 250, 102, 11, 192, 116, 206, 102, 89, 102, 89, -102, 89, 195, 102, 89, 102, 89, 102, 51, 192, 195, 6, 30, 102, 96, 102, -139, 54, 66, 2, 102, 185, 32, 0, 0, 0, 102, 247, 193, 3, 0, 0, - 0, 15, 133, 3, 0, 232, 13, 0, 102, 173, 232, 105, 0, 226, 235, 102, - 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 51, 192, 102, 51, 219, 176, - 13, 180, 14, 187, 7, 0, 205, 16, 176, 10, 180, 14, 187, 7, 0, 205, - 16, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 11, 201, 15, 133, - 9, 0, 232, 208, 255, 102, 97, 144, 31, 7, 195, 102, 51, 192, 102, 51, -219, 173, 180, 14, 187, 7, 0, 205, 16, 226, 240, 232, 183, 255, 102, 97, -144, 31, 7, 195, 96, 172, 60, 0, 116, 9, 180, 14, 187, 7, 0, 205, - 16, 235, 242, 97, 144, 195, 6, 30, 102, 96, 102, 185, 8, 0, 0, 0, -102, 139, 208, 102, 131, 226, 15, 102, 82, 102, 193, 232, 4, 226, 241, 102, -185, 8, 0, 0, 0, 102, 88, 102, 131, 248, 9, 15, 143, 7, 0, 102, -131, 192, 48, 235, 9, 144, 102, 131, 232, 10, 102, 131, 192, 65, 102, 51, -219, 180, 14, 187, 7, 0, 205, 16, 226, 219, 176, 32, 180, 14, 187, 7, - 0, 205, 16, 102, 97, 144, 31, 7, 232, 96, 0, 195, 6, 30, 102, 96, -102, 190, 22, 13, 0, 0, 232, 79, 245, 102, 97, 144, 31, 7, 195, 6, - 30, 102, 96, 102, 190, 38, 13, 0, 0, 232, 60, 245, 102, 97, 144, 31, - 7, 195, 6, 30, 102, 96, 102, 190, 54, 13, 0, 0, 232, 41, 245, 102, - 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 190, 70, 13, 0, 0, 232, - 22, 245, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 190, 86, 13, - 0, 0, 232, 3, 245, 102, 97, 144, 31, 7, 195, 102, 80, 102, 184, 0, - 0, 245, 255, 102, 64, 102, 11, 192, 117, 249, 102, 88, 195, 102, 81, 102, - 80, 102, 184, 5, 0, 0, 0, 30, 7, 102, 139, 249, 232, 71, 252, 102, -139, 193, 102, 91, 102, 83, 102, 15, 183, 14, 12, 2, 102, 186, 14, 2, - 0, 0, 232, 68, 248, 102, 91, 102, 89, 102, 11, 192, 15, 133, 47, 0, -102, 139, 193, 102, 139, 203, 102, 80, 102, 83, 232, 35, 0, 102, 91, 102, - 95, 102, 11, 192, 15, 132, 23, 0, 30, 7, 232, 9, 252, 102, 139, 199, -102, 15, 183, 14, 12, 2, 102, 186, 14, 2, 0, 0, 232, 10, 248, 195, -102, 81, 102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, - 0, 0, 0, 0, 232, 242, 247, 102, 11, 192, 15, 132, 82, 0, 102, 139, -216, 30, 7, 102, 139, 62, 22, 2, 232, 135, 248, 30, 7, 102, 139, 30, - 22, 2, 102, 89, 38, 102, 57, 15, 15, 132, 46, 0, 38, 102, 131, 63, -255, 15, 132, 45, 0, 38, 131, 127, 4, 0, 15, 132, 36, 0, 38, 102, - 15, 183, 71, 4, 3, 216, 139, 195, 37, 0, 128, 116, 215, 140, 192, 5, - 0, 8, 142, 192, 129, 227, 255, 127, 235, 202, 38, 102, 139, 71, 16, 195, -102, 89, 102, 51, 192, 195, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 48, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 49, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 50, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 51, 13, 10, 0, 68, 101, 98, 117, 103, 32, 80, 111, 105, 110, -116, 32, 52, 13, 10 -}; +#include "boot.h" +/** + * boot_array - the first 4136 bytes of $Boot + * + * The first 4136 bytes of $Boot. The rest is just zero. Total 8192 bytes. + */ +const unsigned char boot_array[4136] = { +235, 82, 144, 78, 84, 70, 83, 32, 32, 32, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 250, 51, 192, 142, 208, 188, 0, 124, 251, 104, 192, 7, + 31, 30, 104, 102, 0, 203, 136, 22, 14, 0, 102, 129, 62, 3, 0, 78, + 84, 70, 83, 117, 21, 180, 65, 187, 170, 85, 205, 19, 114, 12, 129, 251, + 85, 170, 117, 6, 247, 193, 1, 0, 117, 3, 233, 210, 0, 30, 131, 236, + 24, 104, 26, 0, 180, 72, 138, 22, 14, 0, 139, 244, 22, 31, 205, 19, +159, 131, 196, 24, 158, 88, 31, 114, 225, 59, 6, 11, 0, 117, 219, 163, + 15, 0, 193, 46, 15, 0, 4, 30, 90, 51, 219, 185, 0, 32, 43, 200, +102, 255, 6, 17, 0, 3, 22, 15, 0, 142, 194, 255, 6, 22, 0, 232, + 64, 0, 43, 200, 119, 239, 184, 0, 187, 205, 26, 102, 35, 192, 117, 45, +102, 129, 251, 84, 67, 80, 65, 117, 36, 129, 249, 2, 1, 114, 30, 22, +104, 7, 187, 22, 104, 112, 14, 22, 104, 9, 0, 102, 83, 102, 83, 102, + 85, 22, 22, 22, 104, 184, 1, 102, 97, 14, 7, 205, 26, 233, 106, 1, +144, 144, 102, 96, 30, 6, 102, 161, 17, 0, 102, 3, 6, 28, 0, 30, +102, 104, 0, 0, 0, 0, 102, 80, 6, 83, 104, 1, 0, 104, 16, 0, +180, 66, 138, 22, 14, 0, 22, 31, 139, 244, 205, 19, 102, 89, 91, 90, +102, 89, 102, 89, 31, 15, 130, 22, 0, 102, 255, 6, 17, 0, 3, 22, + 15, 0, 142, 194, 255, 14, 22, 0, 117, 188, 7, 31, 102, 97, 195, 160, +248, 1, 232, 8, 0, 160, 251, 1, 232, 2, 0, 235, 254, 180, 1, 139, +240, 172, 60, 0, 116, 9, 180, 14, 187, 7, 0, 205, 16, 235, 242, 195, + 13, 10, 65, 32, 100, 105, 115, 107, 32, 114, 101, 97, 100, 32, 101, 114, +114, 111, 114, 32, 111, 99, 99, 117, 114, 114, 101, 100, 0, 13, 10, 66, + 79, 79, 84, 77, 71, 82, 32, 105, 115, 32, 109, 105, 115, 115, 105, 110, +103, 0, 13, 10, 66, 79, 79, 84, 77, 71, 82, 32, 105, 115, 32, 99, +111, 109, 112, 114, 101, 115, 115, 101, 100, 0, 13, 10, 80, 114, 101, 115, +115, 32, 67, 116, 114, 108, 43, 65, 108, 116, 43, 68, 101, 108, 32, 116, +111, 32, 114, 101, 115, 116, 97, 114, 116, 13, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 128, 157, 178, 202, 0, 0, 85, 170, + 7, 0, 66, 0, 79, 0, 79, 0, 84, 0, 77, 0, 71, 0, 82, 0, + 4, 0, 36, 0, 73, 0, 51, 0, 48, 0, 0, 224, 0, 0, 0, 48, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 235, 34, 144, 144, 5, 0, 78, 0, 84, 0, + 76, 0, 68, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, 15, 183, 6, 11, 0, +102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 163, 82, 2, 102, 139, 14, + 64, 0, 128, 249, 0, 15, 143, 14, 0, 246, 217, 102, 184, 1, 0, 0, + 0, 102, 211, 224, 235, 8, 144, 102, 161, 82, 2, 102, 247, 225, 102, 163, +102, 2, 102, 15, 183, 30, 11, 0, 102, 51, 210, 102, 247, 243, 102, 163, + 86, 2, 232, 149, 4, 102, 139, 14, 78, 2, 102, 137, 14, 38, 2, 102, + 3, 14, 102, 2, 102, 137, 14, 42, 2, 102, 3, 14, 102, 2, 102, 137, + 14, 46, 2, 102, 3, 14, 102, 2, 102, 137, 14, 62, 2, 102, 3, 14, +102, 2, 102, 137, 14, 70, 2, 102, 184, 144, 0, 0, 0, 102, 139, 14, + 38, 2, 232, 131, 9, 102, 11, 192, 15, 132, 83, 254, 102, 163, 50, 2, +102, 184, 160, 0, 0, 0, 102, 139, 14, 42, 2, 232, 106, 9, 102, 163, + 54, 2, 102, 184, 176, 0, 0, 0, 102, 139, 14, 46, 2, 232, 88, 9, +102, 163, 58, 2, 102, 161, 50, 2, 102, 11, 192, 15, 132, 32, 254, 103, +128, 120, 8, 0, 15, 133, 23, 254, 103, 102, 141, 80, 16, 103, 3, 66, + 4, 103, 102, 15, 182, 72, 12, 102, 137, 14, 114, 2, 103, 102, 139, 72, + 8, 102, 137, 14, 110, 2, 102, 161, 110, 2, 102, 15, 183, 14, 11, 0, +102, 51, 210, 102, 247, 241, 102, 163, 118, 2, 102, 161, 70, 2, 102, 3, + 6, 110, 2, 102, 163, 74, 2, 102, 131, 62, 54, 2, 0, 15, 132, 29, + 0, 102, 131, 62, 58, 2, 0, 15, 132, 196, 253, 102, 139, 30, 58, 2, + 30, 7, 102, 139, 62, 74, 2, 102, 161, 46, 2, 232, 224, 1, 102, 15, +183, 14, 0, 2, 102, 184, 2, 2, 0, 0, 232, 34, 8, 102, 11, 192, + 15, 133, 22, 0, 102, 15, 183, 14, 90, 2, 102, 184, 92, 2, 0, 0, +232, 12, 8, 102, 11, 192, 15, 132, 66, 12, 103, 102, 139, 0, 30, 7, +102, 139, 62, 62, 2, 232, 63, 6, 102, 161, 62, 2, 102, 187, 32, 0, + 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 228, + 0, 102, 133, 192, 15, 133, 35, 0, 102, 161, 62, 2, 102, 187, 128, 0, + 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 196, + 0, 102, 11, 192, 15, 133, 68, 0, 233, 241, 11, 102, 51, 210, 102, 185, +128, 0, 0, 0, 102, 161, 62, 2, 232, 202, 8, 102, 11, 192, 15, 132, +218, 11, 30, 7, 102, 139, 62, 62, 2, 232, 219, 5, 102, 161, 62, 2, +102, 187, 128, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 128, 0, 102, 11, 192, 15, 132, 176, 11, 103, 102, 15, 183, + 88, 12, 102, 129, 227, 255, 0, 0, 0, 15, 133, 165, 11, 102, 139, 216, +104, 0, 32, 7, 102, 43, 255, 102, 161, 62, 2, 232, 0, 1, 104, 0, + 32, 7, 102, 43, 255, 102, 161, 62, 2, 232, 172, 10, 138, 22, 14, 0, +184, 232, 3, 142, 192, 141, 54, 11, 0, 43, 192, 104, 0, 32, 80, 203, + 6, 30, 102, 96, 102, 139, 218, 102, 15, 182, 14, 13, 0, 102, 247, 225, +102, 163, 17, 0, 102, 139, 195, 102, 247, 225, 163, 22, 0, 139, 223, 131, +227, 15, 140, 192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 51, 252, 102, + 97, 144, 31, 7, 195, 103, 3, 64, 20, 103, 102, 131, 56, 255, 15, 132, + 76, 0, 103, 102, 57, 24, 15, 133, 51, 0, 102, 11, 201, 15, 133, 10, + 0, 103, 128, 120, 9, 0, 15, 133, 35, 0, 195, 103, 58, 72, 9, 15, +133, 26, 0, 102, 139, 240, 103, 3, 112, 10, 232, 151, 6, 102, 81, 30, + 7, 102, 139, 250, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, 102, 131, +120, 4, 0, 15, 132, 7, 0, 103, 102, 3, 64, 4, 235, 171, 102, 43, +192, 195, 102, 139, 243, 232, 108, 6, 103, 102, 3, 0, 103, 247, 64, 12, + 2, 0, 15, 133, 52, 0, 103, 102, 141, 80, 16, 103, 58, 74, 64, 15, +133, 24, 0, 103, 102, 141, 114, 66, 232, 73, 6, 102, 81, 30, 7, 102, +139, 251, 243, 167, 102, 89, 15, 133, 1, 0, 195, 103, 131, 120, 8, 0, + 15, 132, 6, 0, 103, 3, 64, 8, 235, 194, 102, 51, 192, 195, 103, 128, +123, 8, 0, 15, 133, 28, 0, 6, 30, 102, 96, 103, 102, 141, 83, 16, +103, 102, 139, 10, 102, 139, 243, 103, 3, 114, 4, 243, 164, 102, 97, 144, + 31, 7, 195, 102, 80, 103, 102, 141, 83, 16, 102, 133, 192, 15, 133, 10, + 0, 103, 102, 139, 74, 8, 102, 65, 235, 17, 144, 103, 102, 139, 66, 24, +102, 51, 210, 102, 247, 54, 82, 2, 102, 139, 200, 102, 43, 192, 102, 94, +232, 1, 0, 195, 6, 30, 102, 96, 103, 128, 123, 8, 1, 15, 132, 3, + 0, 233, 107, 251, 102, 131, 249, 0, 15, 133, 6, 0, 102, 97, 144, 31, + 7, 195, 102, 83, 102, 80, 102, 81, 102, 86, 102, 87, 6, 232, 145, 4, +102, 139, 209, 7, 102, 95, 102, 94, 102, 89, 102, 133, 192, 15, 132, 52, + 0, 102, 59, 202, 15, 141, 3, 0, 102, 139, 209, 232, 130, 254, 102, 43, +202, 102, 139, 218, 102, 139, 194, 102, 15, 182, 22, 13, 0, 102, 247, 226, +102, 15, 183, 22, 11, 0, 102, 247, 226, 102, 3, 248, 102, 88, 102, 3, +195, 102, 91, 235, 159, 102, 133, 246, 15, 132, 3, 251, 102, 81, 102, 87, + 6, 103, 102, 15, 182, 67, 9, 102, 133, 192, 15, 132, 32, 0, 102, 209, +224, 102, 43, 224, 102, 139, 252, 102, 84, 102, 86, 103, 102, 15, 183, 115, + 10, 102, 3, 243, 102, 139, 200, 243, 164, 102, 94, 235, 3, 144, 102, 80, +102, 80, 103, 102, 139, 3, 102, 80, 103, 102, 139, 67, 24, 102, 80, 103, +102, 139, 86, 32, 102, 133, 210, 15, 132, 11, 0, 102, 139, 254, 30, 7, +102, 139, 194, 232, 113, 3, 102, 139, 198, 102, 90, 102, 89, 102, 66, 102, + 81, 102, 86, 232, 63, 6, 102, 133, 192, 15, 132, 146, 250, 102, 94, 102, + 89, 102, 139, 254, 30, 7, 232, 78, 3, 102, 139, 198, 102, 139, 217, 102, + 89, 102, 90, 102, 81, 102, 86, 102, 209, 233, 232, 248, 253, 102, 133, 192, + 15, 132, 107, 250, 102, 94, 102, 89, 102, 3, 225, 7, 102, 95, 102, 89, +102, 139, 208, 102, 88, 102, 91, 102, 139, 218, 233, 245, 254, 6, 30, 102, + 96, 38, 103, 102, 15, 183, 95, 4, 38, 103, 102, 15, 183, 79, 6, 102, + 11, 201, 15, 132, 57, 250, 102, 3, 223, 102, 131, 195, 2, 102, 129, 199, +254, 1, 0, 0, 102, 73, 102, 11, 201, 15, 132, 23, 0, 38, 103, 139, + 3, 38, 103, 137, 7, 102, 131, 195, 2, 102, 129, 199, 0, 2, 0, 0, +102, 73, 235, 226, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 184, + 1, 0, 0, 0, 102, 163, 34, 2, 102, 161, 30, 2, 102, 3, 6, 102, + 2, 102, 163, 106, 2, 102, 3, 6, 102, 2, 102, 163, 78, 2, 102, 161, + 48, 0, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 139, 30, 78, 2, +102, 137, 7, 102, 163, 17, 0, 131, 195, 4, 102, 161, 86, 2, 102, 137, + 7, 163, 22, 0, 131, 195, 4, 102, 137, 30, 78, 2, 102, 139, 30, 30, + 2, 30, 7, 232, 92, 249, 102, 139, 251, 232, 81, 255, 102, 161, 30, 2, +102, 187, 32, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, + 0, 0, 232, 16, 253, 102, 11, 192, 15, 132, 25, 1, 102, 139, 216, 30, + 7, 102, 139, 62, 26, 2, 102, 51, 192, 232, 162, 253, 102, 139, 30, 26, + 2, 102, 129, 63, 128, 0, 0, 0, 15, 132, 235, 0, 3, 95, 4, 235, +240, 102, 83, 102, 139, 71, 16, 102, 247, 38, 86, 2, 102, 80, 102, 51, +210, 102, 15, 182, 30, 13, 0, 102, 247, 243, 102, 82, 232, 220, 0, 102, + 11, 192, 15, 132, 57, 249, 102, 139, 14, 86, 2, 102, 15, 182, 30, 13, + 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, 139, 30, 78, 2, 102, 137, + 7, 131, 195, 4, 102, 15, 182, 6, 13, 0, 102, 43, 194, 102, 59, 193, + 15, 134, 3, 0, 102, 139, 193, 102, 137, 7, 102, 43, 200, 102, 90, 15, +132, 117, 0, 102, 3, 194, 102, 80, 102, 51, 210, 102, 15, 182, 30, 13, + 0, 102, 247, 243, 102, 81, 232, 130, 0, 102, 89, 102, 11, 192, 15, 132, +221, 248, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 139, 30, 78, 2, +102, 139, 23, 131, 195, 4, 102, 3, 23, 102, 59, 208, 15, 133, 21, 0, +102, 15, 182, 6, 13, 0, 102, 59, 193, 15, 134, 3, 0, 102, 139, 193, +102, 1, 7, 235, 165, 131, 195, 4, 102, 137, 30, 78, 2, 102, 137, 7, +131, 195, 4, 102, 15, 182, 6, 13, 0, 102, 59, 193, 15, 134, 3, 0, +102, 139, 193, 102, 137, 7, 235, 130, 131, 195, 4, 102, 255, 6, 34, 2, +102, 137, 30, 78, 2, 102, 91, 3, 95, 4, 102, 129, 63, 128, 0, 0, + 0, 15, 132, 12, 255, 102, 97, 144, 31, 7, 195, 102, 139, 208, 102, 139, + 14, 34, 2, 102, 139, 54, 106, 2, 102, 3, 54, 102, 2, 102, 82, 102, + 81, 102, 82, 102, 139, 30, 106, 2, 102, 139, 62, 86, 2, 102, 139, 4, +102, 163, 17, 0, 131, 198, 4, 102, 139, 4, 163, 22, 0, 131, 198, 4, + 30, 7, 232, 221, 247, 102, 43, 248, 15, 132, 8, 0, 247, 38, 11, 0, + 3, 216, 235, 217, 102, 139, 62, 106, 2, 30, 7, 232, 191, 253, 102, 161, +106, 2, 102, 187, 128, 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 139, +209, 232, 129, 251, 102, 11, 192, 15, 132, 244, 247, 102, 139, 216, 102, 88, +102, 86, 232, 44, 1, 102, 94, 102, 11, 192, 15, 132, 5, 0, 102, 91, +102, 91, 195, 102, 89, 102, 90, 226, 132, 102, 51, 192, 195, 6, 30, 102, + 96, 102, 80, 102, 81, 102, 51, 210, 102, 15, 182, 30, 13, 0, 102, 247, +243, 102, 82, 102, 87, 232, 83, 255, 102, 95, 102, 11, 192, 15, 132, 174, +247, 102, 15, 182, 30, 13, 0, 102, 247, 227, 102, 90, 102, 3, 194, 102, +163, 17, 0, 102, 89, 102, 15, 182, 30, 13, 0, 102, 59, 203, 15, 142, + 19, 0, 137, 30, 22, 0, 102, 43, 203, 102, 88, 102, 3, 195, 102, 80, +102, 81, 235, 20, 144, 102, 88, 102, 3, 193, 102, 80, 137, 14, 22, 0, +102, 185, 0, 0, 0, 0, 102, 81, 6, 102, 87, 139, 223, 131, 227, 15, +140, 192, 102, 193, 239, 4, 3, 199, 80, 7, 232, 5, 247, 102, 95, 7, +102, 3, 62, 82, 2, 102, 89, 102, 88, 102, 131, 249, 0, 15, 143, 112, +255, 102, 97, 144, 31, 7, 195, 6, 30, 102, 96, 102, 247, 38, 86, 2, +102, 139, 14, 86, 2, 232, 85, 255, 232, 210, 252, 102, 97, 144, 31, 7, +195, 6, 30, 102, 96, 102, 247, 38, 114, 2, 102, 139, 30, 54, 2, 102, +139, 14, 114, 2, 102, 139, 54, 42, 2, 30, 7, 102, 139, 62, 70, 2, +232, 129, 251, 232, 167, 252, 102, 97, 144, 31, 7, 195, 102, 80, 102, 83, +102, 81, 102, 139, 30, 74, 2, 102, 139, 200, 102, 193, 232, 3, 102, 131, +225, 7, 102, 3, 216, 102, 184, 1, 0, 0, 0, 102, 211, 224, 103, 132, + 3, 15, 132, 4, 0, 248, 235, 2, 144, 249, 102, 89, 102, 91, 102, 88, +195, 103, 128, 123, 8, 1, 15, 132, 4, 0, 102, 43, 192, 195, 103, 102, +141, 115, 16, 103, 102, 139, 86, 8, 102, 59, 194, 15, 135, 11, 0, 103, +102, 139, 22, 102, 59, 194, 15, 131, 4, 0, 102, 43, 192, 195, 103, 3, + 94, 16, 102, 43, 246, 103, 128, 59, 0, 15, 132, 62, 0, 232, 129, 0, +102, 3, 241, 232, 57, 0, 102, 3, 202, 102, 59, 193, 15, 140, 33, 0, +102, 139, 209, 102, 80, 103, 102, 15, 182, 11, 102, 139, 193, 102, 131, 224, + 15, 102, 193, 233, 4, 102, 3, 217, 102, 3, 216, 102, 67, 102, 88, 235, +196, 102, 43, 200, 102, 43, 194, 102, 3, 198, 195, 102, 43, 192, 195, 102, + 43, 201, 103, 138, 11, 128, 225, 15, 102, 131, 249, 0, 15, 133, 4, 0, +102, 43, 201, 195, 102, 83, 102, 82, 102, 3, 217, 103, 102, 15, 190, 19, +102, 73, 102, 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, 193, 226, 8, +103, 138, 19, 102, 75, 102, 73, 235, 235, 102, 139, 202, 102, 90, 102, 91, +195, 102, 83, 102, 82, 102, 43, 210, 103, 138, 19, 102, 131, 226, 15, 102, + 43, 201, 103, 138, 11, 192, 233, 4, 102, 131, 249, 0, 15, 133, 8, 0, +102, 43, 201, 102, 90, 102, 91, 195, 102, 3, 218, 102, 3, 217, 103, 102, + 15, 190, 19, 102, 73, 102, 75, 102, 131, 249, 0, 15, 132, 13, 0, 102, +193, 226, 8, 103, 138, 19, 102, 75, 102, 73, 235, 235, 102, 139, 202, 102, + 90, 102, 91, 195, 102, 11, 201, 15, 133, 1, 0, 195, 102, 81, 102, 86, +103, 131, 62, 97, 15, 140, 12, 0, 103, 131, 62, 122, 15, 143, 4, 0, +103, 131, 46, 32, 102, 131, 198, 2, 226, 230, 102, 94, 102, 89, 195, 102, + 80, 102, 81, 102, 139, 208, 102, 161, 50, 2, 103, 102, 141, 88, 16, 103, + 3, 67, 4, 103, 102, 141, 64, 16, 102, 139, 218, 232, 68, 249, 102, 11, +192, 15, 132, 5, 0, 102, 89, 102, 89, 195, 102, 161, 54, 2, 102, 11, +192, 15, 133, 8, 0, 102, 89, 102, 89, 102, 51, 192, 195, 102, 139, 22, + 54, 2, 103, 102, 141, 82, 16, 103, 102, 139, 66, 24, 102, 51, 210, 102, +247, 54, 110, 2, 102, 51, 246, 102, 80, 102, 86, 102, 88, 102, 94, 102, + 59, 198, 15, 132, 58, 0, 102, 86, 102, 64, 102, 80, 102, 72, 232, 27, +254, 114, 232, 232, 235, 253, 102, 90, 102, 94, 102, 89, 102, 91, 102, 83, +102, 81, 102, 86, 102, 82, 102, 161, 70, 2, 103, 102, 141, 64, 24, 232, +208, 248, 102, 11, 192, 116, 196, 102, 89, 102, 89, 102, 89, 102, 89, 195, +102, 89, 102, 89, 102, 51, 192, 195, 102, 81, 102, 80, 102, 184, 5, 0, + 0, 0, 30, 7, 102, 139, 249, 232, 141, 253, 102, 139, 193, 102, 187, 32, + 0, 0, 0, 102, 185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, + 51, 248, 102, 91, 102, 89, 102, 133, 192, 15, 133, 21, 0, 102, 139, 193, +102, 15, 183, 14, 16, 2, 102, 186, 18, 2, 0, 0, 232, 22, 248, 235, + 51, 144, 102, 51, 210, 102, 139, 193, 102, 139, 203, 102, 80, 102, 83, 232, + 35, 0, 102, 91, 102, 95, 102, 11, 192, 15, 132, 23, 0, 30, 7, 232, + 53, 253, 102, 139, 199, 102, 15, 183, 14, 16, 2, 102, 186, 18, 2, 0, + 0, 232, 225, 247, 195, 102, 82, 102, 81, 102, 187, 32, 0, 0, 0, 102, +185, 0, 0, 0, 0, 102, 186, 0, 0, 0, 0, 232, 199, 247, 102, 11, +192, 15, 132, 99, 0, 102, 139, 216, 30, 7, 102, 139, 62, 26, 2, 102, + 51, 192, 232, 89, 248, 30, 7, 102, 139, 30, 26, 2, 102, 89, 102, 90, + 38, 102, 57, 15, 15, 133, 12, 0, 38, 102, 57, 87, 8, 15, 132, 49, + 0, 235, 19, 144, 38, 102, 131, 63, 255, 15, 132, 47, 0, 38, 131, 127, + 4, 0, 15, 132, 38, 0, 38, 102, 15, 183, 71, 4, 3, 216, 139, 195, + 37, 0, 128, 116, 203, 140, 192, 5, 0, 8, 142, 192, 129, 227, 255, 127, +235, 190, 38, 102, 139, 71, 16, 195, 102, 89, 102, 90, 102, 51, 192, 195, +102, 80, 102, 81, 102, 139, 199, 102, 193, 232, 4, 6, 89, 3, 200, 81, + 7, 102, 131, 231, 15, 102, 89, 102, 88, 195, 96, 6, 190, 189, 13, 191, + 0, 32, 30, 7, 185, 13, 0, 144, 243, 165, 7, 97, 195, 1, 35, 69, +103, 137, 171, 205, 239, 254, 220, 186, 152, 118, 84, 50, 16, 240, 225, 210, +195, 0, 0, 0, 0, 32, 32, 96, 139, 54, 24, 32, 38, 138, 5, 136, + 4, 71, 70, 102, 255, 6, 20, 32, 129, 254, 96, 32, 117, 6, 232, 91, + 0, 190, 32, 32, 226, 230, 137, 54, 24, 32, 97, 195, 102, 96, 139, 54, + 24, 32, 176, 128, 136, 4, 70, 50, 192, 129, 254, 96, 32, 117, 6, 232, + 58, 0, 190, 32, 32, 129, 254, 88, 32, 117, 233, 102, 51, 192, 102, 163, + 88, 32, 102, 161, 20, 32, 102, 193, 224, 3, 102, 15, 200, 102, 163, 92, + 32, 232, 24, 0, 187, 0, 32, 102, 139, 7, 102, 15, 200, 102, 137, 7, +131, 195, 4, 129, 251, 52, 32, 117, 238, 102, 97, 195, 102, 96, 187, 32, + 32, 102, 139, 7, 102, 15, 200, 102, 137, 7, 131, 195, 4, 129, 251, 96, + 32, 117, 238, 187, 0, 32, 102, 139, 15, 102, 139, 87, 4, 102, 139, 119, + 8, 102, 139, 127, 12, 102, 139, 111, 16, 187, 32, 32, 199, 6, 26, 32, + 48, 15, 198, 6, 28, 32, 20, 144, 83, 139, 30, 26, 32, 255, 23, 102, + 3, 71, 2, 91, 102, 3, 232, 102, 3, 47, 102, 139, 193, 102, 193, 192, + 5, 102, 3, 197, 102, 139, 239, 102, 139, 254, 102, 139, 242, 102, 193, 198, + 30, 102, 139, 209, 102, 139, 200, 102, 139, 7, 102, 51, 71, 8, 102, 51, + 71, 32, 102, 51, 71, 52, 102, 209, 192, 102, 137, 71, 64, 131, 195, 4, +254, 14, 28, 32, 117, 178, 131, 6, 26, 32, 6, 129, 62, 26, 32, 72, + 15, 117, 159, 187, 0, 32, 102, 1, 15, 102, 1, 87, 4, 102, 1, 119, + 8, 102, 1, 127, 12, 102, 1, 111, 16, 102, 97, 195, 102, 139, 198, 102, + 51, 199, 102, 35, 194, 102, 51, 199, 195, 102, 139, 194, 102, 51, 198, 102, + 51, 199, 195, 102, 83, 102, 139, 194, 102, 35, 198, 102, 139, 218, 102, 35, +223, 102, 11, 195, 102, 139, 222, 102, 35, 223, 102, 11, 195, 102, 91, 195, +252, 14, 153, 121, 130, 90, 9, 15, 161, 235, 217, 110, 19, 15, 220, 188, + 27, 143, 9, 15, 214, 193, 98, 202, 6, 30, 102, 96, 102, 51, 219, 184, + 0, 187, 205, 26, 102, 35, 192, 15, 133, 187, 0, 102, 129, 251, 84, 67, + 80, 65, 15, 133, 176, 0, 129, 249, 2, 1, 15, 130, 168, 0, 102, 97, +144, 31, 7, 6, 30, 102, 96, 103, 128, 123, 8, 0, 15, 133, 12, 0, +103, 102, 141, 83, 16, 103, 102, 139, 10, 235, 37, 144, 103, 102, 141, 83, + 16, 103, 102, 139, 74, 40, 102, 129, 249, 0, 0, 8, 0, 15, 131, 12, + 0, 103, 102, 139, 66, 44, 102, 35, 192, 15, 132, 3, 0, 102, 51, 201, + 14, 31, 232, 245, 253, 102, 35, 201, 15, 132, 50, 0, 102, 186, 0, 128, + 0, 0, 102, 59, 202, 15, 134, 31, 0, 102, 43, 202, 6, 102, 81, 102, + 87, 102, 82, 102, 139, 202, 232, 183, 253, 232, 251, 253, 102, 90, 102, 95, +102, 89, 7, 102, 3, 250, 235, 218, 232, 165, 253, 232, 233, 253, 232, 11, +254, 14, 7, 102, 187, 84, 67, 80, 65, 102, 191, 0, 32, 0, 0, 102, +185, 20, 0, 0, 0, 102, 184, 7, 187, 0, 0, 102, 186, 10, 0, 0, + 0, 102, 51, 246, 205, 26, 102, 97, 144, 31, 7, 195, 160, 249, 1, 233, + 64, 241, 160, 250, 1, 233, 58, 241 +}; diff --git a/ntfsprogs/boot.h b/ntfsprogs/boot.h index 74e9ba12..45d79927 100644 --- a/ntfsprogs/boot.h +++ b/ntfsprogs/boot.h @@ -1,7 +1,7 @@ #ifndef _NTFS_BOOT_H_ #define _NTFS_BOOT_H_ -extern const unsigned char boot_array[3429]; +extern const unsigned char boot_array[4136]; #endif /* _NTFS_BOOT_H_ */ diff --git a/ntfsprogs/list.h b/ntfsprogs/list.h index 9af28aa5..e3c77442 100644 --- a/ntfsprogs/list.h +++ b/ntfsprogs/list.h @@ -24,7 +24,7 @@ /** * struct list_head - Simple doubly linked list implementation. - * + * * Copied from Linux kernel 2.4.2-ac18 into Linux-NTFS (with minor * modifications). - AIA * diff --git a/ntfsprogs/mkntfs.8.in b/ntfsprogs/mkntfs.8.in index eedd1ad6..777649ad 100644 --- a/ntfsprogs/mkntfs.8.in +++ b/ntfsprogs/mkntfs.8.in @@ -45,10 +45,6 @@ mkntfs \- create an NTFS file system .B \-n ] [ -.B \-N -.I ntfs\-version -] -[ .B \-p .I part\-start\-sect ] @@ -123,39 +119,6 @@ Set the volume label for the filesystem. \fB\-C\fR, \fB\-\-enable\-compression\fR Enable compression on the volume. .TP -\fB\-c\fR, \fB\-\-cluster\-size\fR BYTES -Specify the size of clusters in bytes. Valid cluster size values are powers of -two, with at least 256, and at most 65536 bytes per cluster. If omitted, -.B mkntfs -determines the -.I cluster\-size -from the volume size. The value is determined as follows: -.TS -box; -lB lB lB -l l r. -Volume size Default cluster size -0 \- 512MB 512 bytes -512MB \- 1GB 1024 bytes -1GB \- 2GB 2048 bytes -2GB + 4096 bytes -.TE -.sp -.sp -Note that the default cluster size is set to be at least equal to the sector -size as a cluster cannot be smaller than a sector. Also, note that values -greater than 4096 have the side effect that compression is disabled on the -volume (due to limitations in the NTFS compression algorithm currently in use -by Windows). -.TP -\fB\-N\fR, \fB\-\-ntfs\-version\fR STRING -Select the version of NTFS you wish to create. This can be "1.2" -(Windows NT 4.0) or "3.1" (Windows XP, Server 2003 and Vista). -Versions are upwards compatible and Windows 2000, which uses version "3.0", -can read/write both. - -If this option is omitted then version "3.1" is used. -.TP \fB\-n\fR, \fB\-\-no\-action\fR Causes .B mkntfs @@ -164,6 +127,18 @@ to create a filesystem. All steps of the format are carried out except the actual writing to the device. .SS Advanced options .TP +\fB\-c\fR, \fB\-\-cluster\-size\fR BYTES +Specify the size of clusters in bytes. Valid cluster size values are powers of +two, with at least 256, and at most 65536 bytes per cluster. If omitted, +.B mkntfs +uses 4096 bytes as the default cluster size. +.sp +Note that the default cluster size is set to be at least equal to the sector +size as a cluster cannot be smaller than a sector. Also, note that values +greater than 4096 have the side effect that compression is disabled on the +volume (due to limitations in the NTFS compression algorithm currently in use +by Windows). +.TP \fB\-s\fR, \fB\-\-sector\-size\fR BYTES Specify the size of sectors in bytes. Valid sector size values are 256, 512, 1024, 2048 and 4096 bytes per sector. If omitted, diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c index 3f986b30..fa821fe0 100644 --- a/ntfsprogs/mkntfs.c +++ b/ntfsprogs/mkntfs.c @@ -1,10 +1,11 @@ /** * mkntfs - Part of the Linux-NTFS project. * - * Copyright (c) 2000-2006 Anton Altaparmakov + * 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. @@ -29,21 +30,6 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * WARNING: This program might not work on architectures which do not allow - * unaligned access. For those, the program would need to start using - * get/put_unaligned macros (#include ), but not doing it yet, - * since NTFS really mostly applies to ia32 only, which does allow unaligned - * accesses. We might not actually have a problem though, since the structs are - * defined as being packed so that might be enough for gcc to insert the - * correct code. - * - * If anyone using a non-little endian and/or an aligned access only CPU tries - * this program please let me know whether it works or not! - * - * Anton Altaparmakov - */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -81,6 +67,10 @@ #ifdef HAVE_LIBGEN_H #include #endif +#ifdef ENABLE_UUID +#include +#endif + #ifdef HAVE_GETOPT_H #include @@ -141,11 +131,13 @@ #include "utils.h" #include "ntfstime.h" #include "sd.h" -#include "upcase.h" #include "boot.h" #include "attrdef.h" /* #include "version.h" */ #include "logging.h" +#include "support.h" +#include "unistr.h" +#include "misc.h" #ifdef NO_NTFS_DEVICE_DEFAULT_IO_OPS #error "No default device io operations! Cannot build mkntfs. \ @@ -201,8 +193,6 @@ static struct mkntfs_options { 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 */ - u8 ver_major; /* -N, ntfs version to create */ - u8 ver_minor; char *label; /* -L, volume label */ } opts; @@ -221,36 +211,35 @@ static void mkntfs_license(void) 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" - " -c, --cluster-size BYTES Specify the cluster size for 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" - " -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" - " -N, --ntfs-version VERSION NTFS version: 3.1 (default) or 1.2 (old)\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)); +"\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); } @@ -260,11 +249,13 @@ static void mkntfs_usage(void) static void mkntfs_version(void) { ntfs_log_info("\n%s v%s (libntfs-3g)\n\n", EXEC_NAME, VERSION); - ntfs_log_info("Create an NTFS volume on a user specified (block) device.\n\n"); - ntfs_log_info("Copyright (c) 2000-2006 Anton Altaparmakov\n"); + 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); } @@ -298,7 +289,8 @@ static BOOL mkntfs_parse_long(const char *string, const char *name, long *num) /** * mkntfs_parse_llong */ -static BOOL mkntfs_parse_llong(const char *string, const char *name, long long *num) +static BOOL mkntfs_parse_llong(const char *string, const char *name, + long long *num) { char *end = NULL; long long tmp; @@ -313,7 +305,8 @@ static BOOL mkntfs_parse_llong(const char *string, const char *name, long long * tmp = strtoll(string, &end, 0); if (end && *end) { - ntfs_log_error("Cannot understand the %s '%s'.\n", name, string); + ntfs_log_error("Cannot understand the %s '%s'.\n", name, + string); return FALSE; } else { *num = tmp; @@ -346,7 +339,7 @@ static void mkntfs_init_options(struct mkntfs_options *opts2) */ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options *opts2) { - static const char *sopt = "-c:CfFhH:IlL:nN:p:qQs:S:TvVz:"; + 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' }, @@ -360,7 +353,6 @@ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options * { "mft-zone-multiplier",required_argument, NULL, 'z' }, { "no-action", no_argument, NULL, 'n' }, { "no-indexing", no_argument, NULL, 'I' }, - { "ntfs-version", required_argument, NULL, 'N' }, { "partition-start", required_argument, NULL, 'p' }, { "quick", no_argument, NULL, 'Q' }, { "quiet", no_argument, NULL, 'q' }, @@ -378,7 +370,8 @@ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options * int ver = 0; if (!argv || !opts2) { - ntfs_log_error("Internal error: invalid parameters to mkntfs_options.\n"); + ntfs_log_error("Internal error: invalid parameters to " + "mkntfs_options.\n"); return FALSE; } @@ -388,15 +381,18 @@ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options * 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)) + 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)) + if (!mkntfs_parse_long(optarg, "cluster size", + &opts2->cluster_size)) err++; break; case 'F': @@ -420,7 +416,8 @@ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options * if (!opts2->label) { opts2->label = argv[optind-1]; } else { - ntfs_log_error("You may only specify the label once.\n"); + ntfs_log_error("You may only specify the label " + "once.\n"); err++; } break; @@ -430,57 +427,46 @@ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options * case 'n': opts2->no_action = TRUE; break; - case 'N': /* ntfs-version */ - if ((opts2->ver_major == 0) && (opts2->ver_minor == 0)) { - if (strcmp(optarg , "1.2") == 0) { - opts2->ver_major = 1; - opts2->ver_minor = 2; -/* - FIXME: version 3.0 was not checked - } else if (strcmp(optarg , "3.0") == 0) { - opts2->ver_major = 3; - opts2->ver_minor = 0; -*/ } else if (strcmp(optarg , "3.1") == 0) { - opts2->ver_major = 3; - opts2->ver_minor = 1; - } else { - ntfs_log_error("NTFS version '%s' is invalid.\n", optarg); - err++; - } - } else { - ntfs_log_error("You may only specify the NTFS version once.\n"); - err++; - } - break; case 'p': - if (!mkntfs_parse_llong(optarg, "partition start", &opts2->part_start_sect)) + 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); + 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)) + 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)) + 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); + 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); + 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)) + if (!mkntfs_parse_long(optarg, "mft zone multiplier", + &opts2->mft_zone_multiplier)) err++; break; default: @@ -491,9 +477,11 @@ static BOOL mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options * (optopt == 's') || (optopt == 'S') || (optopt == 'N') || (optopt == 'z')) && (!optarg)) { - ntfs_log_error("Option '%s' requires an argument.\n", argv[optind-1]); + 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]); + ntfs_log_error("Unknown option '%s'.\n", + argv[optind - 1]); } err++; break; @@ -533,20 +521,6 @@ static struct timespec mkntfs_time(void) return ts; } -/** - * mkntfs_calloc - */ -static void *ntfs_calloc(size_t nmemb, size_t size) -{ - void *p; - - p = calloc(nmemb, size); - if (!p) - ntfs_log_perror("Failed to calloc() %lld bytes", - (long long)nmemb * size); - return p; -} - /** * append_to_bad_blocks */ @@ -602,9 +576,10 @@ static long long mkntfs_write(struct ntfs_device *dev, } /** - * ntfs_rlwrite - 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. + * 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. * @@ -678,7 +653,7 @@ static s64 ntfs_rlwrite(struct ntfs_device *dev, const runlist *rl, } if (delta) { int eo; - char *b = ntfs_calloc(1, delta); + char *b = ntfs_calloc(delta); if (!b) return -1; bytes_written = mkntfs_write(dev, b, delta); @@ -691,370 +666,6 @@ static s64 ntfs_rlwrite(struct ntfs_device *dev, const runlist *rl, return total; } - -/** - * dump_resident_attr_val - */ -static void dump_resident_attr_val(ATTR_TYPES type, char *val, u32 val_len) -{ - const char *don_t_know = "Don't know what to do with this attribute " - "type yet."; - const char *skip = "Skipping display of $%s attribute value.\n"; - const char *todo = "This is still work in progress."; - char *b = NULL; - int i, j; - - switch (type) { - case AT_STANDARD_INFORMATION: - /* TODO */ - printf("%s\n", todo); - return; - case AT_ATTRIBUTE_LIST: - /* TODO */ - printf("%s\n", todo); - return; - case AT_FILE_NAME: - /* TODO */ - printf("%s\n", todo); - return; - case AT_OBJECT_ID: - /* TODO */ - printf("%s\n", todo); - return; - case AT_SECURITY_DESCRIPTOR: - /* TODO */ - printf("%s\n", todo); - return; - case AT_VOLUME_NAME: - printf("Volume name length = %i\n", (unsigned int)val_len); - if (val_len) { - i = ntfs_ucstombs((ntfschar*)val, val_len, &b, 0); - if (i < 0) - printf("Volume name contains non-displayable " - "Unicode characters.\n"); - printf("Volume name = %s\n", b); - free(b); - } - return; - case AT_VOLUME_INFORMATION: -#define VOL_INF(x) ((VOLUME_INFORMATION *)(x)) - printf("NTFS version %i.%i\n", VOL_INF(val)->major_ver, - VOL_INF(val)->minor_ver); - i = VOL_INF(val)->flags; -#undef VOL_INF - printf("Volume flags = 0x%x: ", i); - if (!i) { - printf("NONE\n"); - return; - } - j = 0; - if (i & VOLUME_MODIFIED_BY_CHKDSK) { - printf("VOLUME_MODIFIED_BY_CHKDSK"); - j = 1; - } - if (i & VOLUME_REPAIR_OBJECT_ID) { - if (j) - printf(" | "); - printf("VOLUME_REPAIR_OBJECT_ID"); - j = 1; - } - if (i & VOLUME_DELETE_USN_UNDERWAY) { - if (j) - printf(" | "); - printf("VOLUME_DELETE_USN_UNDERWAY"); - j = 1; - } - if (i & VOLUME_MOUNTED_ON_NT4) { - if (j) - printf(" | "); - printf("VOLUME_MOUNTED_ON_NT4"); - j = 1; - } - if (i & VOLUME_UPGRADE_ON_MOUNT) { - if (j) - printf(" | "); - printf("VOLUME_UPGRADE_ON_MOUNT"); - j = 1; - } - if (i & VOLUME_RESIZE_LOG_FILE) { - if (j) - printf(" | "); - printf("VOLUME_RESIZE_LOG_FILE"); - j = 1; - } - if (i & VOLUME_IS_DIRTY) { - if (j) - printf(" | "); - printf("VOLUME_IS_DIRTY"); - j = 1; - } - printf("\n"); - return; - case AT_DATA: - printf(skip, "DATA"); - return; - case AT_INDEX_ROOT: - /* TODO */ - printf("collation_rule %u\n", le32_to_cpu(((INDEX_ROOT*)val)->collation_rule)); - printf("index.entries_offset %u\n", le32_to_cpu(((INDEX_ROOT*)val)->index.entries_offset)); - printf("index.index_length %u\n", le32_to_cpu(((INDEX_ROOT*)val)->index.index_length)); - printf("%s\n", todo); - return; - case AT_INDEX_ALLOCATION: - /* TODO */ - printf("%s\n", todo); - return; - case AT_BITMAP: - printf(skip, "BITMAP"); - return; - case AT_REPARSE_POINT: - /* TODO */ - printf("%s\n", todo); - return; - case AT_EA_INFORMATION: - /* TODO */ - printf("%s\n", don_t_know); - return; - case AT_EA: - /* TODO */ - printf("%s\n", don_t_know); - return; - case AT_LOGGED_UTILITY_STREAM: - /* TODO */ - printf("%s\n", don_t_know); - return; - default: - i = le32_to_cpu(type); - printf("Cannot display unknown %s defined attribute type 0x%x" - ".\n", (u32)i >= - le32_to_cpu(AT_FIRST_USER_DEFINED_ATTRIBUTE) ? - "user" : "system", i); - } -} - -/** - * dump_resident_attr - */ -static void dump_resident_attr(ATTR_RECORD *a) -{ - int i; - - i = le32_to_cpu(a->value_length); - printf("Attribute value length = %u (0x%x)\n", i, i); - i = le16_to_cpu(a->value_offset); - printf("Attribute value offset = %u (0x%x)\n", i, i); - i = a->resident_flags; - printf("Resident flags = 0x%x: ", i); - if (!i) - printf("NONE\n"); - else if (i & ~RESIDENT_ATTR_IS_INDEXED) - printf("UNKNOWN FLAG(S)\n"); - else - printf("RESIDENT_ATTR_IS_INDEXED\n"); - dump_resident_attr_val(a->type, (char*)a + le16_to_cpu(a->value_offset), - le32_to_cpu(a->value_length)); -} - -/** - * dump_mapping_pairs_array - */ -static void dump_mapping_pairs_array(char *b __attribute__((unused)), - unsigned int max_len __attribute__((unused))) -{ - /* TODO */ - return; -} - -/** - * dump_non_resident_attr - */ -static void dump_non_resident_attr(ATTR_RECORD *a) -{ - s64 l; - int i; - - l = sle64_to_cpu(a->lowest_vcn); - printf("Lowest VCN = %lli (0x%llx)\n", (long long)l, - (unsigned long long)l); - l = sle64_to_cpu(a->highest_vcn); - printf("Highest VCN = %lli (0x%llx)\n", (long long)l, - (unsigned long long)l); - printf("Mapping pairs array offset = 0x%x\n", - le16_to_cpu(a->mapping_pairs_offset)); - printf("Compression unit = 0x%x: %sCOMPRESSED\n", a->compression_unit, - a->compression_unit ? "" : "NOT "); - if (sle64_to_cpu(a->lowest_vcn)) - printf("Attribute is not the first extent. The following " - "sizes are meaningless:\n"); - l = sle64_to_cpu(a->allocated_size); - printf("Allocated size = %lli (0x%llx)\n", (long long)l, - (unsigned long long)l); - l = sle64_to_cpu(a->data_size); - printf("Data size = %lli (0x%llx)\n", (long long)l, - (unsigned long long)l); - l = sle64_to_cpu(a->initialized_size); - printf("Initialized size = %lli (0x%llx)\n", - (long long)l, (unsigned long long)l); - if (a->flags & ATTR_COMPRESSION_MASK) { - l = sle64_to_cpu(a->compressed_size); - printf("Compressed size = %lli (0x%llx)\n", - (long long)l, (unsigned long long)l); - } - i = le16_to_cpu(a->mapping_pairs_offset); - dump_mapping_pairs_array((char*)a + i, le32_to_cpu(a->length) - i); -} - -/** - * dump_attr_record - */ -static void dump_attr_record(ATTR_RECORD *a) -{ - unsigned int u; - char s[0x200]; - int i; - - printf("-- Beginning dump of attribute record. --\n"); - if (a->type == AT_END) { - printf("Attribute type = 0x%x ($END)\n", - (unsigned int)const_le32_to_cpu(AT_END)); - u = le32_to_cpu(a->length); - printf("Length of resident part = %u (0x%x)\n", u, u); - return; - } - u = le32_to_cpu(a->type); - for (i = 0; g_vol->attrdef[i].type; i++) - if (le32_to_cpu(g_vol->attrdef[i].type) >= u) - break; - if (g_vol->attrdef[i].type) { -#if 0 - printf("type = 0x%x\n", le32_to_cpu(g_vol->attrdef[i].type)); - { char *p = (char*)g_vol->attrdef[i].name; - printf("name = %c%c%c%c%c\n", *p, p[1], p[2], p[3], p[4]); - } -#endif - if (ntfs_ucstombs(g_vol->attrdef[i].name, 0x40, (char**)&s, sizeof(s)) < 0) { - ntfs_log_error("Could not convert Unicode string to single " - "byte string in current locale.\n"); - strncpy(s, "Error converting Unicode string", - sizeof(s)); - } - } else { - strncpy(s, "UNKNOWN_TYPE", sizeof(s)); - } - printf("Attribute type = 0x%x (%s)\n", u, s); - u = le32_to_cpu(a->length); - printf("Length of resident part = %u (0x%x)\n", u, u); - printf("Attribute is %sresident\n", a->non_resident ? "non-" : ""); - printf("Name length = %u unicode characters\n", a->name_length); - printf("Name offset = %u (0x%x)\n", cpu_to_le16(a->name_offset), - cpu_to_le16(a->name_offset)); - u = a->flags; - if (a->name_length) { - if (ntfs_ucstombs((ntfschar*)((char*)a + cpu_to_le16(a->name_offset)), - min(sizeof(s), a->name_length + 1U), (char**)&s, sizeof(s)) < 0) { - ntfs_log_error("Could not convert Unicode string to single " - "byte string in current locale.\n"); - strncpy(s, "Error converting Unicode string", - sizeof(s)); - } - printf("Name = %s\n", s); - } - printf("Attribute flags = 0x%x: ", le16_to_cpu(u)); - if (!u) { - printf("NONE"); - } else { - int first = TRUE; - if (u & ATTR_COMPRESSION_MASK) { - if (u & ATTR_IS_COMPRESSED) { - printf("ATTR_IS_COMPRESSED"); - first = FALSE; - } - if ((u & ATTR_COMPRESSION_MASK) & ~ATTR_IS_COMPRESSED) { - if (!first) - printf(" | "); - else - first = FALSE; - printf("ATTR_UNKNOWN_COMPRESSION"); - } - } - if (u & ATTR_IS_ENCRYPTED) { - if (!first) - printf(" | "); - else - first = FALSE; - printf("ATTR_IS_ENCRYPTED"); - } - if (u & ATTR_IS_SPARSE) { - if (!first) - printf(" | "); - else - first = FALSE; - printf("ATTR_IS_SPARSE"); - } - } - printf("\n"); - printf("Attribute instance = %u\n", le16_to_cpu(a->instance)); - if (a->non_resident) { - dump_non_resident_attr(a); - } else { - dump_resident_attr(a); - } -} - -/** - * dump_mft_record - */ -__attribute__((unused)) -static void dump_mft_record(MFT_RECORD *m) -{ - ATTR_RECORD *a; - unsigned int u; - MFT_REF r; - - printf("-- Beginning dump of mft record. --\n"); - u = le32_to_cpu(m->magic); - printf("Mft record signature (magic) = %c%c%c%c\n", u & 0xff, - u >> 8 & 0xff, u >> 16 & 0xff, u >> 24 & 0xff); - u = le16_to_cpu(m->usa_ofs); - printf("Update sequence array offset = %u (0x%x)\n", u, u); - printf("Update sequence array size = %u\n", le16_to_cpu(m->usa_count)); - printf("$LogFile sequence number (lsn) = %llu\n", - (unsigned long long)le64_to_cpu(m->lsn)); - printf("Sequence number = %u\n", le16_to_cpu(m->sequence_number)); - printf("Reference (hard link) count = %u\n", - le16_to_cpu(m->link_count)); - u = le16_to_cpu(m->attrs_offset); - printf("First attribute offset = %u (0x%x)\n", u, u); - printf("Flags = %u: ", le16_to_cpu(m->flags)); - if (m->flags & MFT_RECORD_IN_USE) - printf("MFT_RECORD_IN_USE"); - else - printf("MFT_RECORD_NOT_IN_USE"); - if (m->flags & MFT_RECORD_IS_DIRECTORY) - printf(" | MFT_RECORD_IS_DIRECTORY"); - printf("\n"); - u = le32_to_cpu(m->bytes_in_use); - printf("Bytes in use = %u (0x%x)\n", u, u); - u = le32_to_cpu(m->bytes_allocated); - printf("Bytes allocated = %u (0x%x)\n", u, u); - r = le64_to_cpu(m->base_mft_record); - printf("Base mft record reference:\n\tMft record number = %llu\n\t" - "Sequence number = %u\n", (unsigned long long)MREF(r), - MSEQNO(r)); - printf("Next attribute instance = %u\n", - le16_to_cpu(m->next_attr_instance)); - a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset)); - printf("-- Beginning dump of attributes within mft record. --\n"); - while ((char*)a < (char*)m + le32_to_cpu(m->bytes_in_use)) { - dump_attr_record(a); - if (a->type == AT_END) - break; - a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); - }; - printf("-- End of attributes. --\n"); -} - - /** * make_room_for_attribute - make room for an attribute inside an mft record * @m: mft record @@ -1495,6 +1106,7 @@ static int mkntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, /** * 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. @@ -1553,8 +1165,8 @@ static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, err = -EOPNOTSUPP; goto err_out; } - if (flags & (ATTR_IS_ENCRYPTED || ATTR_IS_SPARSE)) { - ntfs_log_error("Encrypted/sparse attributes not supported yet.\n"); + if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported.\n"); err = -EOPNOTSUPP; goto err_out; } @@ -1628,26 +1240,26 @@ static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, a->instance = m->next_attr_instance; m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); - a->lowest_vcn = cpu_to_le64(0); - a->highest_vcn = cpu_to_le64(highest_vcn - 1LL); + a->lowest_vcn = 0; + a->highest_vcn = cpu_to_sle64(highest_vcn - 1LL); a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); memset(a->reserved1, 0, sizeof(a->reserved1)); /* FIXME: Allocated size depends on compression. */ - a->allocated_size = cpu_to_le64(highest_vcn * g_vol->cluster_size); - a->data_size = cpu_to_le64(val_len); + a->allocated_size = cpu_to_sle64(highest_vcn * g_vol->cluster_size); + a->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"); + ntfs_log_error("Unknown compression format. Reverting " + "to standard compression.\n"); a->flags &= ~ATTR_COMPRESSION_MASK; a->flags |= ATTR_IS_COMPRESSED; } a->compression_unit = 4; inited_size = val_len; /* FIXME: Set the compressed size. */ - a->compressed_size = cpu_to_le64(0); + a->compressed_size = 0; /* FIXME: Write out the compressed data. */ /* FIXME: err = build_mapping_pairs_compressed(); */ err = -EOPNOTSUPP; @@ -1655,21 +1267,22 @@ static int insert_positioned_attr_in_mft_record(MFT_RECORD *m, a->compression_unit = 0; bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, &inited_size); if (bw != val_len) { - ntfs_log_error("Error writing non-resident attribute value.\n"); + 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->initialized_size = cpu_to_le64(inited_size); + a->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); + ntfs_log_error("insert_positioned_attr_in_mft_record failed " + "with error %i.\n", err < 0 ? err : (int)bw); } err_out: if (ctx) @@ -1735,8 +1348,8 @@ static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, err = -EOPNOTSUPP; goto err_out; } - if (flags & (ATTR_IS_ENCRYPTED || ATTR_IS_SPARSE)) { - ntfs_log_error("Encrypted/sparse attributes not supported yet.\n"); + if (flags & (ATTR_IS_ENCRYPTED | ATTR_IS_SPARSE)) { + ntfs_log_error("Encrypted/sparse attributes not supported.\n"); err = -EOPNOTSUPP; goto err_out; } @@ -1812,29 +1425,29 @@ static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, a->instance = m->next_attr_instance; m->next_attr_instance = cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); - a->lowest_vcn = cpu_to_le64(0); + a->lowest_vcn = 0; for (i = 0; rl[i].length; i++) ; - a->highest_vcn = cpu_to_le64(rl[i].vcn - 1); + a->highest_vcn = cpu_to_sle64(rl[i].vcn - 1); a->mapping_pairs_offset = cpu_to_le16(hdr_size + ((name_len + 7) & ~7)); memset(a->reserved1, 0, sizeof(a->reserved1)); /* FIXME: Allocated size depends on compression. */ - a->allocated_size = cpu_to_le64((val_len + (g_vol->cluster_size - 1)) & + a->allocated_size = cpu_to_sle64((val_len + (g_vol->cluster_size - 1)) & ~(g_vol->cluster_size - 1)); - a->data_size = cpu_to_le64(val_len); - a->initialized_size = cpu_to_le64(val_len); + a->data_size = cpu_to_sle64(val_len); + a->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"); + ntfs_log_error("Unknown compression format. Reverting " + "to standard compression.\n"); a->flags &= ~ATTR_COMPRESSION_MASK; a->flags |= ATTR_IS_COMPRESSED; } a->compression_unit = 4; /* FIXME: Set the compressed size. */ - a->compressed_size = cpu_to_le64(0); + a->compressed_size = 0; /* FIXME: Write out the compressed data. */ /* FIXME: err = build_mapping_pairs_compressed(); */ err = -EOPNOTSUPP; @@ -1842,7 +1455,8 @@ static int insert_non_resident_attr_in_mft_record(MFT_RECORD *m, a->compression_unit = 0; bw = ntfs_rlwrite(g_vol->dev, rl, val, val_len, NULL); if (bw != val_len) { - ntfs_log_error("Error writing non-resident attribute value.\n"); + ntfs_log_error("Error writing non-resident attribute " + "value.\n"); return -errno; } err = ntfs_mapping_pairs_build(g_vol, (u8*)a + hdr_size + @@ -1949,7 +1563,10 @@ static int insert_resident_attr_in_mft_record(MFT_RECORD *m, a->length = cpu_to_le32(asize); a->non_resident = 0; a->name_length = name_len; - a->name_offset = const_cpu_to_le16(24); + 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) @@ -1976,7 +1593,7 @@ err_out: * Return 0 on success or -errno on error. */ static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, - u32 security_id) + le32 security_id) { STANDARD_INFORMATION si; int err, sd_size; @@ -1988,21 +1605,17 @@ static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, si.last_mft_change_time = si.creation_time; si.last_access_time = si.creation_time; si.file_attributes = flags; /* already LE */ - if (g_vol->major_ver < 3) { - memset(&si.reserved12, 0, sizeof(si.reserved12)); - } else { - si.maximum_versions = cpu_to_le32(0); - si.version_number = cpu_to_le32(0); - si.class_id = cpu_to_le32(0); - si.security_id = security_id; - if (si.security_id != 0) - sd_size = 72; - /* FIXME: $Quota support... */ - si.owner_id = cpu_to_le32(0); - si.quota_charged = cpu_to_le64(0ULL); - /* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */ - si.usn = cpu_to_le64(0ULL); - } + si.maximum_versions = cpu_to_le32(0); + si.version_number = cpu_to_le32(0); + si.class_id = cpu_to_le32(0); + si.security_id = security_id; + if (si.security_id != 0) + sd_size = 72; + /* FIXME: $Quota support... */ + si.owner_id = cpu_to_le32(0); + si.quota_charged = cpu_to_le64(0ULL); + /* FIXME: $UsnJrnl support... Not needed on fresh w2k3-volume */ + si.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); @@ -2016,7 +1629,7 @@ static int add_attr_std_info(MFT_RECORD *m, const FILE_ATTR_FLAGS flags, * * Return 0 on success or -errno on error. */ -static int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, +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, @@ -2031,14 +1644,14 @@ static int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, /* 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"); + 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)) { + 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_log_error("BUG: Standard information attribute not " + "present in file record.\n"); ntfs_attr_put_search_ctx(ctx); return -eo; } @@ -2046,7 +1659,7 @@ static int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, le16_to_cpu(ctx->attr->value_offset)); i = (strlen(file_name) + 1) * sizeof(ntfschar); fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = malloc(fn_size); + fn = ntfs_malloc(fn_size); if (!fn) { ntfs_attr_put_search_ctx(ctx); return -errno; @@ -2059,8 +1672,8 @@ static int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, fn->last_access_time = si->last_access_time; ntfs_attr_put_search_ctx(ctx); - fn->allocated_size = cpu_to_le64(allocated_size); - fn->data_size = cpu_to_le64(data_size); + 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) { @@ -2095,6 +1708,34 @@ static int add_attr_file_name(MFT_RECORD *m, const MFT_REF parent_dir, 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 * @@ -2255,7 +1896,7 @@ static int add_attr_index_root(MFT_RECORD *m, const char *name, int err, val_len; val_len = sizeof(INDEX_ROOT) + sizeof(INDEX_ENTRY_HEADER); - r = malloc(val_len); + r = ntfs_malloc(val_len); if (!r) return -errno; r->type = (indexed_attr_type == AT_FILE_NAME) ? AT_FILE_NAME : 0; @@ -2285,8 +1926,8 @@ static int add_attr_index_root(MFT_RECORD *m, const char *name, 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"); + ntfs_log_error("add_attr_index_root: index block size " + "is smaller than the sector size.\n"); free(r); return -EINVAL; } @@ -2441,7 +2082,6 @@ static int upgrade_to_large_index(MFT_RECORD *m, const char *name, err = -EINVAL; goto err_out; } - ntfs_attr_put_search_ctx(ctx); r = (INDEX_ROOT*)((char*)a + le16_to_cpu(a->value_offset)); re_end = (char*)r + le32_to_cpu(a->value_length); re_start = (char*)&r->index + le32_to_cpu(r->index.entries_offset); @@ -2453,7 +2093,7 @@ static int upgrade_to_large_index(MFT_RECORD *m, const char *name, err = add_attr_bitmap(m, name, name_len, ic, bmp, sizeof(bmp)); if (err) goto err_out; - ia_val = ntfs_calloc(1, index_block_size); + ia_val = ntfs_calloc(index_block_size); if (!ia_val) { err = -errno; goto err_out; @@ -2474,10 +2114,10 @@ static int upgrade_to_large_index(MFT_RECORD *m, const char *name, "Thank you.", NTFS_DEV_LIST); } /* Set USN to 1. */ - *(u16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = + *(le16*)((char*)ia_val + le16_to_cpu(ia_val->usa_ofs)) = cpu_to_le16(1); - ia_val->lsn = cpu_to_le64(0); - ia_val->index_block_vcn = cpu_to_le64(0); + ia_val->lsn = 0; + ia_val->index_block_vcn = 0; ia_val->index.ih_flags = LEAF_NODE; /* Align to 8-byte boundary. */ ia_val->index.entries_offset = cpu_to_le32((sizeof(INDEX_HEADER) + @@ -2519,8 +2159,7 @@ static int upgrade_to_large_index(MFT_RECORD *m, const char *name, goto err_out; } /* Set VCN pointer to 0LL. */ - *(VCN*)((char*)re + cpu_to_le16(re->length) - sizeof(VCN)) = - cpu_to_le64(0); + *(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; @@ -2537,6 +2176,7 @@ static int upgrade_to_large_index(MFT_RECORD *m, const char *name, goto err_out; } *idx = ia_val; + ntfs_attr_put_search_ctx(ctx); return 0; err_out: ntfs_attr_put_search_ctx(ctx); @@ -2778,9 +2418,8 @@ do_next: idx_entry = (INDEX_ENTRY*)((u8*)idx_entry + le16_to_cpu(idx_entry->length)); } - } else { + } else return -EINVAL; - } memmove((u8*)idx_entry + idx_size, (u8*)idx_entry, le32_to_cpu(m->bytes_in_use) - ((u8*)idx_entry - (u8*)m)); @@ -2817,10 +2456,10 @@ static int initialize_secure(char *sds, u32 sds_size, MFT_RECORD *m) 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(1, sizeof(INDEX_ENTRY)); + idx_entry_sdh = ntfs_calloc(sizeof(INDEX_ENTRY)); if (!idx_entry_sdh) return -errno; - idx_entry_sii = ntfs_calloc(1, sizeof(INDEX_ENTRY)); + idx_entry_sii = ntfs_calloc(sizeof(INDEX_ENTRY)); if (!idx_entry_sii) { free(idx_entry_sdh); return -errno; @@ -2884,15 +2523,15 @@ static int initialize_secure(char *sds, u32 sds_size, MFT_RECORD *m) */ static int initialize_quota(MFT_RECORD *m) { - int o_size, q1_size, q2_size, err; + 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(1, q1_size); + idx_entry_q1 = ntfs_calloc(q1_size); if (!idx_entry_q1) return errno; idx_entry_q1->data_offset = const_cpu_to_le16(0x14); @@ -2907,13 +2546,11 @@ static int initialize_quota(MFT_RECORD *m) + le16_to_cpu(idx_entry_q1->data_offset)); idx_entry_q1_data->version = const_cpu_to_le32(0x02); idx_entry_q1_data->flags = QUOTA_FLAG_DEFAULT_LIMITS; - if (g_vol->minor_ver == 0) - idx_entry_q1_data->flags |= QUOTA_FLAG_OUT_OF_DATE; idx_entry_q1_data->bytes_used = const_cpu_to_le64(0x00); idx_entry_q1_data->change_time = timespec2ntfs(mkntfs_time()); - idx_entry_q1_data->threshold = const_cpu_to_le64((s64)-1); - idx_entry_q1_data->limit = const_cpu_to_le64((s64)-1); - idx_entry_q1_data->exceeded_time = const_cpu_to_le64(0x00); + 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); @@ -2921,7 +2558,7 @@ static int initialize_quota(MFT_RECORD *m) return err; /* q index entry num 2 */ q2_size = 0x58; - idx_entry_q2 = ntfs_calloc(1, q2_size); + idx_entry_q2 = ntfs_calloc(q2_size); if (!idx_entry_q2) return errno; idx_entry_q2->data_offset = const_cpu_to_le16(0x14); @@ -2938,15 +2575,14 @@ static int initialize_quota(MFT_RECORD *m) 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 = timespec2ntfs(mkntfs_time());; - idx_entry_q2_data->threshold = const_cpu_to_le64((s64)-1); - idx_entry_q2_data->limit = const_cpu_to_le64((s64)-1); - idx_entry_q2_data->exceeded_time = const_cpu_to_le64(0x00); + 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; - idx_entry_q2_data->sid.identifier_authority.high_part = - const_cpu_to_le16(0x0000); - idx_entry_q2_data->sid.identifier_authority.low_part = - const_cpu_to_le32(0x05000000); + 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] = @@ -2957,7 +2593,7 @@ static int initialize_quota(MFT_RECORD *m) if (err) return err; o_size = 0x28; - idx_entry_o = ntfs_calloc(1, o_size); + idx_entry_o = ntfs_calloc(o_size); if (!idx_entry_o) return errno; idx_entry_o->data_offset = const_cpu_to_le16(0x20); @@ -2969,10 +2605,9 @@ static int initialize_quota(MFT_RECORD *m) 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; - idx_entry_o->key.sid.identifier_authority.high_part = - const_cpu_to_le16(0x0000); - idx_entry_o->key.sid.identifier_authority.low_part = - const_cpu_to_le32(0x05000000); + 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] = @@ -2998,7 +2633,7 @@ static int initialize_quota(MFT_RECORD *m) * * Return 0 on success or -errno on error. */ -static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, MFT_REF file_ref, +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; @@ -3031,7 +2666,8 @@ static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, MFT_REF file_ref, 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_attr1->file_name = %s\n", + __buf); free(__buf); } ntfs_log_debug("file_name_attr2->file_name_length = %i\n", @@ -3039,11 +2675,13 @@ static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, MFT_REF file_ref, 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); + 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); + ntfs_log_debug("file_name_attr2->file_name = %s\n", + __buf); free(__buf); } #endif @@ -3080,7 +2718,8 @@ static int insert_file_link_in_dir_index(INDEX_BLOCK *idx, MFT_REF file_ref, /* i = ntfs_file_values_compare(file_name, (FILE_NAME_ATTR*)&ie->key.file_name, 1, - CASE_SENSITIVE, g_vol->upcase, g_vol->upcase_len); + CASE_SENSITIVE, g_vol->upcase, + g_vol->upcase_len); */ i = ntfs_names_full_collate(file_name->file_name, file_name->file_name_length, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name, ((FILE_NAME_ATTR*)&ie->key.file_name)->file_name_length, @@ -3094,7 +2733,8 @@ do_next: #ifdef DEBUG /* Next entry. */ if (!ie->length) { - ntfs_log_debug("BUG: ie->length is zero, breaking out of loop.\n"); + ntfs_log_debug("BUG: ie->length is zero, breaking out " + "of loop.\n"); break; } #endif @@ -3103,8 +2743,8 @@ do_next: 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)); + 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. */ @@ -3130,8 +2770,8 @@ do_next: * * Return 0 on success or -errno on error. */ -static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, - MFT_RECORD *m_file, const MFT_REF ref_file, +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, @@ -3145,7 +2785,7 @@ static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, /* Create the file_name attribute. */ i = (strlen(file_name) + 1) * sizeof(ntfschar); fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = malloc(fn_size); + fn = ntfs_malloc(fn_size); if (!fn) return -errno; fn->parent_directory = ref_parent; @@ -3154,8 +2794,8 @@ static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, 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_le64(allocated_size); - fn->data_size = cpu_to_le64(data_size); + 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) { @@ -3198,8 +2838,8 @@ static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, 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)); + 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( @@ -3208,7 +2848,7 @@ static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, } /* Insert the index entry for file_name in @idx. */ idx_size = (fn_size + 7) & ~7; - idx_entry_new = ntfs_calloc(1, idx_size + 0x10); + idx_entry_new = ntfs_calloc(idx_size + 0x10); if (!idx_entry_new) return -errno; idx_entry_new->indexed_file = ref_file; @@ -3218,8 +2858,8 @@ static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, 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)); + 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); @@ -3246,8 +2886,8 @@ static int create_hardlink_res(MFT_RECORD *m_parent, const MFT_REF ref_parent, * * Return 0 on success or -errno on error. */ -static int create_hardlink(INDEX_BLOCK *idx, const MFT_REF ref_parent, - MFT_RECORD *m_file, const MFT_REF ref_file, +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, @@ -3260,7 +2900,7 @@ static int create_hardlink(INDEX_BLOCK *idx, const MFT_REF ref_parent, /* Create the file_name attribute. */ i = (strlen(file_name) + 1) * sizeof(ntfschar); fn_size = sizeof(FILE_NAME_ATTR) + i; - fn = malloc(fn_size); + fn = ntfs_malloc(fn_size); if (!fn) return -errno; fn->parent_directory = ref_parent; @@ -3270,8 +2910,8 @@ static int create_hardlink(INDEX_BLOCK *idx, const MFT_REF ref_parent, 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_le64(allocated_size); - fn->data_size = cpu_to_le64(data_size); + 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) { @@ -3334,6 +2974,56 @@ static int create_hardlink(INDEX_BLOCK *idx, const MFT_REF ref_parent, 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->data_offset = cpu_to_le16(data_ofs); + idx_entry_new->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 */ @@ -3465,7 +3155,7 @@ done: /** * mkntfs_get_page_size - detect the system's memory page size. */ -static long mkntfs_get_page_size() +static long mkntfs_get_page_size(void) { long page_size; #ifdef _SC_PAGESIZE @@ -3627,14 +3317,11 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol) 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) { - if (volume_size <= 512LL << 20) /* <= 512MB */ - vol->cluster_size = 512; - else if (volume_size <= 1LL << 30) /* ]512MB-1GB] */ - vol->cluster_size = 1024; - else if (volume_size <= 2LL << 30) /* ]1GB-2GB] */ - vol->cluster_size = 2048; - else - vol->cluster_size = 4096; + /* + * 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; @@ -3652,8 +3339,8 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol) return FALSE; } } - ntfs_log_quiet("Cluster size has been automatically set to %d " - "bytes.\n", vol->cluster_size); + 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)) { @@ -3760,7 +3447,8 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol) 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", vol->indx_record_size, page_size); + "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); @@ -3791,7 +3479,7 @@ static BOOL mkntfs_initialize_bitmaps(void) ~(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(1, g_lcn_bitmap_byte_size); + g_lcn_bitmap = ntfs_calloc(g_lcn_bitmap_byte_size); if (!g_lcn_bitmap) return FALSE; /* @@ -3801,13 +3489,10 @@ static BOOL mkntfs_initialize_bitmaps(void) for (i = g_vol->nr_clusters; i < (u64)g_lcn_bitmap_byte_size << 3; i++) ntfs_bit_set(g_lcn_bitmap, i, 1); /* - * Determine mft_size: (16 (1.2) or 27 (3.0+) mft records) or - * one cluster, whichever is bigger. + * Mft size is 27 (NTFS 3.0+) mft records or one cluster, whichever is + * bigger. */ - if (g_vol->major_ver >= 3) - g_mft_size = 27; - else - g_mft_size = 16; + 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; @@ -3820,15 +3505,14 @@ static BOOL mkntfs_initialize_bitmaps(void) 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(1, 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 = malloc(2 * sizeof(runlist)); - if (!g_rl_mft_bmp) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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; @@ -3890,11 +3574,10 @@ static BOOL mkntfs_initialize_rl_mft(void) */ g_mft_zone_end += g_mft_lcn; /* Create runlist for mft. */ - g_rl_mft = malloc(2 * sizeof(runlist)); - if (!g_rl_mft) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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 */ @@ -3912,11 +3595,10 @@ static BOOL mkntfs_initialize_rl_mft(void) ntfs_log_debug("$MFTMirr logical cluster number = 0x%llx\n", g_mftmirr_lcn); /* Create runlist for mft mirror. */ - g_rl_mftmirr = malloc(2 * sizeof(runlist)); - if (!g_rl_mftmirr) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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; /* @@ -3949,11 +3631,10 @@ static BOOL mkntfs_initialize_rl_logfile(void) u64 volume_size; /* Create runlist for log file. */ - g_rl_logfile = malloc(2 * sizeof(runlist)); - if (!g_rl_logfile) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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; @@ -3969,14 +3650,8 @@ static BOOL mkntfs_initialize_rl_logfile(void) g_logfile_size = 512LL * 1024; /* -> 512kiB */ else if (volume_size <= 200LL * 1024 * 1024) /* < 200MiB */ g_logfile_size = 2048LL * 1024; /* -> 2MiB */ - else if (g_vol->major_ver < 3) { - if (volume_size >= 400LL << 20) /* > 400MiB */ - g_logfile_size = 4 << 20; /* -> 4MiB */ - else - g_logfile_size = (volume_size / 100) & - ~(g_vol->cluster_size - 1); - } else { - /* + 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: @@ -4034,11 +3709,10 @@ static BOOL mkntfs_initialize_rl_boot(void) { int i, j; /* Create runlist for $Boot. */ - g_rl_boot = malloc(2 * sizeof(runlist)); - if (!g_rl_boot) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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; /* @@ -4062,11 +3736,10 @@ static BOOL mkntfs_initialize_rl_boot(void) static BOOL mkntfs_initialize_rl_bad(void) { /* Create runlist for $BadClus, $DATA named stream $Bad. */ - g_rl_bad = malloc(2 * sizeof(runlist)); - if (!g_rl_bad) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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; /* @@ -4093,14 +3766,13 @@ static BOOL mkntfs_fill_device_with_zeroes(void) */ int i; ssize_t bw; - unsigned long long position, mid_clust; + 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%%"); - mid_clust = (volume_size >> 1) / g_vol->cluster_size; for (position = 0; position < (unsigned long long)g_vol->nr_clusters; position++) { if (!(position % (int)(progress_inc+1))) { @@ -4119,15 +3791,6 @@ static BOOL mkntfs_fill_device_with_zeroes(void) "system.\n"); return FALSE; } - if (position == mid_clust && - (g_vol->major_ver < 1 || - (g_vol->major_ver == 1 && - g_vol->minor_ver < 2))) { - ntfs_log_error("Error: Bad cluster found in " - "location reserved for system " - "file $Boot.\n"); - return FALSE; - } /* Add the baddie to our bad blocks list. */ if (!append_to_bad_blocks(position)) return FALSE; @@ -4150,10 +3813,7 @@ static BOOL mkntfs_fill_device_with_zeroes(void) if (bw != -1 || errno != EIO) { ntfs_log_error("This should not happen.\n"); return FALSE; - } else if (i + 1ull == position && - (g_vol->major_ver >= 2 || - (g_vol->major_ver == 1 && - g_vol->minor_ver >= 2))) { + } else if (i + 1ull == position) { ntfs_log_error("Error: Bad cluster found in " "location reserved for system " "file $Boot.\n"); @@ -4231,11 +3891,15 @@ static BOOL mkntfs_sync_index_record(INDEX_ALLOCATION* idx, MFT_RECORD* m, return TRUE; } - /** * create_file_volume - */ -static BOOL create_file_volume(MFT_RECORD *m, MFT_REF root_ref, VOLUME_FLAGS fl) +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; @@ -4257,12 +3921,19 @@ static BOOL create_file_volume(MFT_RECORD *m, MFT_REF root_ref, VOLUME_FLAGS fl) 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); + 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)); + ntfs_log_error("Couldn't create $Volume: %s\n", + strerror(-err)); return FALSE; } return TRUE; @@ -4288,6 +3959,8 @@ static int create_backup_boot_sector(u8 *buff) size = 512; if (size < opts.sector_size) size = opts.sector_size; + if (size < opts.cluster_size) + size = opts.cluster_size; if (g_vol->dev->d_ops->seek(g_vol->dev, (opts.num_sectors + 1) * opts.sector_size - size, SEEK_SET) == (off_t)-1) { ntfs_log_perror("Seek failed"); @@ -4324,8 +3997,8 @@ static BOOL mkntfs_create_root_structures(void) { NTFS_BOOT_SECTOR *bs; MFT_RECORD *m; - MFT_REF root_ref; - MFT_REF extend_ref; + leMFT_REF root_ref; + leMFT_REF extend_ref; int i; int j; int err; @@ -4334,13 +4007,12 @@ static BOOL mkntfs_create_root_structures(void) VOLUME_FLAGS volume_flags = 0; int nr_sysfiles; u8 *buf_log = NULL; - int buf_sds_first_size = 0; - int buf_sds_size = 0; - char *buf_sds_init = NULL; - char *buf_sds = NULL; + int buf_sds_first_size; + char *buf_sds; + GUID vol_guid; ntfs_log_quiet("Creating NTFS volume structures.\n"); - nr_sysfiles = g_vol->major_ver < 3 ? 16 : 27; + 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 @@ -4353,7 +4025,8 @@ static BOOL mkntfs_create_root_structures(void) 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"); + ntfs_log_error("Failed to layout system mft records." + "\n"); return FALSE; } if (i == 0 || i > 23) @@ -4370,7 +4043,8 @@ static BOOL mkntfs_create_root_structures(void) 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"); + ntfs_log_error("Failed to layout mft record." + "\n"); return FALSE; } m->flags = cpu_to_le16(0); @@ -4382,12 +4056,11 @@ static BOOL mkntfs_create_root_structures(void) * to each as well as marking them in use in the mft bitmap. */ for (i = 0; i < nr_sysfiles; i++) { - u32 file_attrs; + le32 file_attrs; m = (MFT_RECORD*)(g_buf + i * g_vol->mft_record_size); if (i < 16 || i > 23) { - if (g_vol->major_ver >= 3 && g_vol->minor_ver >= 1) - m->mft_record_number = cpu_to_le32(i); + m->mft_record_number = cpu_to_le32(i); m->flags |= MFT_RECORD_IN_USE; ntfs_bit_set(g_mft_bitmap, 0LL + i, 1); } @@ -4399,33 +4072,27 @@ static BOOL mkntfs_create_root_structures(void) if (opts.enable_compression) file_attrs |= FILE_ATTR_COMPRESSED; } - - if (g_vol->major_ver < 3) { + /* 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(0)); + 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 { - /* 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)); - } + add_attr_std_info(m, file_attrs, + cpu_to_le32(0x00)); } } /* The root directory mft reference. */ @@ -4440,19 +4107,8 @@ static BOOL mkntfs_create_root_structures(void) FILE_ATTR_I30_INDEX_PRESENT, 0, 0, ".", FILE_NAME_WIN32_AND_DOS); if (!err) { - if (g_vol->major_ver == 1) { - init_system_file_sd(FILE_root, &sd, &i); - err = add_attr_sd(m, sd, i); - } else if (NTFS_V3_0(g_vol->major_ver, g_vol->minor_ver)) { - init_system_file_sd(FILE_root, &sd, &i); - err = add_attr_sd(m, sd, i); - } else if (NTFS_V3_1(g_vol->major_ver, g_vol->minor_ver)) { - init_root_sd_31(&sd, &i); - err = add_attr_sd(m, sd, i); - } else { - ntfs_log_error("BUG: Unsupported NTFS version\n"); - return FALSE; - } + init_root_sd(&sd, &i); + err = add_attr_sd(m, sd, i); } /* FIXME: This should be IGNORE_CASE */ if (!err) @@ -4466,14 +4122,16 @@ static BOOL mkntfs_create_root_structures(void) ATTR_RECORD *a; ctx = ntfs_attr_get_search_ctx(NULL, m); if (!ctx) { - ntfs_log_perror("Failed to allocate attribute search context"); + 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)) { + 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"); + ntfs_log_error("BUG: $FILE_NAME attribute not found." + "\n"); return FALSE; } a = ctx->attr; @@ -4488,7 +4146,6 @@ static BOOL mkntfs_create_root_structures(void) strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ /* 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; @@ -4500,10 +4157,6 @@ static BOOL mkntfs_create_root_structures(void) g_mft_size, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, "$MFT", FILE_NAME_WIN32_AND_DOS); - if (!err && g_vol->major_ver == 1) { - init_system_file_sd(FILE_MFT, &sd, &i); - err = add_attr_sd(m, sd, i); - } /* 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, @@ -4512,8 +4165,6 @@ static BOOL mkntfs_create_root_structures(void) ntfs_log_error("Couldn't create $MFT: %s\n", strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ - 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, @@ -4525,22 +4176,16 @@ static BOOL mkntfs_create_root_structures(void) 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 && g_vol->major_ver == 1) { - init_system_file_sd(FILE_MFTMirr, &sd, &i); - err = add_attr_sd(m, sd, i); - } if (err < 0) { - ntfs_log_error("Couldn't create $MFTMirr: %s\n", strerror(-err)); + ntfs_log_error("Couldn't create $MFTMirr: %s\n", + strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ ntfs_log_verbose("Creating $LogFile (mft record 2)\n"); m = (MFT_RECORD*)(g_buf + 2 * g_vol->mft_record_size); - buf_log = malloc(g_logfile_size); - if (!buf_log) { - ntfs_log_perror("Failed to allocate internal buffer"); + 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); @@ -4552,20 +4197,15 @@ static BOOL mkntfs_create_root_structures(void) g_logfile_size, g_logfile_size, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, "$LogFile", FILE_NAME_WIN32_AND_DOS); - if (!err && g_vol->major_ver == 1) { - init_system_file_sd(FILE_LogFile, &sd, &i); - err = add_attr_sd(m, sd, i); - } if (err < 0) { - ntfs_log_error("Couldn't create $LogFile: %s\n", strerror(-err)); + ntfs_log_error("Couldn't create $LogFile: %s\n", + strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ - 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); + 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), @@ -4578,10 +4218,10 @@ static BOOL mkntfs_create_root_structures(void) err = add_attr_sd(m, sd, i); } if (err < 0) { - ntfs_log_error("Couldn't create $AttrDef: %s\n", strerror(-err)); + ntfs_log_error("Couldn't create $AttrDef: %s\n", + strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ 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 */ @@ -4595,23 +4235,18 @@ static BOOL mkntfs_create_root_structures(void) 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, + (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 && g_vol->major_ver == 1) { - init_system_file_sd(FILE_Bitmap, &sd, &i); - err = add_attr_sd(m, sd, i); - } if (err < 0) { ntfs_log_error("Couldn't create $Bitmap: %s\n", strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ - ntfs_log_verbose("Creating $Boot (mft record 7)\n"); m = (MFT_RECORD*)(g_buf + 7 * g_vol->mft_record_size); - bs = ntfs_calloc(1, 8192); + bs = ntfs_calloc(8192); if (!bs) return FALSE; memcpy(bs, boot_array, sizeof(boot_array)); @@ -4625,8 +4260,8 @@ static BOOL mkntfs_create_root_structures(void) 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); + 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); @@ -4641,12 +4276,13 @@ static BOOL mkntfs_create_root_structures(void) 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); + 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", + ntfs_log_error("BUG: calculated clusters_per_mft_record" + " is wrong (= 0x%x)\n", bs->clusters_per_mft_record); return FALSE; } @@ -4659,10 +4295,12 @@ static BOOL mkntfs_create_root_structures(void) 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) { + 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", + ntfs_log_error("BUG: calculated " + "clusters_per_index_record is wrong " + "(= 0x%x)\n", bs->clusters_per_index_record); return FALSE; } @@ -4671,8 +4309,8 @@ static BOOL mkntfs_create_root_structures(void) 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_sle64(((s64)random() << 32) | - ((s64)random() & 0xffffffff)); + 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. @@ -4684,7 +4322,8 @@ static BOOL mkntfs_create_root_structures(void) 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); + 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), @@ -4710,7 +4349,19 @@ static BOOL mkntfs_create_root_structures(void) volume_flags |= VOLUME_IS_DIRTY; } free(bs); - if (!create_file_volume(m, root_ref, volume_flags)) +#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*)&vol_guid); + } else + memset(&vol_guid, 0, sizeof(vol_guid)); +#endif + if (!create_file_volume(m, root_ref, volume_flags, &vol_guid)) return FALSE; ntfs_log_verbose("Creating $BadClus (mft record 8)\n"); m = (MFT_RECORD*)(g_buf + 8 * g_vol->mft_record_size); @@ -4727,94 +4378,53 @@ static BOOL mkntfs_create_root_structures(void) 0LL, 0LL, FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, 0, "$BadClus", FILE_NAME_WIN32_AND_DOS); } - if (!err && g_vol->major_ver == 1) { - init_system_file_sd(FILE_BadClus, &sd, &i); - err = add_attr_sd(m, sd, i); - } if (err < 0) { - ntfs_log_error("Couldn't create $BadClus: %s\n", strerror(-err)); + 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; - /* dump_mft_record(m); */ - /* create $Quota (1.2) or $Secure (3.0+) */ - if (g_vol->major_ver < 3) { - ntfs_log_verbose("Creating $Quota (mft record 9)\n"); - m = (MFT_RECORD*)(g_buf + 9 * g_vol->mft_record_size); - 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(9, 9), 0LL, 0LL, - FILE_ATTR_HIDDEN | FILE_ATTR_SYSTEM, 0, - 0, "$Quota", FILE_NAME_WIN32_AND_DOS); - if (!err) { - init_system_file_sd(FILE_Secure, &sd, &i); - err = add_attr_sd(m, sd, i); - } - if (err < 0) { - ntfs_log_error("Couldn't create $Quota: %s\n", - strerror(-err)); + 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; - } - } else { - 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); - if (!err) { - if (g_vol->minor_ver == 0) { - buf_sds_first_size = 0x1E0; - buf_sds_size = 0x40000 + buf_sds_first_size; - buf_sds_init = ntfs_calloc(1, buf_sds_first_size); - if (!buf_sds_init) - return FALSE; - init_secure_30(buf_sds_init); - } else { - buf_sds_first_size = 0xFC; - buf_sds_size = 0x40000 + buf_sds_first_size; - buf_sds_init = ntfs_calloc(1, buf_sds_first_size); - if (!buf_sds_init) - return FALSE; - init_secure_31(buf_sds_init); - } - buf_sds = ntfs_calloc(1, buf_sds_size); - if (!buf_sds) { - free(buf_sds_init); - return FALSE; - } - memcpy(buf_sds, buf_sds_init, buf_sds_first_size); - memcpy(buf_sds + 0x40000, buf_sds_init, - buf_sds_first_size); - err = add_attr_data(m, "$SDS", 4, 0, 0, (u8*)buf_sds, - buf_sds_size); - free(buf_sds); - } - /* 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_init, buf_sds_first_size, m); - - free (buf_sds_init); - buf_sds_init = NULL; - if (err < 0) { - ntfs_log_error("Couldn't create $Secure: %s\n", - strerror(-err)); - 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; } - /* dump_mft_record(m); */ 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, @@ -4824,59 +4434,37 @@ static BOOL mkntfs_create_root_structures(void) 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, + ~(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 && g_vol->major_ver == 1) { - init_system_file_sd(FILE_UpCase, &sd, &i); - err = add_attr_sd(m, sd, i); - } if (err < 0) { ntfs_log_error("Couldn't create $UpCase: %s\n", strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ - - if (g_vol->major_ver < 3) { - ntfs_log_verbose("Creating empty record, marked as in use " - "(mft record 11)\n"); - m = (MFT_RECORD*)(g_buf + 11 * g_vol->mft_record_size); - err = add_attr_data(m, NULL, 0, 0, 0, NULL, 0); - if (!err) { - init_system_file_sd(11, &sd, &j); - err = add_attr_sd(m, sd, j); - } - if (err < 0) { - ntfs_log_error("Couldn't create system file 11 (0x0b): %s\n", - strerror(-err)); - return FALSE; - } - /* dump_mft_record(m); */ - } else { - ntfs_log_verbose("Creating $Extend (mft record 11)\n"); - /* - * $Extends 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_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 1.2 reserved system files (mft records 0xc-0xf) */ + /* 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); @@ -4886,80 +4474,82 @@ static BOOL mkntfs_create_root_structures(void) 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)); + ntfs_log_error("Couldn't create system file %i (0x%x): " + "%s\n", i, i, strerror(-err)); return FALSE; } - /* dump_mft_record(m); */ } /* create systemfiles for ntfs volumes (3.1) */ /* starting with file 24 (ignoring file 16-23) */ - if (g_vol->major_ver >= 3) { - 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; - } + 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); - 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); - 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", + /* 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, &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; - } + 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; } @@ -4988,6 +4578,9 @@ static int mkntfs_redirect(struct mkntfs_options *opts2) 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); @@ -4996,43 +4589,23 @@ static int mkntfs_redirect(struct mkntfs_options *opts2) goto done; } } - if (opts.ver_major) { - g_vol->major_ver = opts.ver_major; - g_vol->minor_ver = opts.ver_minor; - } else { - /* Create NTFS 3.1 (Windows XP) volumes by default. */ - g_vol->major_ver = 3; - g_vol->minor_ver = 1; - } 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 = malloc(g_vol->upcase_len * sizeof(ntfschar)); - if (!g_vol->upcase) { - ntfs_log_perror("Could not create upcase structure"); + g_vol->upcase = ntfs_malloc(g_vol->upcase_len * sizeof(ntfschar)); + if (!g_vol->upcase) goto done; - } - init_upcase_table(g_vol->upcase, g_vol->upcase_len * sizeof(ntfschar)); - if (g_vol->major_ver < 3) { - g_vol->attrdef = ntfs_calloc(1, 36000); - if (g_vol->attrdef) { - memcpy(g_vol->attrdef, attrdef_ntfs12_array, - sizeof(attrdef_ntfs12_array)); - g_vol->attrdef_len = 36000; - } - } else { - g_vol->attrdef = malloc(sizeof(attrdef_ntfs3x_array)); - if (g_vol->attrdef) { - memcpy(g_vol->attrdef, attrdef_ntfs3x_array, - sizeof(attrdef_ntfs3x_array)); - g_vol->attrdef_len = sizeof(attrdef_ntfs3x_array); - } - } + 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; @@ -5055,7 +4628,7 @@ static int mkntfs_redirect(struct mkntfs_options *opts2) if (!mkntfs_initialize_rl_boot()) goto done; /* Allocate a buffer large enough to hold the mft. */ - g_buf = ntfs_calloc(1, g_mft_size); + g_buf = ntfs_calloc(g_mft_size); if (!g_buf) goto done; /* Create runlist for $BadClus, $DATA named stream $Bad. */ @@ -5199,4 +4772,3 @@ int main(int argc, char *argv[]) done: return result; } - diff --git a/ntfsprogs/ntfscat.8.in b/ntfsprogs/ntfscat.8.in index 58e1ef50..1127a74a 100644 --- a/ntfsprogs/ntfscat.8.in +++ b/ntfsprogs/ntfscat.8.in @@ -1,7 +1,8 @@ .\" Copyright (c) 2003\-2005 Richard Russon. +.\" Copyright (c) 2007 Yura Pakhuchiy. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFSCAT 8 "November 2005" "ntfs-3g @VERSION@" +.TH NTFSCAT 8 "September 2007" "ntfs-3g @VERSION@" .SH NAME ntfscat \- print NTFS files and streams on the standard output .SH SYNOPSIS @@ -133,5 +134,8 @@ The manual pages are available online at: http://man.linux-ntfs.org/ .hy .SH SEE ALSO +Read \fBlibntfs\fR(8) for details how to access encrypted files. +.sp +.BR libntfs (8), .BR ntfsls (8), .BR ntfsprogs (8) diff --git a/ntfsprogs/ntfscat.c b/ntfsprogs/ntfscat.c index 481a6730..7c5ba3f3 100644 --- a/ntfsprogs/ntfscat.c +++ b/ntfsprogs/ntfscat.c @@ -4,6 +4,7 @@ * Copyright (c) 2003-2005 Richard Russon * Copyright (c) 2003-2005 Anton Altaparmakov * Copyright (c) 2003-2005 Szabolcs Szakacsits + * Copyright (c) 2007 Yura Pakhuchiy * * This utility will concatenate files and print on the standard output. * @@ -60,11 +61,12 @@ static struct options opts; */ static void version(void) { - ntfs_log_info("\n%s v%s (libntfs-3g) - Concatenate files and print on the " - "standard output.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("\n%s v%s (libntfs-3g) - Concatenate files and print " + "on the standard output.\n\n", EXEC_NAME, VERSION); ntfs_log_info("Copyright (c) 2003-2005 Richard Russon\n"); ntfs_log_info("Copyright (c) 2003-2005 Anton Altaparmakov\n"); ntfs_log_info("Copyright (c) 2003-2005 Szabolcs Szakacsits\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); } @@ -86,7 +88,8 @@ static void usage(void) " -q, --quiet Less output\n" " -V, --version Version information\n" " -v, --verbose More output\n\n", - //" -r --raw Display the compressed or encrypted file", +// Does not work for compressed files at present so leave undocumented... +// " -r --raw Display the raw data (e.g. for compressed or encrypted file)", EXEC_NAME); ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home); } @@ -130,15 +133,15 @@ static int parse_attribute(const char *value, ATTR_TYPES *attr) for (i = 0; attr_name[i]; i++) { if ((strcmp(value, attr_name[i]) == 0) || - (strcmp(value, attr_name[i]+1) == 0)) { - *attr = (ATTR_TYPES) ((i+1)*16); + (strcmp(value, attr_name[i] + 1) == 0)) { + *attr = (ATTR_TYPES)cpu_to_le32((i + 1) * 16); return 1; } } num = strtol(value, NULL, 0); if ((num > 0) && (num < 257)) { - *attr = (ATTR_TYPES) num; + *attr = (ATTR_TYPES)cpu_to_le32(num); return 1; } @@ -156,7 +159,7 @@ static int parse_attribute(const char *value, ATTR_TYPES *attr) */ static int parse_options(int argc, char **argv) { - static const char *sopt = "-a:fh?i:n:qVv"; + static const char *sopt = "-a:fh?i:n:qVvr"; static const struct option lopt[] = { { "attribute", required_argument, NULL, 'a' }, { "attribute-name", required_argument, NULL, 'n' }, @@ -166,6 +169,7 @@ static int parse_options(int argc, char **argv) { "quiet", no_argument, NULL, 'q' }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, NULL, 'v' }, + { "raw", no_argument, NULL, 'r' }, { NULL, 0, NULL, 0 } }; @@ -179,7 +183,7 @@ static int parse_options(int argc, char **argv) opterr = 0; /* We'll handle the errors, thank you. */ opts.inode = -1; - opts.attr = -1; + opts.attr = cpu_to_le32(-1); opts.attr_name = NULL; opts.attr_name_len = 0; @@ -187,17 +191,19 @@ static int parse_options(int argc, char **argv) switch (c) { case 1: /* A non-option argument */ if (!opts.device) { - opts.device = argv[optind-1]; + opts.device = argv[optind - 1]; } else if (!opts.file) { - opts.file = argv[optind-1]; + opts.file = argv[optind - 1]; } else { - ntfs_log_error("You must specify exactly one file.\n"); + ntfs_log_error("You must specify exactly one " + "file.\n"); err++; } break; case 'a': - if (opts.attr != (ATTR_TYPES)-1) { - ntfs_log_error("You must specify exactly one attribute.\n"); + if (opts.attr != cpu_to_le32(-1)) { + ntfs_log_error("You must specify exactly one " + "attribute.\n"); } else if (parse_attribute(optarg, &attr) > 0) { opts.attr = attr; break; @@ -232,7 +238,8 @@ static int parse_options(int argc, char **argv) opts.attr_name_len = ntfs_mbstoucs_libntfscompat(optarg, &opts.attr_name, 0); if (opts.attr_name_len < 0) { - ntfs_log_perror("Invalid attribute name '%s'", optarg); + ntfs_log_perror("Invalid attribute name '%s'", + optarg); usage(); } @@ -247,6 +254,9 @@ static int parse_options(int argc, char **argv) opts.verbose++; ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); break; + case 'r': + opts.raw = TRUE; + break; default: ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]); err++; @@ -312,8 +322,7 @@ static int index_get_size(ntfs_inode *inode) return 0; // not a directory iroot = (INDEX_ROOT*)((u8*)attr90 + le16_to_cpu(attr90->value_offset)); - - return iroot->index_block_size; + return le32_to_cpu(iroot->index_block_size); } /** @@ -335,7 +344,8 @@ static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type, attr = ntfs_attr_open(inode, type, name, namelen); if (!attr) { - ntfs_log_error("Cannot find attribute type 0x%lx.\n", (long) type); + ntfs_log_error("Cannot find attribute type 0x%x.\n", + le32_to_cpu(type)); free(buffer); return 1; } @@ -349,10 +359,11 @@ static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type, offset = 0; for (;;) { - if (block_size > 0) { + if (!opts.raw && block_size > 0) { // These types have fixup bytes_read = ntfs_attr_mst_pread(attr, offset, 1, block_size, buffer); - bytes_read *= block_size; + if (bytes_read > 0) + bytes_read *= block_size; } else { bytes_read = ntfs_attr_pread(attr, offset, bufsize, buffer); } @@ -399,7 +410,8 @@ int main(int argc, char *argv[]) utils_set_locale(); - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + vol = utils_mount_volume(opts.device, MS_RDONLY | + (opts.force ? MS_RECOVER : 0)); if (!vol) { ntfs_log_perror("ERROR: couldn't mount volume"); return 1; @@ -416,7 +428,7 @@ int main(int argc, char *argv[]) } attr = AT_DATA; - if (opts.attr != (ATTR_TYPES)-1) + if (opts.attr != cpu_to_le32(-1)) attr = opts.attr; result = cat(vol, inode, attr, opts.attr_name, opts.attr_name_len); diff --git a/ntfsprogs/ntfscat.h b/ntfsprogs/ntfscat.h index 75b2b068..cf474b48 100644 --- a/ntfsprogs/ntfscat.h +++ b/ntfsprogs/ntfscat.h @@ -38,6 +38,7 @@ struct options { int force; /* Override common sense */ int quiet; /* Less output */ int verbose; /* Extra output */ + BOOL raw; /* Raw data output */ }; #endif /* _NTFSCAT_H_ */ diff --git a/ntfsprogs/ntfsck.c b/ntfsprogs/ntfsck.c new file mode 100644 index 00000000..5fb1ddd3 --- /dev/null +++ b/ntfsprogs/ntfsck.c @@ -0,0 +1,868 @@ +/** + * ntfsck - Part of the Linux-NTFS project. + * + * Copyright (c) 2006 Yuval Fledel + * + * This utility will check and fix errors on an NTFS volume. + * + * 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 + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "config.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "cluster.h" +#include "utils.h" + +#define RETURN_FS_ERRORS_CORRECTED (1) +#define RETURN_SYSTEM_NEEDS_REBOOT (2) +#define RETURN_FS_ERRORS_LEFT_UNCORRECTED (4) +#define RETURN_OPERATIONAL_ERROR (8) +#define RETURN_USAGE_OR_SYNTAX_ERROR (16) +#define RETURN_CANCELLED_BY_USER (32) +/* Where did 64 go? */ +#define RETURN_SHARED_LIBRARY_ERROR (128) + +/* todo: command line: (everything is optional) + * fsck-frontend options: + * -C [fd] : display progress bar (send it to the file descriptor if specified) + * -T : don't show the title on startup + * fsck-checker options: + * -a : auto-repair. no questions. (optional: if marked clean and -f not specified, just check if mounable) + * -p : auto-repair safe. no questions (optional: same) + * -n : only check. no repair. + * -r : interactively repair. + * -y : always yes. + * -v : verbose. + * -V : version. + * taken from fsck.ext2 + * -b sb : use the superblock from sb. For corrupted volumes. (do we want separete boot/mft options?) + * -c : use badblocks(8) to find bad blocks (R/O mode) and add the findings to $Bad. + * -C fd : write competion info to fd. If 0, print a completion bar. + * -d : debugging output. + * -D : rebalance indices. + * -f : force checking even if marked clean. + * -F : flush buffers before beginning. (for time-benchmarking) + * -k : When used with -c, don't erase previous $Bad items. + * -n : Open fs as readonly. assume always no. (why is it needed if -r is not specified?) + * -t : Print time statistics. + * taken from fsck.reiserfs + * --rebuild-sb : try to find $MFT start and rebuild the boot sector. + * --rebuild-tree : scan for items and rebuild the indices that point to them (0x30, $SDS, etc.) + * --clean-reserved: zero rezerved fields. (use with care!) + * --adjust-size -z: insert a sparse hole if the data_size is larger than the size marked in the runlist. + * --logfile file : report corruptions (unlike other errors) to a file instead of stderr. + * --nolog : don't report corruptions at all. + * --quiet -q : no progress bar. + * taken from fsck.msdos + * -w : flush after every write. + * - do n passes. (only 2 in fsck.msdos. second should not report errors. Bonus: stop when error list does not change) + * taken from fsck.jfs + * --omit-journal-reply: self-descriptive (why would someone do that?) + * --replay-journal-only: self-descriptive. don't check otherwise. + * taken from fsck.xfs + * -s : only serious errors should be reported. + * -i ino : verbose behaviour only for inode ino. + * -b bno : verbose behaviour only for cluster bno. + * -L : zero log. + * inspired by others + * - don't do cluster accounting. + * - don't do mft record accounting. + * - don't do file names accounting. + * - don't do security_id accounting. + * - don't check acl inheritance problems. + * - undelete unused mft records. (bonus: different options for 100% salvagable and less) + * - error-level-report n: only report errors above this error level + * - error-level-repair n: only repair errors below this error level + * - don't fail on ntfsclone metadata pruning. + * signals: + * SIGUSR1 : start displaying progress bar + * SIGUSR2 : stop displaying progress bar. + */ + +/* Assuming NO_NTFS_DEVICE_DEFAULT_IO_OPS is not set */ + +static int errors = 0; +static int unsupported = 0; + +static short bytes_per_sector, sectors_per_cluster; +//static s64 mft_offset, mftmirr_offset; +static s64 current_mft_record; + +/** + * This is just a preliminary volume. + * Filled while checking the boot sector and used in the preliminary MFT check. + */ +static ntfs_volume vol; + +static runlist_element *mft_rl, *mft_bitmap_rl; + +#define check_failed(FORMAT, ARGS...) \ + do { \ + errors++; \ + ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__, \ + NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS); \ + } while (0); + +/** + * 0 success. + * 1 fail. + */ +static int assert_u32_equal(u32 val, u32 ok, const char *name) +{ + if (val!=ok) { + check_failed("Assertion failed for '%lld:%s'. should be 0x%x, " + "was 0x%x.\n", current_mft_record, name, + (int)ok, (int)val); + //errors++; + return 1; + } + return 0; +} + +static int assert_u32_noteq(u32 val, u32 wrong, const char *name) +{ + if (val==wrong) { + check_failed("Assertion failed for '%lld:%s'. should not be " + "0x%x.\n", current_mft_record, name, (int)wrong); + return 1; + } + return 0; +} + +static int assert_u32_lesseq(u32 val1, u32 val2, const char *name) +{ + if (val1 > val2) { + check_failed("Assertion failed for '%s'. 0x%x > 0x%x\n", + name, (int)val1, (int)val2); + //errors++; + return 1; + } + return 0; +} + +static int assert_u32_less(u32 val1, u32 val2, const char *name) +{ + if (val1 >= val2) { + check_failed("Assertion failed for '%s'. 0x%x >= 0x%x\n", + name, (int)val1, (int)val2); + //errors++; + return 1; + } + return 0; +} + +/** + * Return: 0 ok, 1 error. + * + * todo: may we use ntfs_boot_sector_is_ntfs() instead? + * It already does the checks but will not be able to fix anything. + */ +static BOOL verify_boot_sector(struct ntfs_device *dev) +{ + u8 buf[512]; + NTFS_BOOT_SECTOR *ntfs_boot = (NTFS_BOOT_SECTOR *)&buf; + //u32 bytes_per_cluster; + + current_mft_record = 9; + + if (dev->d_ops->pread(dev, buf, sizeof(buf), 0)!=sizeof(buf)) { + check_failed("Failed to read boot sector.\n"); + return 1; + } + + if ((buf[0]!=0xeb) || + ((buf[1]!=0x52) && (buf[1]!=0x5b)) || + (buf[2]!=0x90)) { + check_failed("Boot sector: Bad jump.\n"); + } + if (ntfs_boot->oem_id != NTFS_SB_MAGIC) { + check_failed("Boot sector: Bad NTFS magic.\n"); + } + bytes_per_sector = le16_to_cpu(ntfs_boot->bpb.bytes_per_sector); + if (!bytes_per_sector) { + check_failed("Boot sector: Bytes per sector is 0.\n"); + } + if (bytes_per_sector%512) { + check_failed("Boot sector: Bytes per sector is not a multiple" + " of 512.\n"); + } + sectors_per_cluster = ntfs_boot->bpb.sectors_per_cluster; + + // todo: if partition, query bios and match heads/tracks? */ + + // Initialize some values from vol. We will need those later. + ntfs_boot_sector_parse(&vol, (NTFS_BOOT_SECTOR *)buf); + vol.dev = dev; + + return 0; +} + +/** + * Load the runlist of the attribute. + * + * Return NULL if an error. + * The caller is responsible on freeing the allocated memory if the result is not NULL. + * + * Set size_of_file_record to some reasonable size when in doubt (the Windows default is 1024.) + * + * attr_type must be little endian. + * + * This function has code duplication with check_file_record() and + * check_attr_record() but its goal is to be less strict. Thus the + * duplicated checks are the minimal required for not crashing. + * + * Assumes dev is open. + */ +static runlist *load_runlist(struct ntfs_device *dev, s64 offset_to_file_record, u32 attr_type, u32 size_of_file_record) +{ + u8 *buf; + u16 attrs_offset; + u32 length; + ATTR_RECORD *attr_rec; + + if (size_of_file_record<22) // offset to attrs_offset + return NULL; + + buf = (u8*)ntfs_malloc(size_of_file_record); + if (!buf) + return NULL; + + if (dev->d_ops->pread(dev, buf, size_of_file_record, offset_to_file_record)!=size_of_file_record) { + check_failed("Failed to read file record at offset %lld (0x%llx).\n", offset_to_file_record, offset_to_file_record); + return NULL; + } + + attrs_offset = le16_to_cpu(((MFT_RECORD*)buf)->attrs_offset); + // first attribute must be after the header. + if (attrs_offset<42) { + check_failed("First attribute must be after the header (%u).\n", (int)attrs_offset); + } + attr_rec = (ATTR_RECORD *)(buf + attrs_offset); + //printf("uv1.\n"); + + while ((u8*)attr_rec<=buf+size_of_file_record-4) { + + //printf("Attr type: 0x%x.\n", attr_rec->type); + // Check attribute record. (Only what is in the buffer) + if (attr_rec->type==AT_END) { + check_failed("Attribute 0x%x not found in file record at offset %lld (0x%llx).\n", (int)le32_to_cpu(attr_rec->type), offset_to_file_record, offset_to_file_record); + return NULL; + } + if ((u8*)attr_rec>buf+size_of_file_record-8) { + // not AT_END yet no room for the length field. + check_failed("Attribute 0x%x is not AT_END, yet no " + "room for the length field.\n", + (int)le32_to_cpu(attr_rec->type)); + return NULL; + } + + length = le32_to_cpu(attr_rec->length); + + // Check that this attribute does not overflow the mft_record + if ((u8*)attr_rec+length >= buf+size_of_file_record) { + check_failed("Attribute (0x%x) is larger than FILE record at offset %lld (0x%llx).\n", + (int)le32_to_cpu(attr_rec->type), offset_to_file_record, offset_to_file_record); + return NULL; + } + // todo: what ATTRIBUTE_LIST (0x20)? + + if (attr_rec->type==attr_type) { + // Eurika! + + // ntfs_mapping_pairs_decompress only use two values from vol. Just fake it. + // todo: it will also use vol->major_ver if defined(DEBUG). But only for printing purposes. + + // Assume ntfs_boot_sector_parse() was called. + return ntfs_mapping_pairs_decompress(&vol, attr_rec, NULL); + } + + attr_rec = (ATTR_RECORD*)((u8*)attr_rec+length); + } + // If we got here, there was an overflow. + check_failed("file record corrupted at offset %lld (0x%llx).\n", offset_to_file_record, offset_to_file_record); + return NULL; +} + +/** + * Return: >=0 last VCN + * LCN_EINVAL error. + */ +static VCN get_last_vcn(runlist *rl) +{ + VCN res; + + if (!rl) + return LCN_EINVAL; + + res = LCN_EINVAL; + while (rl->length) { + ntfs_log_verbose("vcn: %lld, length: %lld.\n", rl->vcn, + rl->length); + if (rl->vcn<0) + res = rl->vcn; + else + res = rl->vcn + rl->length; + rl++; + } + + return res; +} + +static u32 mft_bitmap_records; +static u8 *mft_bitmap_buf; + +/** + * Assumes mft_bitmap_rl is initialized. + * return: 0 ok. + * RETURN_OPERATIONAL_ERROR on error. + */ +static int mft_bitmap_load(struct ntfs_device *dev) +{ + VCN vcn; + u32 mft_bitmap_length; + + vcn = get_last_vcn(mft_bitmap_rl); + if (vcn<=LCN_EINVAL) { + mft_bitmap_buf = NULL; + /* This case should not happen, not even with on-disk errors */ + goto error; + } + + mft_bitmap_length = vcn * vol.cluster_size; + mft_bitmap_records = 8 * mft_bitmap_length * vol.cluster_size / + vol.mft_record_size; + + //printf("sizes: %d, %d.\n", mft_bitmap_length, mft_bitmap_records); + + mft_bitmap_buf = (u8*)ntfs_malloc(mft_bitmap_length); + if (!mft_bitmap_buf) + goto error; + if (ntfs_rl_pread(&vol, mft_bitmap_rl, 0, mft_bitmap_length, + mft_bitmap_buf)!=mft_bitmap_length) + goto error; + return 0; +error: + mft_bitmap_records = 0; + ntfs_log_error("Could not load $MFT/Bitmap.\n"); + return RETURN_OPERATIONAL_ERROR; +} + +/** + * -1 Error. + * 0 Unused record + * 1 Used record + * + * Assumes mft_bitmap_rl was initialized. + */ +static int mft_bitmap_get_bit(s64 mft_no) +{ + if (mft_no>=mft_bitmap_records) + return -1; + return ntfs_bit_get(mft_bitmap_buf, mft_no); +} + +/** + * @attr_rec: The attribute record to check + * @mft_rec: The parent FILE record. + * @buflen: The size of the FILE record. + * + * Return: + * NULL: Fatal error occured. Not sure where is the next record. + * otherwise: pointer to the next attribute record. + * + * The function only check fields that are inside this attr record. + * + * Assumes mft_rec is current_mft_record. + */ +static ATTR_REC *check_attr_record(ATTR_REC *attr_rec, MFT_RECORD *mft_rec, + u16 buflen) +{ + u16 name_offset; + u16 attrs_offset = le16_to_cpu(mft_rec->attrs_offset); + u32 attr_type = le32_to_cpu(attr_rec->type); + u32 length = le32_to_cpu(attr_rec->length); + + // Check that this attribute does not overflow the mft_record + if ((u8*)attr_rec+length >= ((u8*)mft_rec)+buflen) { + check_failed("Attribute (0x%x) is larger than FILE record (%lld).\n", + (int)attr_type, current_mft_record); + return NULL; + } + + // Attr type must be a multiple of 0x10 and 0x10<=x<=0x100. + if ((attr_type & ~0x0F0) && (attr_type != 0x100)) { + check_failed("Unknown attribute type 0x%x.\n", + (int)attr_type); + goto check_attr_record_next_attr; + } + + if (length<24) { + check_failed("Attribute %lld:0x%x Length too short (%u).\n", + current_mft_record, (int)attr_type, (int)length); + goto check_attr_record_next_attr; + } + + // If this is the first attribute: + // todo: instance number must be smaller than next_instance. + if ((u8*)attr_rec == ((u8*)mft_rec) + attrs_offset) { + if (!mft_rec->base_mft_record) + assert_u32_equal(attr_type, 0x10, + "First attribute type"); + // The following not always holds. + // attr 0x10 becomes instance 1 and attr 0x40 becomes 0. + //assert_u32_equal(attr_rec->instance, 0, + // "First attribute instance number"); + } else { + assert_u32_noteq(attr_type, 0x10, + "Not-first attribute type"); + // The following not always holds. + //assert_u32_noteq(attr_rec->instance, 0, + // "Not-first attribute instance number"); + } + //if (current_mft_record==938 || current_mft_record==1683 || current_mft_record==3152 || current_mft_record==22410) + //printf("Attribute %lld:0x%x instance: %u isbase:%d.\n", + // current_mft_record, (int)attr_type, (int)le16_to_cpu(attr_rec->instance), (int)mft_rec->base_mft_record); + // todo: instance is unique. + + // Check flags. + if (attr_rec->flags & ~(const_cpu_to_le16(0xc0ff))) { + check_failed("Attribute %lld:0x%x Unknown flags (0x%x).\n", + current_mft_record, (int)attr_type, + (int)le16_to_cpu(attr_rec->flags)); + } + + if (attr_rec->non_resident>1) { + check_failed("Attribute %lld:0x%x Unknown non-resident " + "flag (0x%x).\n", current_mft_record, + (int)attr_type, (int)attr_rec->non_resident); + goto check_attr_record_next_attr; + } + + name_offset = le16_to_cpu(attr_rec->name_offset); + /* + * todo: name must be legal unicode. + * Not really, information below in urls is about filenames, but I + * believe it also applies to attribute names. (Yura) + * http://blogs.msdn.com/michkap/archive/2006/09/24/769540.aspx + * http://blogs.msdn.com/michkap/archive/2006/09/10/748699.aspx + */ + + if (attr_rec->non_resident) { + // Non-Resident + + // Make sure all the fields exist. + if (length<64) { + check_failed("Non-resident attribute %lld:0x%x too short (%u).\n", + current_mft_record, (int)attr_type, (int)length); + goto check_attr_record_next_attr; + } + if (attr_rec->compression_unit && (length<72)) { + check_failed("Compressed attribute %lld:0x%x too short (%u).\n", + current_mft_record, (int)attr_type, (int)length); + goto check_attr_record_next_attr; + } + + // todo: name comes before mapping pairs, and after the header. + // todo: length==mapping_pairs_offset+length of compressed mapping pairs. + // todo: mapping_pairs_offset is 8-byte aligned. + + // todo: lowest vcn <= highest_vcn + // todo: if base record -> lowest vcn==0 + // todo: lowest_vcn!=0 -> attribute list is used. + // todo: lowest_vcn & highest_vcn are in the drive (0<=xvalue_offset); + u32 value_length = le32_to_cpu(attr_rec->value_length); + // Resident + if (attr_rec->name_length) { + if (name_offset < 24) + check_failed("Resident attribute with " + "name intersecting header.\n"); + if (value_offset < name_offset + + attr_rec->name_length) + check_failed("Named resident attribute " + "with value before name.\n"); + } + // if resident, length==value_length+value_offset + //assert_u32_equal(le32_to_cpu(attr_rec->value_length)+ + // value_offset, length, + // "length==value_length+value_offset"); + // if resident, length==value_length+value_offset + if (value_length+value_offset > length) { + check_failed("value_length(%d)+value_offset(%d)>length(%d) for attribute 0x%x.\n", (int)value_length, (int)value_offset, (int)length, (int)attr_type); + return NULL; + } + + // Check resident_flags. + if (attr_rec->resident_flags>0x01) { + check_failed("Unknown resident flags (0x%x) for attribute 0x%x.\n", (int)attr_rec->resident_flags, (int)attr_type); + } else if (attr_rec->resident_flags && (attr_type!=0x30)) { + check_failed("Resident flags mark attribute 0x%x as indexed.\n", (int)attr_type); + } + + // reservedR is 0. + assert_u32_equal(attr_rec->reservedR, 0, "Resident Reserved"); + + // todo: attribute must not be 0xa0 (not sure about 0xb0, 0xe0, 0xf0) + // todo: check content well-formness per attr_type. + } + return 0; +check_attr_record_next_attr: + return (ATTR_REC *)(((u8 *)attr_rec) + length); +} + +/** + * All checks that can be satisfied only by data from the buffer. + * No other [MFT records/metadata files] are required. + * + * The buffer is changed by removing the Update Sequence. + * + * Return: + * 0 Everything's cool. + * else Consider this record as damaged. + */ +static BOOL check_file_record(u8 *buffer, u16 buflen) +{ + u16 usa_count, usa_ofs, attrs_offset, usa; + u32 bytes_in_use, bytes_allocated, i; + MFT_RECORD *mft_rec = (MFT_RECORD *)buffer; + ATTR_REC *attr_rec; + + // check record magic + assert_u32_equal(mft_rec->magic, magic_FILE, "FILE record magic"); + // todo: records 16-23 must be filled in order. + // todo: what to do with magic_BAAD? + + // check usa_count+offset to update seq <= attrs_offset < + // bytes_in_use <= bytes_allocated <= buflen. + usa_ofs = le16_to_cpu(mft_rec->usa_ofs); + usa_count = le16_to_cpu(mft_rec->usa_count); + attrs_offset = le16_to_cpu(mft_rec->attrs_offset); + bytes_in_use = le32_to_cpu(mft_rec->bytes_in_use); + bytes_allocated = le32_to_cpu(mft_rec->bytes_allocated); + if (assert_u32_lesseq(usa_ofs+usa_count, attrs_offset, + "usa_ofs+usa_count <= attrs_offset") || + assert_u32_less(attrs_offset, bytes_in_use, + "attrs_offset < bytes_in_use") || + assert_u32_lesseq(bytes_in_use, bytes_allocated, + "bytes_in_use <= bytes_allocated") || + assert_u32_lesseq(bytes_allocated, buflen, + "bytes_allocated <= max_record_size")) { + return 1; + } + + + // We should know all the flags. + if (mft_rec->flags>0xf) { + check_failed("Unknown MFT record flags (0x%x).\n", + (unsigned int)mft_rec->flags); + } + // todo: flag in_use must be on. + + // Remove update seq & check it. + usa = *(u16*)(buffer+usa_ofs); // The value that should be at the end of every sector. + assert_u32_equal(usa_count-1, buflen/bytes_per_sector, "USA length"); + for (i=1;itype==AT_END) { + // Done. + return 0; + } + if ((u8*)attr_rec>buffer+buflen-8) { + // not AT_END yet no room for the length field. + check_failed("Attribute 0x%x is not AT_END, yet no " + "room for the length field.\n", + (int)le32_to_cpu(attr_rec->type)); + return 1; + } + + attr_rec = check_attr_record(attr_rec, mft_rec, buflen); + if (!attr_rec) + return 1; + } + // If we got here, there was an overflow. + return 1; + + // todo: an attribute should be at the offset to first attribute, and the offset should be inside the buffer. It should have the value of "next attribute id". + // todo: if base record, it should start with attribute 0x10. + + // Highlevel check of attributes. + // todo: Attributes are well-formed. + // todo: Room for next attribute in the end of the previous record. + + return FALSE; +} + +static void replay_log(ntfs_volume *vol) +{ + // At this time, only check that the log is fully replayed. + ntfs_log_warning("Unsupported: replay_log()\n"); + // todo: if logfile is clean, return success. + unsupported++; +} + +static void verify_mft_record(ntfs_volume *vol, s64 mft_num) +{ + u8 *buffer; + int is_used; + + current_mft_record = mft_num; + + is_used = mft_bitmap_get_bit(mft_num); + if (is_used<0) { + ntfs_log_error("Error getting bit value for record %lld.\n", mft_num); + } else if (!is_used) { + ntfs_log_verbose("Record %lld unused. Skipping.\n", mft_num); + return; + } + + buffer = ntfs_malloc(vol->mft_record_size); + if (!buffer) + goto verify_mft_record_error; + + ntfs_log_verbose("MFT record %lld\n", mft_num); + if (ntfs_attr_pread(vol->mft_na, mft_num*vol->mft_record_size, vol->mft_record_size, buffer) < 0) { + ntfs_log_perror("Couldn't read $MFT record %lld", mft_num); + goto verify_mft_record_error; + } + + check_file_record(buffer, vol->mft_record_size); + // todo: if offset to first attribute >= 0x30, number of mft record should match. + // todo: Match the "record is used" with the mft bitmap. + // todo: if this is not base, check that the parent is a base, and is in use, and pointing to this record. + + // todo: if base record: for each extent record: + // todo: verify_file_record + // todo: hard link count should be the number of 0x30 attributes. + // todo: Order of attributes. + // todo: make sure compression_unit is the same. + + return; +verify_mft_record_error: + + if (buffer) + free(buffer); + errors++; +} + +/** + * This function serves as bootstraping for the more comprehensive checks. + * It will load the MFT runlist and MFT/Bitmap runlist. + * It should not depend on other checks or we may have a circular dependancy. + * Also, this loadng must be forgiving, unlike the comprehensive checks. + */ +static int verify_mft_preliminary(struct ntfs_device *dev) +{ + current_mft_record = 0; + s64 mft_offset, mftmirr_offset; + int res; + + ntfs_log_trace("Entering verify_mft_preliminary().\n"); + // todo: get size_of_file_record from boot sector + // Load the first segment of the $MFT/DATA runlist. + mft_offset = vol.mft_lcn * vol.cluster_size; + mftmirr_offset = vol.mftmirr_lcn * vol.cluster_size; + mft_rl = load_runlist(dev, mft_offset, AT_DATA, 1024); + if (!mft_rl) { + check_failed("Loading $MFT runlist failed. Trying $MFTMirr.\n"); + mft_rl = load_runlist(dev, mftmirr_offset, AT_DATA, 1024); + } + if (!mft_rl) { + check_failed("Loading $MFTMirr runlist failed too. Aborting.\n"); + return RETURN_FS_ERRORS_LEFT_UNCORRECTED | RETURN_OPERATIONAL_ERROR; + } + // TODO: else { recover $MFT } // Use $MFTMirr to recover $MFT. + // todo: support loading the next runlist extents when ATTRIBUTE_LIST is used on $MFT. + // If attribute list: Gradually load mft runlist. (parse runlist from first file record, check all referenced file records, continue with the next file record). If no attribute list, just load it. + + // Load the runlist of $MFT/Bitmap. + // todo: what about ATTRIBUTE_LIST? Can we reuse code? + mft_bitmap_rl = load_runlist(dev, mft_offset, AT_BITMAP, 1024); + if (!mft_bitmap_rl) { + check_failed("Loading $MFT/Bitmap runlist failed. Trying $MFTMirr.\n"); + mft_bitmap_rl = load_runlist(dev, mftmirr_offset, AT_BITMAP, 1024); + } + if (!mft_bitmap_rl) { + check_failed("Loading $MFTMirr/Bitmap runlist failed too. Aborting.\n"); + return RETURN_FS_ERRORS_LEFT_UNCORRECTED; + // todo: rebuild the bitmap by using the "in_use" file record flag or by filling it with 1's. + } + + /* Load $MFT/Bitmap */ + if ((res = mft_bitmap_load(dev))) + return res; + return -1; /* FIXME: Just added to fix compiler warning without + thinking about what should be here. (Yura) */ +} + +static void check_volume(ntfs_volume *vol) +{ + s64 mft_num, nr_mft_records; + + ntfs_log_warning("Unsupported: check_volume()\n"); + unsupported++; + + // For each mft record, verify that it contains a valid file record. + nr_mft_records = vol->mft_na->initialized_size >> + vol->mft_record_size_bits; + ntfs_log_info("Checking %lld MFT records.\n", nr_mft_records); + + for (mft_num=0; mft_num < nr_mft_records; mft_num++) { + verify_mft_record(vol, mft_num); + } + + // todo: Check metadata files. + + // todo: Second pass on mft records. Now check the contents as well. + // todo: When going through runlists, build a bitmap. + + // todo: cluster accounting. + return; +} + +static int reset_dirty(ntfs_volume *vol) +{ + u16 flags; + + if (!(vol->flags | VOLUME_IS_DIRTY)) + return 0; + + ntfs_log_verbose("Resetting dirty flag.\n"); + + flags = vol->flags & ~VOLUME_IS_DIRTY; + + if (ntfs_volume_write_flags(vol, flags)) { + ntfs_log_error("Error setting volume flags.\n"); + return -1; + } + return 0; +} + +/** + * main - Does just what C99 claim it does. + * + * For more details on arguments and results, check the man page. + */ +int main(int argc, char **argv) +{ + struct ntfs_device *dev; + ntfs_volume *vol; + const char *name; + int ret; + + if (argc != 2) + return RETURN_USAGE_OR_SYNTAX_ERROR; + name = argv[1]; + + ntfs_log_set_handler(ntfs_log_handler_outerr); + //ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_VERBOSE | NTFS_LOG_LEVEL_PROGRESS); + + /* Allocate an ntfs_device structure. */ + dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); + if (!dev) + return RETURN_OPERATIONAL_ERROR; + + if (dev->d_ops->open(dev, O_RDONLY)) { //O_RDWR/O_RDONLY? + ntfs_log_perror("Error opening partition device"); + ntfs_device_free(dev); + return RETURN_OPERATIONAL_ERROR; + } + + if ((ret = verify_boot_sector(dev))) { + dev->d_ops->close(dev); + return ret; + } + ntfs_log_verbose("Boot sector verification complete. Proceeding to $MFT"); + + verify_mft_preliminary(dev); + + /* ntfs_device_mount() expects the device to be closed. */ + if (dev->d_ops->close(dev)) + ntfs_log_perror("Failed to close the device."); + + // at this point we know that the volume is valid enough for mounting. + + /* Call ntfs_device_mount() to do the actual mount. */ + vol = ntfs_device_mount(dev, MS_RDONLY); + if (!vol) { + ntfs_device_free(dev); + return 2; + } + + replay_log(vol); + + if (vol->flags & VOLUME_IS_DIRTY) + ntfs_log_warning("Volume is dirty.\n"); + + check_volume(vol); + + if (errors) + ntfs_log_info("Errors found.\n"); + if (unsupported) + ntfs_log_info("Unsupported cases found.\n"); + + if (!errors && !unsupported) { + reset_dirty(vol); + } + + ntfs_umount(vol, FALSE); + + if (errors) + return 2; + if (unsupported) + return 1; + return 0; +} + diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index 8037a68b..75231fa3 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -2,7 +2,7 @@ * ntfsclone - Part of the Linux-NTFS project. * * Copyright (c) 2003-2006 Szabolcs Szakacsits - * Copyright (c) 2004-2005 Anton Altaparmakov + * Copyright (c) 2004-2006 Anton Altaparmakov * Special image format support copyright (c) 2004 Per Olofsson * * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. @@ -55,6 +55,12 @@ #include #endif +/* + * FIXME: ntfsclone do bad things about endians handling. Fix it and remove + * this note and define. + */ +#define NTFS_DO_NOT_CHECK_ENDIANS + #include "debug.h" #include "types.h" #include "support.h" @@ -73,6 +79,7 @@ #include "ntfstime.h" #include "utils.h" /* #include "version.h" */ +#include "misc.h" #if defined(linux) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */ @@ -95,7 +102,7 @@ static const char *dirty_volume_msg = "Volume '%s' is scheduled for a check or it was shutdown \n" "uncleanly. Please boot Windows or use the --force option to progress.\n"; -struct { +static struct { int verbose; int quiet; int debug; @@ -116,7 +123,6 @@ struct { struct bitmap { s64 size; u8 *bm; - u8 padding[4]; /* Unused: padding to 64 bit. */ }; struct progress_bar { @@ -140,24 +146,44 @@ struct ntfs_walk_cluster { }; -ntfs_volume *vol = NULL; -struct bitmap lcn_bitmap; +static ntfs_volume *vol = NULL; +static struct bitmap lcn_bitmap; -int fd_in; -int fd_out; -FILE *msg_out = NULL; +static int fd_in; +static int fd_out; +static FILE *msg_out = NULL; -int wipe = 0; -unsigned int nr_used_mft_records = 0; -unsigned int wiped_unused_mft_data = 0; -unsigned int wiped_unused_mft = 0; -unsigned int wiped_resident_data = 0; -unsigned int wiped_timestamp_data = 0; +static int wipe = 0; +static unsigned int nr_used_mft_records = 0; +static unsigned int wiped_unused_mft_data = 0; +static unsigned int wiped_unused_mft = 0; +static unsigned int wiped_resident_data = 0; +static unsigned int wiped_timestamp_data = 0; + +static BOOL image_is_host_endian = FALSE; #define IMAGE_MAGIC "\0ntfsclone-image" #define IMAGE_MAGIC_SIZE 16 -struct { +/* This is the first endianness safe format version. */ +#define NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE 10 +#define NTFSCLONE_IMG_VER_MINOR_ENDIANNESS_SAFE 0 + +/* + * Set the version to 10.0 to avoid colisions with old ntfsclone which + * stupidly used the volume version as the image version... )-: I hope NTFS + * never reaches version 10.0 and if it does one day I hope no-one is using + * such an old ntfsclone by then... + * + * NOTE: Only bump the minor version if the image format and header are still + * backwards compatible. Otherwise always bump the major version. If in + * doubt, bump the major version. + */ +#define NTFSCLONE_IMG_VER_MAJOR 10 +#define NTFSCLONE_IMG_VER_MINOR 0 + +/* All values are in little endian. */ +static struct { char magic[IMAGE_MAGIC_SIZE]; u8 major_ver; u8 minor_ver; @@ -165,8 +191,12 @@ struct { s64 device_size; s64 nr_clusters; s64 inuse; + u32 offset_to_image_data; /* From start of image_hdr. */ } __attribute__((__packed__)) image_hdr; +#define NTFSCLONE_IMG_HEADER_SIZE_OLD \ + (offsetof(typeof(image_hdr), offset_to_image_data)) + #define NTFS_MBYTE (1000 * 1000) #define ERR_PREFIX "ERROR" @@ -527,7 +557,7 @@ static void copy_cluster(int rescue, u64 rescue_lcn) { char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ /* vol is NULL if opt.restore_image is set */ - u32 csize = image_hdr.cluster_size; + u32 csize = le32_to_cpu(image_hdr.cluster_size); void *fd = (void *)&fd_in; off_t rescue_pos; @@ -589,10 +619,12 @@ static void lseek_to_cluster(s64 lcn) static void image_skip_clusters(s64 count) { if (opt.save_image && count > 0) { + typeof(count) count_buf; char buff[1 + sizeof(count)]; buff[0] = 0; - memcpy(buff + 1, &count, sizeof(count)); + count_buf = cpu_to_sle64(count); + memcpy(buff + 1, &count_buf, sizeof(count_buf)); if (write_all(&fd_out, buff, sizeof(buff)) == -1) perr_exit("write_all"); @@ -629,13 +661,15 @@ static void clone_ntfs(u64 nr_clusters) else Printf("Cloning NTFS ...\n"); - if ((buf = calloc(1, csize)) == NULL) + buf = ntfs_calloc(csize); + if (!buf) perr_exit("clone_ntfs"); progress_init(&progress, p_counter, nr_clusters, 100); if (opt.save_image) { - if (write_all(&fd_out, &image_hdr, sizeof(image_hdr)) == -1) + if (write_all(&fd_out, &image_hdr, + image_hdr.offset_to_image_data) == -1) perr_exit("write_all"); } @@ -678,7 +712,7 @@ static void write_empty_clusters(s32 csize, s64 count, static void restore_image(void) { s64 pos = 0, count; - s32 csize = image_hdr.cluster_size; + s32 csize = le32_to_cpu(image_hdr.cluster_size); char cmd; u64 p_counter = 0; struct progress_bar progress; @@ -686,21 +720,25 @@ static void restore_image(void) Printf("Restoring NTFS from image ...\n"); progress_init(&progress, p_counter, opt.std_out ? - image_hdr.nr_clusters : image_hdr.inuse, 100); + sle64_to_cpu(image_hdr.nr_clusters) : + sle64_to_cpu(image_hdr.inuse), + 100); - while (pos < image_hdr.nr_clusters) { + while (pos < sle64_to_cpu(image_hdr.nr_clusters)) { if (read_all(&fd_in, &cmd, sizeof(cmd)) == -1) perr_exit("read_all"); if (cmd == 0) { if (read_all(&fd_in, &count, sizeof(count)) == -1) perr_exit("read_all"); + if (!image_is_host_endian) + count = sle64_to_cpu(count); if (opt.std_out) write_empty_clusters(csize, count, &progress, &p_counter); else { - if (lseek(fd_out, count * csize, SEEK_CUR) - == (off_t)-1) + if (lseek(fd_out, count * csize, SEEK_CUR) == + (off_t)-1) perr_exit("restore_image: lseek"); } pos += count; @@ -718,15 +756,16 @@ static void wipe_index_entry_timestams(INDEX_ENTRY *e) static const struct timespec zero_time = { .tv_sec = 0, .tv_nsec = 0 }; s64 timestamp = timespec2ntfs(zero_time); + /* FIXME: can fall into infinite loop if corrupted */ while (!(e->ie_flags & INDEX_ENTRY_END)) { e->key.file_name.creation_time = timestamp; e->key.file_name.last_data_change_time = timestamp; e->key.file_name.last_mft_change_time = timestamp; e->key.file_name.last_access_time = timestamp; - + wiped_timestamp_data += 32; - + e = (INDEX_ENTRY *)((u8 *)e + le16_to_cpu(e->length)); } } @@ -744,7 +783,8 @@ static void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr) indexr = ntfs_index_root_get(ni, attr); if (!indexr) { - ntfs_log_perror("Failed to read $INDEX_ROOT attribute"); + perr_printf("Failed to read $INDEX_ROOT attribute of inode " + "%lld", ni->mft_no); return; } @@ -753,34 +793,38 @@ static void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr) name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); name_len = attr->name_length; - + byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL); if (!byte) { - ntfs_log_perror("Failed to read $BITMAP attribute"); + perr_printf("Failed to read $BITMAP attribute"); goto out_indexr; } na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, name, name_len); if (!na) { - ntfs_log_perror("Failed to open $INDEX_ALLOCATION attribute"); + perr_printf("Failed to open $INDEX_ALLOCATION attribute"); goto out_bitmap; } - tmp_indexa = indexa = malloc(na->data_size); - if (!tmp_indexa) { - ntfs_log_perror("malloc failed"); + + if (!na->data_size) goto out_na; - } + + tmp_indexa = indexa = ntfs_malloc(na->data_size); + if (!tmp_indexa) + goto out_na; + if (ntfs_attr_pread(na, 0, na->data_size, indexa) != na->data_size) { - ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute"); + perr_printf("Failed to read $INDEX_ALLOCATION attribute"); goto out_indexa; } bit = 0; while ((u8 *)tmp_indexa < (u8 *)indexa + na->data_size) { - if (*byte & (1 << bit)) { + if (*byte & (1 << bit)) { if (ntfs_mst_post_read_fixup((NTFS_RECORD *)tmp_indexa, - indexr->index_block_size)) { - ntfs_log_perror("Damaged INDX record"); + le32_to_cpu( + indexr->index_block_size))) { + perr_printf("Damaged INDX record"); goto out_indexa; } entry = (INDEX_ENTRY *)((u8 *)tmp_indexa + le32_to_cpu( @@ -792,22 +836,22 @@ static void wipe_index_allocation_timestamps(ntfs_inode *ni, ATTR_RECORD *attr) perr_exit("ntfs_mft_usn_dec"); if (ntfs_mst_pre_write_fixup((NTFS_RECORD *)tmp_indexa, - indexr->index_block_size)) { - ntfs_log_perror("INDX write fixup failed"); + le32_to_cpu( + indexr->index_block_size))) { + perr_printf("INDX write fixup failed"); goto out_indexa; } } - tmp_indexa = (INDEX_ALLOCATION *)((u8 *)tmp_indexa + - indexr->index_block_size); + tmp_indexa = (INDEX_ALLOCATION *)((u8 *)tmp_indexa + + le32_to_cpu(indexr->index_block_size)); bit++; if (bit > 7) { bit = 0; byte++; } } - if (ntfs_rl_pwrite(vol, na->rl, 0, 0, na->data_size, indexa) != na->data_size) - ntfs_log_perror("ntfs_rl_pwrite failed"); + perr_printf("ntfs_rl_pwrite failed for inode %lld", ni->mft_no); out_indexa: free(indexa); out_na: @@ -847,7 +891,7 @@ static void wipe_index_root_timestamps(ATTR_RECORD *attr, s64 timestamp) QUOTA_CONTROL_ENTRY *quota_q; quota_q = (QUOTA_CONTROL_ENTRY *)((u8 *)entry + - entry->data_offset); + le16_to_cpu(entry->data_offset)); /* * FIXME: no guarantee it's indeed /$Extend/$Quota:$Q. * For now, as a minimal safeguard, we check only for @@ -866,7 +910,7 @@ static void wipe_index_root_timestamps(ATTR_RECORD *attr, s64 timestamp) #define WIPE_TIMESTAMPS(atype, attr, timestamp) \ do { \ atype *ats; \ - ats = (atype *)((char *)(attr) + (attr)->value_offset); \ + ats = (atype *)((char *)(attr) + le16_to_cpu((attr)->value_offset)); \ \ ats->creation_time = (timestamp); \ ats->last_data_change_time = (timestamp); \ @@ -888,7 +932,7 @@ static void wipe_timestamps(ntfs_walk_clusters_ctx *image) else if (a->type == AT_STANDARD_INFORMATION) WIPE_TIMESTAMPS(STANDARD_INFORMATION, a, timestamp); - + else if (a->type == AT_INDEX_ROOT) wipe_index_root_timestamps(a, timestamp); } @@ -1132,11 +1176,26 @@ static void mft_record_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) { if (ntfs_mft_usn_dec(ni->mrec)) perr_exit("ntfs_mft_usn_dec"); - + if (ntfs_mft_record_write(volume, ni->mft_no, ni->mrec)) perr_exit("ntfs_mft_record_write"); } +static void mft_inode_write_with_same_usn(ntfs_volume *volume, ntfs_inode *ni) +{ + s32 i; + + mft_record_write_with_same_usn(volume, ni); + + if (ni->nr_extents <= 0) + return; + + for (i = 0; i < ni->nr_extents; ++i) { + ntfs_inode *eni = ni->extent_nis[i]; + mft_record_write_with_same_usn(volume, eni); + } +} + static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) { s64 inode = 0; @@ -1159,7 +1218,7 @@ static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) /* FIXME: Terrible kludge for libntfs not being able to return a deleted MFT record as inode */ - ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode)); + ni = ntfs_calloc(sizeof(ntfs_inode)); if (!ni) perr_exit("walk_clusters"); @@ -1208,7 +1267,7 @@ static int walk_clusters(ntfs_volume *volume, struct ntfs_walk_cluster *walk) out: if (wipe) { wipe_unused_mft_data(ni); - mft_record_write_with_same_usn(volume, ni); + mft_inode_write_with_same_usn(volume, ni); } if (ntfs_inode_close(ni)) @@ -1240,7 +1299,8 @@ static void setup_lcn_bitmap(void) /* Determine lcn bitmap byte size and allocate it. */ lcn_bitmap.size = rounded_up_division(vol->nr_clusters, 8); - if (!(lcn_bitmap.bm = (unsigned char *)calloc(1, lcn_bitmap.size))) + lcn_bitmap.bm = ntfs_calloc(lcn_bitmap.size); + if (!lcn_bitmap.bm) perr_exit("Failed to allocate internal buffer"); bitmap_file_data_fixup(vol->nr_clusters, &lcn_bitmap); @@ -1260,14 +1320,15 @@ static void print_volume_size(const char *str, s64 bytes) } -static void print_disk_usage(u32 cluster_size, s64 nr_clusters, s64 inuse) +static void print_disk_usage(const char *spacer, u32 cluster_size, + s64 nr_clusters, s64 inuse) { s64 total, used; total = nr_clusters * cluster_size; used = inuse * cluster_size; - Printf("Space in use : %lld MB (%.1f%%) ", + Printf("Space in use %s: %lld MB (%.1f%%) ", spacer, (long long)rounded_up_division(used, NTFS_MBYTE), 100.0 * ((float)used / total)); @@ -1276,16 +1337,21 @@ static void print_disk_usage(u32 cluster_size, s64 nr_clusters, s64 inuse) static void print_image_info(void) { - Printf("NTFS volume version: %d.%d\n", - image_hdr.major_ver, image_hdr.minor_ver); - Printf("Cluster size : %u bytes\n", - (unsigned int)image_hdr.cluster_size); - print_volume_size("Image volume size ", - image_hdr.nr_clusters * image_hdr.cluster_size); - Printf("Image device size : %lld bytes\n", image_hdr.device_size); - print_disk_usage(image_hdr.cluster_size, - image_hdr.nr_clusters, - image_hdr.inuse); + Printf("Ntfsclone image version: %d.%d\n", + image_hdr.major_ver, image_hdr.minor_ver); + Printf("Cluster size : %u bytes\n", + (unsigned)le32_to_cpu(image_hdr.cluster_size)); + print_volume_size("Image volume size ", + sle64_to_cpu(image_hdr.nr_clusters) * + le32_to_cpu(image_hdr.cluster_size)); + Printf("Image device size : %lld bytes\n", + sle64_to_cpu(image_hdr.device_size)); + print_disk_usage(" ", le32_to_cpu(image_hdr.cluster_size), + sle64_to_cpu(image_hdr.nr_clusters), + sle64_to_cpu(image_hdr.inuse)); + Printf("Offset to image data : %u (0x%x) bytes\n", + (unsigned)le32_to_cpu(image_hdr.offset_to_image_data), + (unsigned)le32_to_cpu(image_hdr.offset_to_image_data)); } static void check_if_mounted(const char *device, unsigned long new_mntflag) @@ -1348,7 +1414,7 @@ static void mount_volume(unsigned long new_mntflag) volume_size(vol, vol->nr_clusters)); } -struct ntfs_walk_cluster backup_clusters = { NULL, NULL }; +static struct ntfs_walk_cluster backup_clusters = { NULL, NULL }; static int device_offset_valid(int fd, s64 ofs) { @@ -1475,14 +1541,61 @@ static s64 open_image(void) if ((fd_in = open(opt.volume, O_RDONLY)) == -1) perr_exit("failed to open image"); } - - if (read_all(&fd_in, &image_hdr, sizeof(image_hdr)) == -1) + if (read_all(&fd_in, &image_hdr, NTFSCLONE_IMG_HEADER_SIZE_OLD) == -1) perr_exit("read_all"); - if (memcmp(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) != 0) err_exit("Input file is not an image! (invalid magic)\n"); + if (image_hdr.major_ver < NTFSCLONE_IMG_VER_MAJOR_ENDIANNESS_SAFE) { + image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; + image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; +#if (__BYTE_ORDER == __BIG_ENDIAN) + Printf("Old image format detected. If the image was created " + "on a little endian architecture it will not " + "work. Use a more recent version of " + "ntfsclone to recreate the image.\n"); + image_hdr.cluster_size = cpu_to_le32(image_hdr.cluster_size); + image_hdr.device_size = cpu_to_sle64(image_hdr.device_size); + image_hdr.nr_clusters = cpu_to_sle64(image_hdr.nr_clusters); + image_hdr.inuse = cpu_to_sle64(image_hdr.inuse); +#endif + image_hdr.offset_to_image_data = + const_cpu_to_le32((sizeof(image_hdr) + 7) & ~7); + image_is_host_endian = TRUE; + } else { + typeof(image_hdr.offset_to_image_data) offset_to_image_data; + int delta; - return image_hdr.device_size; + if (image_hdr.major_ver > NTFSCLONE_IMG_VER_MAJOR) + err_exit("Do not know how to handle image format " + "version %d.%d. Please obtain a " + "newer version of ntfsclone.\n", + image_hdr.major_ver, + image_hdr.minor_ver); + /* Read the image header data offset. */ + if (read_all(&fd_in, &offset_to_image_data, + sizeof(offset_to_image_data)) == -1) + perr_exit("read_all"); + image_hdr.offset_to_image_data = + le32_to_cpu(offset_to_image_data); + /* + * Read any fields from the header that we have not read yet so + * that the input stream is positioned correctly. This means + * we can support future minor versions that just extend the + * header in a backwards compatible way. + */ + delta = offset_to_image_data - (NTFSCLONE_IMG_HEADER_SIZE_OLD + + sizeof(image_hdr.offset_to_image_data)); + if (delta > 0) { + char *dummy_buf; + + dummy_buf = malloc(delta); + if (!dummy_buf) + perr_exit("malloc dummy_buffer"); + if (read_all(&fd_in, dummy_buf, delta) == -1) + perr_exit("read_all"); + } + } + return sle64_to_cpu(image_hdr.device_size); } static s64 open_volume(void) @@ -1508,12 +1621,14 @@ static s64 open_volume(void) static void initialise_image_hdr(s64 device_size, s64 inuse) { memcpy(image_hdr.magic, IMAGE_MAGIC, IMAGE_MAGIC_SIZE); - image_hdr.major_ver = vol->major_ver; - image_hdr.minor_ver = vol->minor_ver; - image_hdr.cluster_size = vol->cluster_size; - image_hdr.device_size = device_size; - image_hdr.nr_clusters = vol->nr_clusters; - image_hdr.inuse = inuse; + image_hdr.major_ver = NTFSCLONE_IMG_VER_MAJOR; + image_hdr.minor_ver = NTFSCLONE_IMG_VER_MINOR; + image_hdr.cluster_size = cpu_to_le32(vol->cluster_size); + image_hdr.device_size = cpu_to_sle64(device_size); + image_hdr.nr_clusters = cpu_to_sle64(vol->nr_clusters); + image_hdr.inuse = cpu_to_sle64(inuse); + image_hdr.offset_to_image_data = cpu_to_le32((sizeof(image_hdr) + 7) & + ~7); } static void check_output_device(s64 input_size) @@ -1610,10 +1725,11 @@ static void check_dest_free_space(u64 src_bytes) { u64 dest_bytes; struct statvfs stvfs; + struct stat st; if (opt.metadata || opt.blkdev_out || opt.std_out) return; - /* + /* * TODO: save_image needs a bit more space than src_bytes * due to the free space encoding overhead. */ @@ -1622,11 +1738,18 @@ static void check_dest_free_space(u64 src_bytes) strerror(errno)); return; } + + /* If file is a FIFO then there is no point in checking the size. */ + if (!fstat(fd_out, &st)) { + if (S_ISFIFO(st.st_mode)) + return; + } else + Printf("WARNING: fstat failed: %s\n", strerror(errno)); dest_bytes = (u64)stvfs.f_frsize * stvfs.f_bfree; if (!dest_bytes) dest_bytes = (u64)stvfs.f_bsize * stvfs.f_bfree; - + if (dest_bytes < src_bytes) err_exit("Destination doesn't have enough free space: " "%llu MB < %llu MB\n", @@ -1651,11 +1774,13 @@ int main(int argc, char **argv) if (opt.restore_image) { device_size = open_image(); - ntfs_size = image_hdr.nr_clusters * image_hdr.cluster_size; + ntfs_size = sle64_to_cpu(image_hdr.nr_clusters) * + le32_to_cpu(image_hdr.cluster_size); } else { device_size = open_volume(); ntfs_size = vol->nr_clusters * vol->cluster_size; } + // FIXME: This needs to be the cluster size... ntfs_size += 512; /* add backup boot sector */ if (opt.std_out) { @@ -1671,7 +1796,7 @@ int main(int argc, char **argv) flags |= O_EXCL; } - if ((fd_out = open(opt.output, flags, S_IRWXU)) == -1) + if ((fd_out = open(opt.output, flags, S_IRUSR | S_IWUSR)) == -1) perr_exit("Opening file '%s' failed", opt.output); if (!opt.save_image) @@ -1691,7 +1816,7 @@ int main(int argc, char **argv) walk_clusters(vol, &backup_clusters); compare_bitmaps(&lcn_bitmap); - print_disk_usage(vol->cluster_size, vol->nr_clusters, image.inuse); + print_disk_usage("", vol->cluster_size, vol->nr_clusters, image.inuse); check_dest_free_space(vol->cluster_size * image.inuse); diff --git a/ntfsprogs/ntfscluster.c b/ntfsprogs/ntfscluster.c index 1d67a44b..9cc3f699 100644 --- a/ntfsprogs/ntfscluster.c +++ b/ntfsprogs/ntfscluster.c @@ -491,7 +491,8 @@ int main(int argc, char *argv[]) utils_set_locale(); - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + vol = utils_mount_volume(opts.device, MS_RDONLY | + (opts.force ? MS_RECOVER : 0)); if (!vol) return 1; diff --git a/ntfsprogs/ntfscluster.h b/ntfsprogs/ntfscluster.h index fbc6c1d3..5a3d6b3c 100644 --- a/ntfsprogs/ntfscluster.h +++ b/ntfsprogs/ntfscluster.h @@ -56,7 +56,6 @@ struct match { ATTR_TYPES type; /* Attribute type */ ntfschar *name; /* Attribute name */ int name_len; /* Length of attribute name */ - u8 padding[4]; /* Unused: padding to 64 bit. */ }; #endif /* _NTFSCLUSTER_H_ */ diff --git a/ntfsprogs/ntfscmp.c b/ntfsprogs/ntfscmp.c index 89014885..e7f8c929 100644 --- a/ntfsprogs/ntfscmp.c +++ b/ntfsprogs/ntfscmp.c @@ -1,10 +1,26 @@ /** - * ntfscmp - compare two NTFS volumes. + * ntfscmp - Part of the Linux-NTFS project. * * Copyright (c) 2005-2006 Szabolcs Szakacsits - * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy * - * This utility is part of the Linux-NTFS project. + * This utility compare two NTFS volumes. + * + * 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 + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" @@ -17,6 +33,10 @@ #include #include +#include +#include +#include + #include "utils.h" /* #include "version.h" */ @@ -37,7 +57,7 @@ static const char *hibernated_volume_msg = "turned off properly\n"; -struct { +static struct { int debug; int show_progress; int verbose; @@ -55,7 +75,6 @@ struct progress_bar { int resolution; int flags; float unit; - u8 padding[4]; /* Unused: padding to 64 bit. */ }; /* WARNING: don't modify the text, external tools grep for it */ @@ -308,7 +327,7 @@ static inline s64 get_nr_mft_records(ntfs_volume *vol) #define NTFSCMP_EXTENSION_RECORD 4 #define NTFSCMP_INODE_CLOSE_ERROR 5 -const char *ntfscmp_errs[] = { +static const char *ntfscmp_errs[] = { "OK", "INODE_OPEN_ERROR", "INODE_OPEN_IO_ERROR", @@ -385,7 +404,8 @@ static void print_attribute_name(char *name) } #define GET_ATTR_NAME(a) \ - ((ntfschar *)(((u8 *)(a)) + ((a)->name_offset))), ((a)->name_length) + ((ntfschar *)(((u8 *)(a)) + le16_to_cpu((a)->name_offset))), \ + ((a)->name_length) static void free_name(char **name) { @@ -464,6 +484,119 @@ static void print_ctx(ntfs_attr_search_ctx *ctx) free_name(&name); } +static void print_differ(ntfs_attr *na) +{ + print_na(na); + printf("content: DIFFER\n"); +} + +static int cmp_buffer(u8 *buf1, u8 *buf2, long long int size, ntfs_attr *na) +{ + if (memcmp(buf1, buf2, size)) { + print_differ(na); + return -1; + } + return 0; +} + +struct cmp_ia { + INDEX_ALLOCATION *ia; + INDEX_ALLOCATION *tmp_ia; + u8 *bitmap; + u8 *byte; + s64 bm_size; +}; + +static int setup_cmp_ia(ntfs_attr *na, struct cmp_ia *cia) +{ + cia->bitmap = ntfs_attr_readall(na->ni, AT_BITMAP, na->name, + na->name_len, &cia->bm_size); + if (!cia->bitmap) { + perr_println("Failed to readall BITMAP"); + return -1; + } + cia->byte = cia->bitmap; + + cia->tmp_ia = cia->ia = ntfs_malloc(na->data_size); + if (!cia->tmp_ia) + goto free_bm; + + if (ntfs_attr_pread(na, 0, na->data_size, cia->ia) != na->data_size) { + perr_println("Failed to pread INDEX_ALLOCATION"); + goto free_ia; + } + + return 0; +free_ia: + free(cia->ia); +free_bm: + free(cia->bitmap); + return -1; +} + +static void cmp_index_allocation(ntfs_attr *na1, ntfs_attr *na2) +{ + struct cmp_ia cia1, cia2; + int bit, ret1, ret2; + u32 ib_size; + + if (setup_cmp_ia(na1, &cia1)) + return; + if (setup_cmp_ia(na2, &cia2)) + return; + /* + * FIXME: ia can be the same even if the bitmap sizes are different. + */ + if (cia1.bm_size != cia1.bm_size) + goto out; + + if (cmp_buffer(cia1.bitmap, cia2.bitmap, cia1.bm_size, na1)) + goto out; + + if (cmp_buffer((u8 *)cia1.ia, (u8 *)cia2.ia, 0x18, na1)) + goto out; + + ib_size = le32_to_cpu(cia1.ia->index.allocated_size) + 0x18; + + bit = 0; + while ((u8 *)cia1.tmp_ia < (u8 *)cia1.ia + na1->data_size) { + if (*cia1.byte & (1 << bit)) { + ret1 = ntfs_mst_post_read_fixup((NTFS_RECORD *) + cia1.tmp_ia, ib_size); + ret2 = ntfs_mst_post_read_fixup((NTFS_RECORD *) + cia2.tmp_ia, ib_size); + if (ret1 != ret2) { + print_differ(na1); + goto out; + } + + if (ret1 == -1) + continue; + + if (cmp_buffer(((u8 *)cia1.tmp_ia) + 0x18, + ((u8 *)cia2.tmp_ia) + 0x18, + le32_to_cpu(cia1.ia-> + index.index_length), na1)) + goto out; + } + + cia1.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia1.tmp_ia + ib_size); + cia2.tmp_ia = (INDEX_ALLOCATION *)((u8 *)cia2.tmp_ia + ib_size); + + bit++; + if (bit > 7) { + bit = 0; + cia1.byte++; + } + } +out: + free(cia1.ia); + free(cia2.ia); + free(cia1.bitmap); + free(cia2.bitmap); + return; +} + static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2) { s64 pos; @@ -503,14 +636,8 @@ static void cmp_attribute_data(ntfs_attr *na1, ntfs_attr *na2) exit(1); } - if (memcmp(buf1, buf2, count1)) { - print_na(na1); - printf("content"); - if (opt.verbose) - printf(" (len = %lld)", count1); - printf(": DIFFER\n"); + if (cmp_buffer(buf1, buf2, count1, na1)) return; - } } err_printf("%s read overrun: ", __FUNCTION__); @@ -531,7 +658,7 @@ static int cmp_attribute_header(ATTR_RECORD *a1, ATTR_RECORD *a2) /* * FIXME: includes paddings which are not handled by ntfsinfo! */ - header_size = a1->length; + header_size = le32_to_cpu(a1->length); } return memcmp(a1, a2, header_size); @@ -575,7 +702,10 @@ static void cmp_attribute(ntfs_attr_search_ctx *ctx1, return; } - cmp_attribute_data(na1, na2); + if (na1->type == AT_INDEX_ALLOCATION) + cmp_index_allocation(na1, na2); + else + cmp_attribute_data(na1, na2); close_attribs: ntfs_attr_close(na1); @@ -708,9 +838,11 @@ static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2) old_atype1 = atype1; old_ret1 = ret1; - if (!ret1 && (atype1 <= atype2 || ret2)) + if (!ret1 && (le32_to_cpu(atype1) <= le32_to_cpu(atype2) || + ret2)) ret1 = next_attr(ctx1, &atype1, &name1, &errno1); - if (!ret2 && (old_atype1 >= atype2 || old_ret1)) + if (!ret2 && (le32_to_cpu(old_atype1) >= le32_to_cpu(atype2) || + old_ret1)) ret2 = next_attr(ctx2, &atype2, &name2, &errno2); print_attributes(ni1, atype1, atype2, name1, name2); @@ -724,14 +856,15 @@ static int cmp_attributes(ntfs_inode *ni1, ntfs_inode *ni2) break; } - if (ret2 || atype1 < atype2) { + if (ret2 || le32_to_cpu(atype1) < le32_to_cpu(atype2)) { if (new_attribute(ctx1, prev_atype, prev_name)) { print_ctx(ctx1); printf("presence: EXISTS != MISSING\n"); - set_prev(&prev_name, &prev_atype, name1, atype1); + set_prev(&prev_name, &prev_atype, name1, + atype1); } - } else if (ret1 || atype1 > atype2) { + } else if (ret1 || le32_to_cpu(atype1) > le32_to_cpu(atype2)) { if (new_attribute(ctx2, prev_atype, prev_name)) { print_ctx(ctx2); printf("presence: MISSING != EXISTS \n"); diff --git a/ntfsprogs/ntfscp.8.in b/ntfsprogs/ntfscp.8.in index a97a7941..8bf26b43 100644 --- a/ntfsprogs/ntfscp.8.in +++ b/ntfsprogs/ntfscp.8.in @@ -1,30 +1,20 @@ -.\" Copyright (c) 2004\-2005 Yura Pakhuchiy. +.\" Copyright (c) 2004\-2007 Yura Pakhuchiy. .\" Copyright (c) 2005 Richard Russon. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFSCP 8 "November 2005" "ntfs-3g @VERSION@" +.TH NTFSCP 8 "September 2007" "ntfs-3g @VERSION@" .SH NAME -ntfscp \- overwrite file on an NTFS volume. +ntfscp \- copy file to an NTFS volume. .SH SYNOPSIS -.B ntfscp -[\fIoptions\fR] \fIdevice source_file destination\fR +\fBntfscp\fR [\fIoptions\fR] \fIdevice source_file destination\fR .SH DESCRIPTION -.B ntfscp -will overwrite file on an NTFS volume. At present -.B ntfscp -can't create new files. -.B destination -can be either file or directory. In case if -.B destination -is directory specified by name then -.B source_file -is copied into this directory, in case if -.B destination -is directory and specified by inode number then unnamed data attribute is -created for this inode and -.B source_file -is copied into it (WARNING: it's unusual to have unnamed data streams in the -directories, think twice before specifying directory by inode number). +\fBntfscp\fR will copy file to an NTFS volume. \fIdestination\fR can be either +file or directory. In case if \fIdestination\fR is directory specified by name +then \fIsource_file\fR is copied into this directory, in case if +\fIdestination\fR is directory and specified by inode number then unnamed data +attribute is created for this inode and \fIsource_file\fR is copied into it +(WARNING: it's unusual to have unnamed data streams in the directories, think +twice before specifying directory by inode number). .SH OPTIONS Below is a summary of all the options that .B ntfscp @@ -96,24 +86,26 @@ windows is C): .sp .RE .SH BUGS -There are no known problems with -.BR ntfscp . -If you find a bug please send an email describing the problem to the -development team: +There are no known problems with \fBntfscp\fR. If you find a bug please send an +email describing the problem to the development team: .br .nh ntfs\-3g\-devel@lists.sf.net .hy .SH AUTHORS -.B ntfscp -was written by Yura Pakhuchiy, with contributions from Anton Altaparmakov. +\fBntfscp\fR was written by Yura Pakhuchiy, with contributions from Anton +Altaparmakov and Hil Liao. .SH DEDICATION With love to Marina Sapego. .SH AVAILABILITY +<<<<<<< HEAD .B ntfscp is part of the .B ntfs-3g package and is available from: +======= +\fBntfscp\fR is part of the \fBntfsprogs\fR package and is available from: +>>>>>>> linux-ntfs/libntfs-3g_port-v2_0_0 .br .nh http://www.tuxera.com/community/ diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index 0747ffe7..2ca204cb 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -1,10 +1,11 @@ /** * ntfscp - Part of the Linux-NTFS project. * - * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2006 Hil Liao * - * This utility will overwrite files on NTFS volume. + * This utility will copy file to an NTFS volume. * * 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 @@ -43,6 +44,9 @@ #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_LIBGEN_H +#include +#endif #include "types.h" #include "attrib.h" @@ -68,7 +72,7 @@ struct options { static const char *EXEC_NAME = "ntfscp"; static struct options opts; -volatile sig_atomic_t caught_terminate = 0; +static volatile sig_atomic_t caught_terminate = 0; /** * version - Print version information about the program @@ -79,9 +83,11 @@ volatile sig_atomic_t caught_terminate = 0; */ static void version(void) { - ntfs_log_info("\n%s v%s (libntfs-3g) - Overwrite files on NTFS " + ntfs_log_info("\n%s v%s (libntfs-3g) - Copy file to an NTFS " "volume.\n\n", EXEC_NAME, VERSION); - ntfs_log_info("Copyright (c) 2004-2005 Yura Pakhuchiy\n"); + ntfs_log_info("Copyright (c) 2004-2007 Yura Pakhuchiy\n"); + ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n"); + ntfs_log_info("Copyright (c) 2006 Hil Liao\n"); ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -178,7 +184,7 @@ static int parse_options(int argc, char **argv) ntfs_log_error("Couldn't parse attribute.\n"); err++; } else - opts.attribute = (ATTR_TYPES)attr; + opts.attribute = (ATTR_TYPES)cpu_to_le32(attr); break; case 'i': opts.inode++; @@ -270,6 +276,34 @@ static void signal_handler(int arg __attribute__((unused))) caught_terminate++; } +/** + * Create a regular file under the given directory inode + * + * It is a wrapper function to ntfs_create(...) + * + * Return: the created file inode + */ +static ntfs_inode *ntfs_new_file(ntfs_inode *dir_ni, + const char *filename) +{ + ntfschar *ufilename; + /* inode to the file that is being created */ + ntfs_inode *ni; + int ufilename_len; + + /* ntfs_mbstoucs(...) will allocate memory for ufilename if it's NULL */ + ufilename = NULL; + ufilename_len = ntfs_mbstoucs_libntfscompat(filename, &ufilename, 0); + if (ufilename_len == -1) { + ntfs_log_perror("ERROR: Failed to convert '%s' to unicode", + filename); + return NULL; + } + ni = ntfs_create(dir_ni, 0, ufilename, ufilename_len, S_IFREG); + free(ufilename); + return ni; +} + /** * main - Begin here * @@ -313,14 +347,16 @@ int main(int argc, char *argv[]) if (opts.noaction) flags = MS_RDONLY; + if (opts.force) + flags |= MS_RECOVER; - vol = utils_mount_volume(opts.device, flags, opts.force); + vol = utils_mount_volume(opts.device, flags); if (!vol) { ntfs_log_perror("ERROR: couldn't mount volume"); return 1; } - if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) + if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force) goto umount; { @@ -352,45 +388,113 @@ int main(int argc, char *argv[]) } else out = ntfs_pathname_to_inode(vol, NULL, opts.dest_file); if (!out) { - ntfs_log_perror("ERROR: Couldn't open destination file"); - goto close_src; - } - if ((le16_to_cpu(out->mrec->flags) & MFT_RECORD_IS_DIRECTORY) && - !opts.inode){ - /* - * @out is directory and it was specified by pathname, add - * filename to path and reopen inode. - */ - char *filename, *new_dest_file; + /* Copy the file if the dest_file's parent dir can be opened. */ + char *parent_dirname; + char *filename; + ntfs_inode *dir_ni; + ntfs_inode *ni; + int dest_path_len; + char *dirname_last_whack; - /* - * FIXME: There should exist more beautiful way to get filename. - * Not sure that it will work in windows, but I don't think that - * someone will use ntfscp under windows. - */ - filename = strrchr(opts.src_file, '/'); - if (filename) - filename++; - else - filename = opts.src_file; - /* Add 2 bytes for '/' and null-terminator. */ - new_dest_file = malloc(strlen(opts.dest_file) + - strlen(filename) + 2); - if (!new_dest_file) { - ntfs_log_perror("ERROR: malloc() failed"); - goto close_dst; - } - strcpy(new_dest_file, opts.dest_file); - strcat(new_dest_file, "/"); - strcat(new_dest_file, filename); - ntfs_inode_close(out); - out = ntfs_pathname_to_inode(vol, NULL, new_dest_file); - free(new_dest_file); - if (!out) { - ntfs_log_perror("ERROR: Failed to open destination " - "file"); + filename = basename(opts.dest_file); + dest_path_len = strlen(opts.dest_file); + parent_dirname = strdup(opts.dest_file); + if (!parent_dirname) { + ntfs_log_perror("strdup() failed"); goto close_src; } + dirname_last_whack = strrchr(parent_dirname, '/'); + if (dirname_last_whack) { + dirname_last_whack[1] = 0; + dir_ni = ntfs_pathname_to_inode(vol, NULL, + parent_dirname); + } else { + ntfs_log_verbose("Target path does not contain '/'. " + "Using root directory as parent.\n"); + dir_ni = ntfs_inode_open(vol, FILE_root); + } + if (dir_ni) { + if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* Remove the last '/' for estetic reasons. */ + dirname_last_whack[0] = 0; + ntfs_log_error("The file '%s' already exists " + "and is not a directory. " + "Aborting.\n", parent_dirname); + free(parent_dirname); + ntfs_inode_close(dir_ni); + goto close_src; + } + ntfs_log_verbose("Creating a new file '%s' under '%s'" + "\n", filename, parent_dirname); + ni = ntfs_new_file(dir_ni, filename); + ntfs_inode_close(dir_ni); + if (!ni) { + ntfs_log_perror("Failed to create '%s' under " + "'%s'", filename, + parent_dirname); + free(parent_dirname); + goto close_src; + } + out = ni; + } else { + ntfs_log_perror("ERROR: Couldn't open '%s'", + parent_dirname); + free(parent_dirname); + goto close_src; + } + free(parent_dirname); + } + /* The destination is a directory. */ + if ((out->mrec->flags & MFT_RECORD_IS_DIRECTORY) && !opts.inode) { + char *filename; + char *overwrite_filename; + int overwrite_filename_len; + ntfs_inode *ni; + ntfs_inode *dir_ni; + int filename_len; + int dest_dirname_len; + + filename = basename(opts.src_file); + dir_ni = out; + filename_len = strlen(filename); + dest_dirname_len = strlen(opts.dest_file); + overwrite_filename_len = filename_len+dest_dirname_len + 2; + overwrite_filename = malloc(overwrite_filename_len); + if (!overwrite_filename) { + ntfs_log_perror("ERROR: Failed to allocate %i bytes " + "memory for the overwrite filename", + overwrite_filename_len); + ntfs_inode_close(out); + goto close_src; + } + strcpy(overwrite_filename, opts.dest_file); + if (opts.dest_file[dest_dirname_len - 1] != '/') { + strcat(overwrite_filename, "/"); + } + strcat(overwrite_filename, filename); + ni = ntfs_pathname_to_inode(vol, NULL, overwrite_filename); + /* Does a file with the same name exist in the dest dir? */ + if (ni) { + ntfs_log_verbose("Destination path has a file with " + "the same name\nOverwriting the file " + "'%s'\n", overwrite_filename); + ntfs_inode_close(out); + out = ni; + } else { + ntfs_log_verbose("Creating a new file '%s' under " + "'%s'\n", filename, opts.dest_file); + ni = ntfs_new_file(dir_ni, filename); + ntfs_inode_close(dir_ni); + if (!ni) { + ntfs_log_perror("ERROR: Failed to create the " + "destination file under '%s'", + opts.dest_file); + free(overwrite_filename); + goto close_src; + } + out = ni; + } + free(overwrite_filename); } attr_name = ntfs_str2ucs(opts.attr_name, &attr_name_len); @@ -424,7 +528,7 @@ int main(int argc, char *argv[]) ntfs_log_verbose("Old file size: %lld\n", na->data_size); if (na->data_size != new_size) { - if (ntfs_attr_truncate(na, new_size)) { + if (__ntfs_attr_truncate(na, new_size, FALSE)) { ntfs_log_perror("ERROR: Couldn't resize attribute"); goto close_attr; } diff --git a/ntfsprogs/ntfsdecrypt.c b/ntfsprogs/ntfsdecrypt.c index 71b40bb9..d1b80466 100644 --- a/ntfsprogs/ntfsdecrypt.c +++ b/ntfsprogs/ntfsdecrypt.c @@ -2,7 +2,8 @@ * ntfsdecrypt - Decrypt ntfs encrypted files. Part of the Linux-NTFS project. * * Copyright (c) 2005 Yuval Fledel - * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005-2007 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy * * This utility will decrypt files and print the decrypted data on the standard * output. @@ -25,10 +26,6 @@ #include "config.h" -#if !defined(HAVE_GCRYPT_H) || !defined(HAVE_GNUTLS_PKCS12_H) -#error A required header file is missing. Aborting. -#endif - #ifdef HAVE_SYS_TYPES_H #include #endif @@ -56,12 +53,8 @@ #ifdef HAVE_ERRNO_H #include #endif -#ifdef HAVE_GCRYPT_H #include -#endif -#ifdef HAVE_GNUTLS_PKCS12_H #include -#endif #include "types.h" #include "attrib.h" @@ -74,6 +67,19 @@ typedef gcry_sexp_t ntfs_rsa_private_key; +#define NTFS_SHA1_THUMBPRINT_SIZE 0x14 + +#define NTFS_CRED_TYPE_CERT_THUMBPRINT const_cpu_to_le32(3) + +#define NTFS_EFS_CERT_PURPOSE_OID_DDF "1.3.6.1.4.1.311.10.3.4" +#define NTFS_EFS_CERT_PURPOSE_OID_DRF "1.3.6.1.4.1.311.10.3.4.1" + +typedef enum { + DF_TYPE_UNKNOWN, + DF_TYPE_DDF, + DF_TYPE_DRF, +} NTFS_DF_TYPES; + /** * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit) * @@ -101,14 +107,14 @@ typedef enum { */ typedef struct { gcry_cipher_hd_t gcry_cipher_hd; - u32 alg_id; + le32 alg_id; u8 *key_data; gcry_cipher_hd_t *des_gcry_cipher_hd_ptr; } ntfs_fek; /* DESX-MS128 implementation for libgcrypt. */ static gcry_module_t ntfs_desx_module; -static unsigned ntfs_desx_algorithm_id = -1; +static int ntfs_desx_algorithm_id = -1; typedef struct { u64 in_whitening, out_whitening; @@ -446,19 +452,34 @@ static ntfs_rsa_private_key ntfs_rsa_private_key_import_from_gnutls( return (ntfs_rsa_private_key)rsa_key; } +/** + * ntfs_rsa_private_key_release + */ +static void ntfs_rsa_private_key_release(ntfs_rsa_private_key rsa_key) +{ + gcry_sexp_release((gcry_sexp_t)rsa_key); +} + /** * ntfs_pkcs12_extract_rsa_key */ static ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size, - char *password) + char *password, char *thumbprint, int thumbprint_size, + NTFS_DF_TYPES *df_type) { int err, bag_index, flags; gnutls_datum_t dpfx, dkey; - gnutls_pkcs12_t pkcs12; - gnutls_pkcs12_bag_t bag; - gnutls_x509_privkey_t pkey; + gnutls_pkcs12_t pkcs12 = NULL; + gnutls_pkcs12_bag_t bag = NULL; + gnutls_x509_privkey_t pkey = NULL; + gnutls_x509_crt_t crt = NULL; ntfs_rsa_private_key rsa_key = NULL; + char purpose_oid[100]; + size_t purpose_oid_size = sizeof(purpose_oid); + size_t tp_size = thumbprint_size; + BOOL have_thumbprint = FALSE; + *df_type = DF_TYPE_UNKNOWN; /* Create a pkcs12 structure. */ err = gnutls_pkcs12_init(&pkcs12); if (err) { @@ -474,7 +495,7 @@ static ntfs_rsa_private_key ntfs_pkcs12_extract_rsa_key(u8 *pfx, int pfx_size, ntfs_log_error("Failed to convert the PFX file from DER to " "native PKCS#12 format: %s\n", gnutls_strerror(err)); - goto out; + goto err; } /* * Verify that the password is correct and that the key file has not @@ -491,9 +512,9 @@ retry_verify: password = NULL; goto retry_verify; } - ntfs_log_error("Failed to verify the MAC (%s). Is the " + ntfs_log_error("Failed to verify the MAC: %s Is the " "password correct?\n", gnutls_strerror(err)); - goto out; + goto err; } for (bag_index = 0; ; bag_index++) { err = gnutls_pkcs12_bag_init(&bag); @@ -501,29 +522,31 @@ retry_verify: ntfs_log_error("Failed to initialize PKCS#12 Bag " "structure: %s\n", gnutls_strerror(err)); - goto out; + goto err; } err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag); if (err) { - if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + err = 0; break; + } ntfs_log_error("Failed to obtain Bag from PKCS#12 " "structure: %s\n", gnutls_strerror(err)); - goto bag_out; + goto err; } check_again: err = gnutls_pkcs12_bag_get_count(bag); if (err < 0) { ntfs_log_error("Failed to obtain Bag count: %s\n", gnutls_strerror(err)); - goto bag_out; + goto err; } err = gnutls_pkcs12_bag_get_type(bag, 0); if (err < 0) { ntfs_log_error("Failed to determine Bag type: %s\n", gnutls_strerror(err)); - goto bag_out; + goto err; } flags = 0; switch (err) { @@ -534,14 +557,14 @@ check_again: if (err < 0) { ntfs_log_error("Failed to obtain Bag data: " "%s\n", gnutls_strerror(err)); - goto bag_out; + goto err; } err = gnutls_x509_privkey_init(&pkey); if (err) { ntfs_log_error("Failed to initialized " "private key structure: %s\n", gnutls_strerror(err)); - goto bag_out; + goto err; } /* Decrypt the private key into GNU TLS format. */ err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey, @@ -551,7 +574,7 @@ check_again: "key from DER to GNU TLS " "format: %s\n", gnutls_strerror(err)); - goto key_out; + goto err; } #if 0 /* @@ -579,38 +602,106 @@ check_again: #endif /* Convert the private key to our internal format. */ rsa_key = ntfs_rsa_private_key_import_from_gnutls(pkey); - goto key_out; + if (!rsa_key) + goto err; + break; case GNUTLS_BAG_ENCRYPTED: err = gnutls_pkcs12_bag_decrypt(bag, password); if (err) { ntfs_log_error("Failed to decrypt Bag: %s\n", gnutls_strerror(err)); - goto bag_out; + goto err; } goto check_again; + case GNUTLS_BAG_CERTIFICATE: + err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); + if (err < 0) { + ntfs_log_error("Failed to obtain Bag data: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_init(&crt); + if (err) { + ntfs_log_error("Failed to initialize " + "certificate structure: %s\n", + gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_import(crt, &dkey, + GNUTLS_X509_FMT_DER); + if (err) { + ntfs_log_error("Failed to convert certificate " + "from DER to GNU TLS format: " + "%s\n", gnutls_strerror(err)); + goto err; + } + err = gnutls_x509_crt_get_key_purpose_oid(crt, 0, + purpose_oid, &purpose_oid_size, NULL); + if (err) { + ntfs_log_error("Failed to get key purpose " + "OID: %s\n", + gnutls_strerror(err)); + goto err; + } + purpose_oid[purpose_oid_size - 1] = '\0'; + if (!strcmp(purpose_oid, + NTFS_EFS_CERT_PURPOSE_OID_DRF)) + *df_type = DF_TYPE_DRF; + else if (!strcmp(purpose_oid, + NTFS_EFS_CERT_PURPOSE_OID_DDF)) + *df_type = DF_TYPE_DDF; + else { + ntfs_log_error("Certificate has unknown " + "purpose OID %s.\n", + purpose_oid); + err = EINVAL; + goto err; + } + /* Return the thumbprint to the caller. */ + err = gnutls_x509_crt_get_fingerprint(crt, + GNUTLS_DIG_SHA1, thumbprint, &tp_size); + if (err) { + ntfs_log_error("Failed to get thumbprint: " + "%s\n", gnutls_strerror(err)); + goto err; + } + if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) { + ntfs_log_error("Invalid thumbprint size %zd. " + "Should be %d.\n", tp_size, + thumbprint_size); + err = EINVAL; + goto err; + } + have_thumbprint = TRUE; + gnutls_x509_crt_deinit(crt); + crt = NULL; + break; default: /* We do not care about other types. */ break; } gnutls_pkcs12_bag_deinit(bag); } -key_out: - gnutls_x509_privkey_deinit(pkey); -bag_out: - gnutls_pkcs12_bag_deinit(bag); -out: - gnutls_pkcs12_deinit(pkcs12); +err: + if (rsa_key && (err || *df_type == DF_TYPE_UNKNOWN || + !have_thumbprint)) { + if (!err) + ntfs_log_error("Key type or thumbprint not found, " + "aborting.\n"); + ntfs_rsa_private_key_release(rsa_key); + rsa_key = NULL; + } + if (crt) + gnutls_x509_crt_deinit(crt); + if (pkey) + gnutls_x509_privkey_deinit(pkey); + if (bag) + gnutls_pkcs12_bag_deinit(bag); + if (pkcs12) + gnutls_pkcs12_deinit(pkcs12); return rsa_key; } -/** - * ntfs_rsa_private_key_release - */ -static void ntfs_rsa_private_key_release(ntfs_rsa_private_key rsa_key) -{ - gcry_sexp_release((gcry_sexp_t)rsa_key); -} - /** * ntfs_buffer_reverse - * @@ -971,23 +1062,27 @@ static inline BOOL ntfs_des_test(void) /** * ntfs_fek_import_from_raw */ -static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, - unsigned fek_size __attribute__((unused))) +static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, unsigned fek_size) { ntfs_fek *fek; u32 key_size, wanted_key_size, gcry_algo; gcry_error_t err; - // TODO: Sanity checking of sizes and offsets. key_size = le32_to_cpup(fek_buf); - //ntfs_log_debug("key_size 0x%x\n", key_size); + ntfs_log_debug("key_size 0x%x\n", key_size); + if (key_size + 16 > fek_size) { + ntfs_log_debug("Invalid FEK. It was probably decrypted with " + "the incorrect RSA key."); + errno = EINVAL; + return NULL; + } fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) + sizeof(gcry_cipher_hd_t)); if (!fek) { errno = ENOMEM; return NULL; } - fek->alg_id = *(u32*)(fek_buf + 8); + fek->alg_id = *(le32*)(fek_buf + 8); //ntfs_log_debug("alg_id 0x%x\n", le32_to_cpu(fek->alg_id)); fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7); memcpy(fek->key_data, fek_buf + 16, key_size); @@ -1039,7 +1134,8 @@ static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf, } if (key_size != wanted_key_size) { ntfs_log_error("%s key of %u bytes but needed size is %u " - "bytes, assuming corrupt key. Aborting.\n", + "bytes, assuming corrupt or incorrect key. " + "Aborting.\n", gcry_cipher_algo_name(gcry_algo), (unsigned)key_size, (unsigned)wanted_key_size); err = EIO; @@ -1083,7 +1179,8 @@ static void ntfs_fek_release(ntfs_fek *fek) * ntfs_df_array_fek_get */ static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, - ntfs_rsa_private_key rsa_key) + ntfs_rsa_private_key rsa_key, char *thumbprint, + int thumbprint_size) { EFS_DF_HEADER *df_header; EFS_DF_CREDENTIAL_HEADER *df_cred; @@ -1093,14 +1190,41 @@ static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, u32 df_count, fek_size; unsigned i; - df_header = (EFS_DF_HEADER*)(df_array + 1); df_count = le32_to_cpu(df_array->df_count); - for (i = 0; i < df_count; i++) { + if (!df_count) + ntfs_log_error("There are no elements in the DF array.\n"); + df_header = (EFS_DF_HEADER*)(df_array + 1); + for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)( + (u8*)df_header + le32_to_cpu(df_header->df_length))) { df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header + le32_to_cpu(df_header->cred_header_offset)); + if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) { + ntfs_log_debug("Credential type is not certificate " + "thumbprint, skipping DF entry.\n"); + continue; + } df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred + le32_to_cpu( df_cred->cert_thumbprint_header_offset)); + if (le32_to_cpu(df_cert->thumbprint_size) != thumbprint_size) { + ntfs_log_error("Thumbprint size %d is not valid " + "(should be %d), skipping this DF " + "entry.\n", + le32_to_cpu(df_cert->thumbprint_size), + thumbprint_size); + continue; + } + if (memcmp((u8*)df_cert + + le32_to_cpu(df_cert->thumbprint_offset), + thumbprint, thumbprint_size)) { + ntfs_log_debug("Thumbprints do not match, skipping " + "this DF entry.\n"); + continue; + } + /* + * The thumbprints match so this is probably the DF entry + * matching the RSA key. Try to decrypt the FEK with it. + */ fek_size = le32_to_cpu(df_header->fek_size); fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset); /* Decrypt the FEK. Note: This is done in place. */ @@ -1115,8 +1239,6 @@ static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, } else ntfs_log_error("Failed to decrypt the file " "encryption key.\n"); - df_header = (EFS_DF_HEADER*)((u8*)df_header + - le32_to_cpu(df_header->df_length)); } return NULL; } @@ -1125,10 +1247,11 @@ static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array, * ntfs_inode_fek_get - */ static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, - ntfs_rsa_private_key rsa_key) + ntfs_rsa_private_key rsa_key, char *thumbprint, + int thumbprint_size, NTFS_DF_TYPES df_type) { EFS_ATTR_HEADER *efs; - EFS_DF_ARRAY_HEADER *df_array; + EFS_DF_ARRAY_HEADER *df_array = NULL; ntfs_fek *fek = NULL; /* Obtain the $EFS contents. */ @@ -1137,17 +1260,29 @@ static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode, ntfs_log_perror("Failed to read $EFS attribute"); return NULL; } - /* Iterate through the DDFs & DRFs until we obtain a key. */ - if (efs->offset_to_ddf_array) { - df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + - le32_to_cpu(efs->offset_to_ddf_array)); - fek = ntfs_df_array_fek_get(df_array, rsa_key); - } - if (!fek && efs->offset_to_drf_array) { - df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + - le32_to_cpu(efs->offset_to_drf_array)); - fek = ntfs_df_array_fek_get(df_array, rsa_key); - } + /* + * Depending on whether the key is a normal key or a data recovery key, + * iterate through the DDF or DRF array, respectively. + */ + if (df_type == DF_TYPE_DDF) { + if (efs->offset_to_ddf_array) + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_ddf_array)); + else + ntfs_log_error("There are no entries in the DDF " + "array.\n"); + } else if (df_type == DF_TYPE_DRF) { + if (efs->offset_to_drf_array) + df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs + + le32_to_cpu(efs->offset_to_drf_array)); + else + ntfs_log_error("There are no entries in the DRF " + "array.\n"); + } else + ntfs_log_error("Invalid DF type.\n"); + if (df_array) + fek = ntfs_df_array_fek_get(df_array, rsa_key, thumbprint, + thumbprint_size); free(efs); return fek; } @@ -1178,18 +1313,20 @@ static int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset) } /* Apply the IV. */ if (fek->alg_id == CALG_AES_256) { - ((u64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); - ((u64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); + ((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset); + ((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset); } else { /* All other algos (Des, 3Des, DesX) use the same IV. */ - ((u64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); + ((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset); } return 512; } /** - * ntfs_cat_decrypt - * TODO: + * ntfs_cat_decrypt - Decrypt the contents of an encrypted file to stdout. + * @inode: An encrypted file's inode structure, as obtained by + * ntfs_inode_open(). + * @fek: A file encryption key. As obtained by ntfs_inode_fek_get(). */ static int ntfs_cat_decrypt(ntfs_inode *inode, ntfs_fek *fek) { @@ -1272,6 +1409,8 @@ int main(int argc, char *argv[]) ntfs_fek *fek; unsigned pfx_size; int res; + NTFS_DF_TYPES df_type; + char thumbprint[NTFS_SHA1_THUMBPRINT_SIZE]; ntfs_log_set_handler(ntfs_log_handler_stderr); @@ -1300,19 +1439,20 @@ int main(int argc, char *argv[]) return 1; } /* Obtain the user's private RSA key from the key file. */ - rsa_key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, password); + rsa_key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, password, + thumbprint, sizeof(thumbprint), &df_type); /* Destroy the password. */ memset(password, 0, strlen(password)); /* No longer need the pfx file contents. */ free(pfx_buf); if (!rsa_key) { - ntfs_log_error("Failed to extract the private RSA key. Did " - "you perhaps mistype the password?\n"); + ntfs_log_error("Failed to extract the private RSA key.\n"); ntfs_crypto_deinit(); return 1; } /* Mount the ntfs volume. */ - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + vol = utils_mount_volume(opts.device, MS_RDONLY | + (opts.force ? MS_RECOVER : 0)); if (!vol) { ntfs_log_error("Failed to mount ntfs volume. Aborting.\n"); ntfs_rsa_private_key_release(rsa_key); @@ -1332,7 +1472,8 @@ int main(int argc, char *argv[]) return 1; } /* Obtain the file encryption key of the encrypted file. */ - fek = ntfs_inode_fek_get(inode, rsa_key); + fek = ntfs_inode_fek_get(inode, rsa_key, thumbprint, + sizeof(thumbprint), df_type); ntfs_rsa_private_key_release(rsa_key); if (fek) { res = ntfs_cat_decrypt(inode, fek); diff --git a/ntfsprogs/ntfsdump_logfile.c b/ntfsprogs/ntfsdump_logfile.c index d4cdf35d..f5e1fd02 100644 --- a/ntfsprogs/ntfsdump_logfile.c +++ b/ntfsprogs/ntfsdump_logfile.c @@ -197,6 +197,9 @@ static int logfile_open(BOOL is_volume, const char *filename, ntfs_inode *ni; ntfs_attr *na; + /* Porting note: NTFS_MNT_FORENSIC is not needed when we mount + * the volume in read-only mode. No changes will be made to the + * logfile or anything else when we are in read only-mode. */ vol = ntfs_mount(filename, MS_RDONLY); if (!vol) log_err_exit(NULL, "Failed to mount %s: %s\n", diff --git a/ntfsprogs/ntfsfix.8.in b/ntfsprogs/ntfsfix.8.in index 5b46fee6..6bd9c2c5 100644 --- a/ntfsprogs/ntfsfix.8.in +++ b/ntfsprogs/ntfsfix.8.in @@ -14,14 +14,14 @@ is a utility that fixes some common NTFS problems. .B ntfsfix is .B NOT -a Linux version of chkdsk. It only repairs some fundamental NTFS +a Linux version of chkdsk. It only repairs some fundamental NTFS inconsistencies, resets the NTFS journal file and schedules an NTFS consistency check for the first boot into Windows. .sp You may run .B ntfsfix on an NTFS volume if you think it was damaged by Windows or some other way -and it can't be mounted. +and it cannot be mounted. .SH OPTIONS Below is a summary of all the options that .B ntfsfix diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index a0495d8e..1d5615db 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -1,8 +1,9 @@ /** * ntfsfix - Part of the Linux-NTFS project. * - * Copyright (c) 2000-2006 Anton Altaparmakov. - * Copyright (c) 2002-2006 Szabolcs Szakacsits. + * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2007 Yura Pakhuchiy * * This utility fixes some common NTFS problems, resets the NTFS journal file * and schedules an NTFS consistency check for the first boot into Windows. @@ -82,10 +83,8 @@ switch if you want to be able to build the NTFS utilities." static const char *EXEC_NAME = "ntfsfix"; static const char *OK = "OK\n"; static const char *FAILED = "FAILED\n"; -static BOOL vol_is_dirty = FALSE; -static BOOL journal_is_empty = FALSE; -struct { +static struct { char *volume; } opt; @@ -118,8 +117,9 @@ static void version(void) { ntfs_log_info("%s v%s\n\n" "Attempt to fix an NTFS partition.\n\n" - "Copyright (c) 2000-2006 Anton Altaparmakov.\n" - "Copyright (c) 2002-2006 Szabolcs Szakacsits.\n\n", + "Copyright (c) 2000-2006 Anton Altaparmakov\n" + "Copyright (c) 2002-2006 Szabolcs Szakacsits\n" + "Copyright (c) 2007 Yura Pakhuchiy\n\n", EXEC_NAME, VERSION); ntfs_log_info("%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); exit(1); @@ -170,7 +170,7 @@ static void parse_options(int argc, char **argv) /** * OLD_ntfs_volume_set_flags */ -static int OLD_ntfs_volume_set_flags(ntfs_volume *vol, const u16 flags) +static int OLD_ntfs_volume_set_flags(ntfs_volume *vol, const le16 flags) { MFT_RECORD *m = NULL; ATTR_RECORD *a; @@ -227,7 +227,7 @@ static int OLD_ntfs_volume_set_flags(ntfs_volume *vol, const u16 flags) goto err_out; } /* Set the volume flags. */ - vol->flags = c->flags = cpu_to_le16(flags); + vol->flags = c->flags = flags; if (ntfs_mft_record_write(vol, FILE_Volume, m)) { ntfs_log_perror("Error writing $Volume"); goto err_out; @@ -245,56 +245,33 @@ err_exit: */ static int set_dirty_flag(ntfs_volume *vol) { - u16 flags; + le16 flags; - if (vol_is_dirty == TRUE) + /* Porting note: We test for the current state of VOLUME_IS_DIRTY. This + * should actually be more appropriate than testing for NVolWasDirty. */ + if (vol->flags | VOLUME_IS_DIRTY) return 0; - ntfs_log_info("Setting required flags on partition... "); /* * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run * and fix it for us. */ flags = vol->flags | VOLUME_IS_DIRTY; - /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */ - if (vol->major_ver >= 2) - flags |= VOLUME_MOUNTED_ON_NT4; if (OLD_ntfs_volume_set_flags(vol, flags)) { ntfs_log_info(FAILED); ntfs_log_error("Error setting volume flags.\n"); return -1; } + vol->flags = flags; + + /* Porting note: libntfs-3g does not have the 'WasDirty' flag/property, + * and never touches the 'dirty' bit except when explicitly told to do + * so. Since we just wrote the VOLUME_IS_DIRTY bit to disk, and + * vol->flags is up-to-date, we can just ignore the NVolSetWasDirty + * statement. */ + /* NVolSetWasDirty(vol); */ + ntfs_log_info(OK); - vol_is_dirty = TRUE; - return 0; -} - -/** - * set_dirty_flag_mount - */ -static int set_dirty_flag_mount(ntfs_volume *vol) -{ - u16 flags; - - if (vol_is_dirty == TRUE) - return 0; - - ntfs_log_info("Setting required flags on partition... "); - /* - * Set chkdsk flag, i.e. mark the partition dirty so chkdsk will run - * and fix it for us. - */ - flags = vol->flags | VOLUME_IS_DIRTY; - /* If NTFS volume version >= 2.0 then set mounted on NT4 flag. */ - if (vol->major_ver >= 2) - flags |= VOLUME_MOUNTED_ON_NT4; - if (ntfs_volume_write_flags(vol, flags)) { - ntfs_log_info(FAILED); - ntfs_log_error("Error setting volume flags.\n"); - return -1; - } - ntfs_log_info(OK); - vol_is_dirty = TRUE; return 0; } @@ -303,9 +280,8 @@ static int set_dirty_flag_mount(ntfs_volume *vol) */ static int empty_journal(ntfs_volume *vol) { - if (journal_is_empty == TRUE) + if (NVolLogFileEmpty(vol)) return 0; - ntfs_log_info("Going to empty the journal ($LogFile)... "); if (ntfs_logfile_reset(vol)) { ntfs_log_info(FAILED); @@ -313,7 +289,6 @@ static int empty_journal(ntfs_volume *vol) return -1; } ntfs_log_info(OK); - journal_is_empty = TRUE; return 0; } @@ -398,7 +373,7 @@ static int fix_mftmirr(ntfs_volume *vol) use_mirr = FALSE; mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); if (mrec->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp(mrec)) { + if (ntfs_is_baad_record(mrec->magic)) { ntfs_log_info(FAILED); ntfs_log_error("$MFT error: Incomplete multi " "sector transfer detected in " @@ -406,7 +381,7 @@ static int fix_mftmirr(ntfs_volume *vol) ")-:\n", s); goto error_exit; } - if (!ntfs_is_mft_recordp(mrec)) { + if (!ntfs_is_mft_record(mrec->magic)) { ntfs_log_info(FAILED); ntfs_log_error("$MFT error: Invalid mft " "record for %s.\nCannot " @@ -416,14 +391,14 @@ static int fix_mftmirr(ntfs_volume *vol) } mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); if (mrec2->flags & MFT_RECORD_IN_USE) { - if (ntfs_is_baad_recordp(mrec2)) { + if (ntfs_is_baad_record(mrec2->magic)) { ntfs_log_info(FAILED); ntfs_log_error("$MFTMirr error: Incomplete " "multi sector transfer " "detected in %s.\n", s); goto error_exit; } - if (!ntfs_is_mft_recordp(mrec2)) { + if (!ntfs_is_mft_record(mrec2->magic)) { ntfs_log_info(FAILED); ntfs_log_error("$MFTMirr error: Invalid mft " "record for %s.\n", s); @@ -431,7 +406,7 @@ static int fix_mftmirr(ntfs_volume *vol) } /* $MFT is corrupt but $MFTMirr is ok, use $MFTMirr. */ if (!(mrec->flags & MFT_RECORD_IN_USE) && - !ntfs_is_mft_recordp(mrec)) + !ntfs_is_mft_record(mrec->magic)) use_mirr = TRUE; } if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { @@ -475,13 +450,13 @@ static int fix_mount(void) ntfs_log_info("Attempting to correct errors... "); - dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, NULL); + dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, + NULL); if (!dev) { ntfs_log_info(FAILED); ntfs_log_perror("Failed to allocate device"); return -1; } - vol = ntfs_volume_startup(dev, 0); if (!vol) { ntfs_log_info(FAILED); @@ -490,17 +465,12 @@ static int fix_mount(void) ntfs_device_free(dev); return -1; } - if (fix_mftmirr(vol) < 0) goto error_exit; - - /* FIXME: Will this fail? Probably... */ if (set_dirty_flag(vol) < 0) goto error_exit; - if (empty_journal(vol) < 0) goto error_exit; - ret = 0; error_exit: /* ntfs_umount() will invoke ntfs_device_free() for us. */ @@ -550,6 +520,20 @@ int main(int argc, char **argv) exit(1); } } + /* So the unmount does not clear it again. */ + + /* Porting note: The WasDirty flag was set here to prevent ntfs_unmount + * from clearing the dirty bit (which might have been set in + * fix_mount()). So the intention is to leave the dirty bit set. + * + * libntfs-3g does not automatically set or clear dirty flags on + * mount/unmount, this means that the assumption that the dirty flag is + * now set does not hold. So we need to set it if not already set. */ + if(!(vol->flags & VOLUME_IS_DIRTY) && ntfs_volume_write_flags(vol, + vol->flags | VOLUME_IS_DIRTY)) { + ntfs_log_error("Error: Failed to set volume dirty flag (%d " + "(%s))!\n", errno, strerror(errno)); + } /* Check NTFS version is ok for us (in $Volume) */ ntfs_log_info("NTFS volume version is %i.%i.\n", vol->major_ver, @@ -558,22 +542,13 @@ int main(int argc, char **argv) ntfs_log_error("Error: Unknown NTFS version.\n"); goto error_exit; } - - if (set_dirty_flag_mount(vol) < 0) - goto error_exit; - - if (empty_journal(vol) < 0) - goto error_exit; - if (vol->major_ver >= 3) { - /* FIXME: If on NTFS 3.0+, check for presence of the usn journal and - disable it (if present) as Win2k might be unhappy otherwise and Bad - Things(TM) could happen depending on what applications are actually - using it for. */ + /* + * FIXME: If on NTFS 3.0+, check for presence of the usn + * journal and stamp it if present. + */ } - - /* FIXME: Should we be marking the quota out of date, too? */ - + /* FIXME: We should be marking the quota out of date, too. */ /* That's all for now! */ ntfs_log_info("NTFS partition %s was processed successfully.\n", vol->dev->d_name); diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 7ceeebcf..35e3343f 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -6,7 +6,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2003-2006 Szabolcs Szakacsits * Copyright (c) 2004-2005 Yuval Fledel - * Copyright (c) 2004-2005 Yura Pakhuchiy + * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2005 Cristian Klein * * This utility will dump a file's attributes. @@ -78,6 +78,8 @@ #include "dir.h" #include "ntfstime.h" /* #include "version.h" */ +#include "support.h" +#include "misc.h" static const char *EXEC_NAME = "ntfsinfo"; @@ -85,12 +87,12 @@ static struct options { const char *device; /* Device/File to work with */ const char *filename; /* Resolve this filename to mft number */ s64 inode; /* Info for this inode */ + int debug; /* Debug output */ int quiet; /* Less output */ int verbose; /* Extra output */ int force; /* Override common sense */ int notime; /* Don't report timestamps at all */ int mft; /* Dump information about the volume as well */ - u8 padding[4]; /* Unused: padding to 64 bit. */ } opts; /** @@ -111,7 +113,7 @@ static void version(void) printf(" 2003-2006 Szabolcs Szakacsits\n"); printf(" 2003 Leonard Norrgård\n"); printf(" 2004-2005 Yuval Fledel\n"); - printf(" 2004-2005 Yura Pakhuchiy\n"); + printf(" 2004-2007 Yura Pakhuchiy\n"); printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -134,7 +136,11 @@ static void usage(void) " -q, --quiet Less output\n" " -v, --verbose More output\n" " -V, --version Display version information\n" - " -h, --help Display this help\n\n", + " -h, --help Display this help\n" +#ifdef DEBUG + " -d, --debug Show debug information\n" +#endif + "\n", EXEC_NAME); printf("%s%s\n", ntfs_bugs, ntfs_home); } @@ -150,8 +156,11 @@ static void usage(void) */ static int parse_options(int argc, char *argv[]) { - static const char *sopt = "-:fhi:F:mqtTvV"; + static const char *sopt = "-:dfhi:F:mqtTvV"; static const struct option lopt[] = { +#ifdef DEBUG + { "debug", no_argument, NULL, 'd' }, +#endif { "force", no_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, { "inode", required_argument, NULL, 'i' }, @@ -176,8 +185,6 @@ static int parse_options(int argc, char *argv[]) opts.filename = NULL; while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { - ntfs_log_trace("optind=%d; c='%c' optarg=\"%s\".\n", optind, c, - optarg); switch (c) { case 1: if (!opts.device) @@ -185,6 +192,9 @@ static int parse_options(int argc, char *argv[]) else err++; break; + case 'd': + opts.debug++; + break; case 'i': if ((opts.inode != -1) || (!utils_parse_size(optarg, &opts.inode, FALSE))) { @@ -292,6 +302,14 @@ static int parse_options(int argc, char *argv[]) } +#ifdef DEBUG + if (!opts.debug) + if (!freopen("/dev/null", "w", stderr)) { + ntfs_log_perror("Failed to freopen stderr to /dev/null"); + exit(1); + } +#endif + if (ver) version(); if (help || err) @@ -315,7 +333,7 @@ static int parse_options(int argc, char *argv[]) * sle64_to_cpu(standard_attr->creation_time)); * printf("\tFile Creation Time:\t %s", time_str); */ -static char *ntfsinfo_time_to_str(const s64 sle_ntfs_clock) +static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock) { struct timespec unix_clock = ntfs2timespec((ntfs_time) sle_ntfs_clock); return ctime(&unix_clock.tv_sec); @@ -331,22 +349,21 @@ static char *ntfsinfo_time_to_str(const s64 sle_ntfs_clock) * null if cannot convert to multi-byte string. errno would contain the * error id. no memory allocated in that case */ -static char *ntfs_attr_get_name(ATTR_RECORD *attr) +static char *ntfs_attr_get_name_mbs(ATTR_RECORD *attr) { ntfschar *ucs_attr_name; char *mbs_attr_name = NULL; int mbs_attr_name_size; - /* calculate name position */ - ucs_attr_name = (ntfschar *)((char *)attr + le16_to_cpu(attr->name_offset)); - /* convert unicode to printable format */ - mbs_attr_name_size = ntfs_ucstombs(ucs_attr_name,attr->name_length, - &mbs_attr_name,0); - if (mbs_attr_name_size>0) { + /* Get name in unicode. */ + ucs_attr_name = ntfs_attr_get_name(attr); + /* Convert unicode to printable format. */ + mbs_attr_name_size = ntfs_ucstombs(ucs_attr_name, attr->name_length, + &mbs_attr_name, 0); + if (mbs_attr_name_size > 0) return mbs_attr_name; - } else { + else return NULL; - } } @@ -430,7 +447,7 @@ static void ntfs_dump_volume(ntfs_volume *vol) * @type: dump flags for this attribute type * @flags: flags for dumping */ -static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, u32 flags) +static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags) { printf("%sFile attributes:\t", indent); if (flags & FILE_ATTR_READONLY) { @@ -445,6 +462,10 @@ static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, u32 flags) printf(" SYSTEM"); flags &= ~FILE_ATTR_SYSTEM; } + if (flags & FILE_ATTR_DIRECTORY) { + printf(" DIRECTORY"); + flags &= ~FILE_ATTR_DIRECTORY; + } if (flags & FILE_ATTR_ARCHIVE) { printf(" ARCHIVE"); flags &= ~FILE_ATTR_ARCHIVE; @@ -499,7 +520,8 @@ static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, u32 flags) } if (flags) printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags)); - printf("\n"); + /* Print all the flags in hex. */ + printf(" (0x%08x)\n", (unsigned)le32_to_cpu(flags)); } /** @@ -571,19 +593,36 @@ static void ntfs_dump_attr_standard_information(ATTR_RECORD *attr) le32_to_cpu(standard_attr->version_number)); printf("\tClass ID:\t\t %u \n", (unsigned int)le32_to_cpu(standard_attr->class_id)); - printf("\tUser ID:\t\t %u \n", + printf("\tUser ID:\t\t %u (0x%x)\n", + (unsigned int)le32_to_cpu(standard_attr->owner_id), (unsigned int)le32_to_cpu(standard_attr->owner_id)); - printf("\tSecurity ID:\t\t %u \n", + printf("\tSecurity ID:\t\t %u (0x%x)\n", + (unsigned int)le32_to_cpu(standard_attr->security_id), (unsigned int)le32_to_cpu(standard_attr->security_id)); - printf("\tQuota charged:\t\t %llu \n", (unsigned long long) + printf("\tQuota charged:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(standard_attr->quota_charged), + (unsigned long long) le64_to_cpu(standard_attr->quota_charged)); - printf("\tUpdate Sequence Number:\t %llu \n", + printf("\tUpdate Sequence Number:\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(standard_attr->usn), (unsigned long long) le64_to_cpu(standard_attr->usn)); } else { - printf("\tSize of STANDARD_INFORMATION is %u. It should be " - "either 72 or 48, something is wrong...\n", - (unsigned int)value_length); + printf("\tSize of STANDARD_INFORMATION is %u (0x%x). It " + "should be either 72 or 48, something is " + "wrong...\n", (unsigned int)value_length, + (unsigned)value_length); + } +} + +static void ntfs_dump_bytes(u8 *buf, int start, int stop) +{ + int i; + + for (i = start; i < stop; i++) { + printf("%02x ", buf[i]); } } @@ -604,11 +643,10 @@ static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) ntfs_log_perror("ntfs_get_attribute_value_length failed"); return; } - value = malloc(l); - if (!value) { - ntfs_log_perror("malloc failed"); + value = ntfs_malloc(l); + if (!value) return; - } + l = ntfs_get_attribute_value(vol, attr, value); if (!l) { ntfs_log_perror("ntfs_get_attribute_value failed"); @@ -622,15 +660,27 @@ static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) printf("\n"); printf("\t\tAttribute type:\t0x%x\n", (unsigned int)le32_to_cpu(entry->type)); - printf("\t\tRecord length:\t%u\n", - le16_to_cpu(entry->length)); - printf("\t\tName length:\t%u\n", entry->name_length); - printf("\t\tName offset:\t%u\n", entry->name_offset); - printf("\t\tStarting VCN:\t%lld\n", + printf("\t\tRecord length:\t%u (0x%x)\n", + (unsigned)le16_to_cpu(entry->length), + (unsigned)le16_to_cpu(entry->length)); + printf("\t\tName length:\t%u (0x%x)\n", + (unsigned)entry->name_length, + (unsigned)entry->name_length); + printf("\t\tName offset:\t%u (0x%x)\n", + (unsigned)entry->name_offset, + (unsigned)entry->name_offset); + printf("\t\tStarting VCN:\t%lld (0x%llx)\n", + (long long)sle64_to_cpu(entry->lowest_vcn), + (unsigned long long) sle64_to_cpu(entry->lowest_vcn)); - printf("\t\tMFT reference:\t%lld\n", + printf("\t\tMFT reference:\t%lld (0x%llx)\n", + (unsigned long long) + MREF_LE(entry->mft_reference), + (unsigned long long) MREF_LE(entry->mft_reference)); - printf("\t\tInstance:\t%u\n", le16_to_cpu(entry->instance)); + printf("\t\tInstance:\t%u (0x%x)\n", + (unsigned)le16_to_cpu(entry->instance), + (unsigned)le16_to_cpu(entry->instance)); printf("\t\tName:\t\t"); if (entry->name_length) { char *name = NULL; @@ -646,6 +696,11 @@ static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) ntfs_log_perror("ntfs_ucstombs failed"); } else printf("unnamed\n"); + printf("\t\tPadding:\t"); + ntfs_dump_bytes((u8 *)entry, entry->name_offset + + sizeof(ntfschar) * entry->name_length, + le16_to_cpu(entry->length)); + printf("\n"); } free(value); printf("\tEnd of attribute list reached.\n"); @@ -657,8 +712,9 @@ static void ntfs_dump_attr_list(ATTR_RECORD *attr, ntfs_volume *vol) static void ntfs_dump_filename(const char *indent, FILE_NAME_ATTR *file_name_attr) { - printf("%sParent directory:\t %lld\n", indent, - (long long)MREF_LE(file_name_attr->parent_directory)); + printf("%sParent directory:\t %lld (0x%llx)\n", indent, + (long long)MREF_LE(file_name_attr->parent_directory), + (long long)MREF_LE(file_name_attr->parent_directory)); /* time stuff */ if (!opts.notime) { char *ntfs_time_str; @@ -680,22 +736,32 @@ static void ntfs_dump_filename(const char *indent, printf("%sLast Accessed Time:\t %s", indent, ntfs_time_str); } /* other basic stuff about the file */ - printf("%sAllocated Size:\t\t %lld\n", indent, (long long) + printf("%sAllocated Size:\t\t %lld (0x%llx)\n", indent, (long long) + sle64_to_cpu(file_name_attr->allocated_size), + (unsigned long long) sle64_to_cpu(file_name_attr->allocated_size)); - printf("%sData Size:\t\t %lld\n", indent, - (long long)sle64_to_cpu(file_name_attr->data_size)); - printf("%sFilename Length:\t %d\n", indent, + printf("%sData Size:\t\t %lld (0x%llx)\n", indent, + (long long)sle64_to_cpu(file_name_attr->data_size), + (unsigned long long) + sle64_to_cpu(file_name_attr->data_size)); + printf("%sFilename Length:\t %d (0x%x)\n", indent, + (unsigned)file_name_attr->file_name_length, (unsigned)file_name_attr->file_name_length); ntfs_dump_flags(indent, AT_FILE_NAME, file_name_attr->file_attributes); if (file_name_attr->file_attributes & FILE_ATTR_REPARSE_POINT && - file_name_attr->reserved) + file_name_attr->reparse_point_tag) printf("%sReparse point tag:\t 0x%x\n", indent, (unsigned) le32_to_cpu(file_name_attr->reparse_point_tag)); else if (file_name_attr->reparse_point_tag) { - printf("%sEA Length:\t\t %d\n", indent, (unsigned) + printf("%sEA Length:\t\t %d (0x%x)\n", indent, (unsigned) + le16_to_cpu(file_name_attr->packed_ea_size), + (unsigned) le16_to_cpu(file_name_attr->packed_ea_size)); if (file_name_attr->reserved) - printf("%sReserved:\t\t %d\n", indent, (unsigned) + printf("%sReserved:\t\t %d (0x%x)\n", indent, + (unsigned) + le16_to_cpu(file_name_attr->reserved), + (unsigned) le16_to_cpu(file_name_attr->reserved)); } /* The filename. */ @@ -797,7 +863,10 @@ static void ntfs_dump_acl(const char *prefix, ACL *acl) printf("%sRevision\t %u\n", prefix, acl->revision); - /* don't recalc le16_to_cpu every iteration (minor speedup on big-endians */ + /* + * Do not recalculate le16_to_cpu every iteration (minor speedup on + * big-endian machines. + */ ace_count = le16_to_cpu(acl->ace_count); /* initialize 'ace' to the first ace (if any) */ @@ -825,7 +894,7 @@ static void ntfs_dump_acl(const char *prefix, ACL *acl) } printf("%sACE:\t\t type:%s flags:0x%x access:0x%x\n", prefix, - ace_type, (unsigned int)le16_to_cpu(ace->flags), + ace_type, (unsigned int)ace->flags, (unsigned int)le32_to_cpu(ace->mask)); /* get a SID string */ sid = ntfs_sid_to_mbs(&ace->sid, NULL, 0); @@ -833,7 +902,8 @@ static void ntfs_dump_acl(const char *prefix, ACL *acl) free(sid); /* proceed to next ACE */ - ace = (ACCESS_ALLOWED_ACE *)(((char *)ace) + le32_to_cpu(ace->size)); + ace = (ACCESS_ALLOWED_ACE *)(((char *)ace) + + le16_to_cpu(ace->size)); } } @@ -842,21 +912,39 @@ static void ntfs_dump_security_descriptor(SECURITY_DESCRIPTOR_ATTR *sec_desc, const char *indent) { char *sid; - + printf("%s\tRevision:\t\t %u\n", indent, sec_desc->revision); /* TODO: parse the flags */ - printf("%s\tFlags:\t\t\t 0x%0x\n", indent, sec_desc->control); + printf("%s\tControl:\t\t 0x%04x\n", indent, + le16_to_cpu(sec_desc->control)); - sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + - le32_to_cpu(sec_desc->owner)), NULL, 0); - printf("%s\tOwner SID:\t\t %s\n", indent, sid); - free(sid); + if (~sec_desc->control & SE_SELF_RELATIVE) { + SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)sec_desc; - sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + - le32_to_cpu(sec_desc->group)), NULL, 0); - printf("%s\tGroup SID:\t\t %s\n", indent, sid); - free(sid); + printf("%s\tOwner SID pointer:\t %p\n", indent, sd->owner); + printf("%s\tGroup SID pointer:\t %p\n", indent, sd->group); + printf("%s\tSACL pointer:\t\t %p\n", indent, sd->sacl); + printf("%s\tDACL pointer:\t\t %p\n", indent, sd->dacl); + + return; + } + + if (sec_desc->owner) { + sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + + le32_to_cpu(sec_desc->owner)), NULL, 0); + printf("%s\tOwner SID:\t\t %s\n", indent, sid); + free(sid); + } else + printf("%s\tOwner SID:\t\t missing\n", indent); + + if (sec_desc->group) { + sid = ntfs_sid_to_mbs((SID *)((char *)sec_desc + + le32_to_cpu(sec_desc->group)), NULL, 0); + printf("%s\tGroup SID:\t\t %s\n", indent, sid); + free(sid); + } else + printf("%s\tGroup SID:\t\t missing\n", indent); printf("%s\tSystem ACL:\t\t ", indent); if (sec_desc->control & SE_SACL_PRESENT) { @@ -896,14 +984,13 @@ static void ntfs_dump_attr_security_descriptor(ATTR_RECORD *attr, ntfs_volume *v if (attr->non_resident) { /* FIXME: We don't handle fragmented mapping pairs case. */ - runlist *rl = ntfs_mapping_pairs_decompress(vol, attr, 0); + runlist *rl = ntfs_mapping_pairs_decompress(vol, attr, NULL); if (rl) { s64 data_size, bytes_read; data_size = sle64_to_cpu(attr->data_size); - sec_desc_attr = malloc(data_size); + sec_desc_attr = ntfs_malloc(data_size); if (!sec_desc_attr) { - ntfs_log_perror("malloc failed"); free(rl); return; } @@ -928,8 +1015,8 @@ static void ntfs_dump_attr_security_descriptor(ATTR_RECORD *attr, ntfs_volume *v } ntfs_dump_security_descriptor(sec_desc_attr, ""); - - if (attr->non_resident) + + if (attr->non_resident) free(sec_desc_attr); } @@ -942,7 +1029,7 @@ static void ntfs_dump_attr_volume_name(ATTR_RECORD *attr) { ntfschar *ucs_vol_name = NULL; - if (attr->value_length>0) { + if (le32_to_cpu(attr->value_length) > 0) { char *mbs_vol_name = NULL; int mbs_vol_name_size; /* calculate volume name position */ @@ -950,20 +1037,18 @@ static void ntfs_dump_attr_volume_name(ATTR_RECORD *attr) le16_to_cpu(attr->value_offset)); /* convert the name to current locale multibyte sequence */ mbs_vol_name_size = ntfs_ucstombs(ucs_vol_name, - le32_to_cpu(attr->value_length)/sizeof(ntfschar), - &mbs_vol_name,0); + le32_to_cpu(attr->value_length) / + sizeof(ntfschar), &mbs_vol_name, 0); if (mbs_vol_name_size>0) { /* output the converted name. */ - printf("\tVolume Name:\t\t '%s'\n",mbs_vol_name); + printf("\tVolume Name:\t\t '%s'\n", mbs_vol_name); free(mbs_vol_name); - } else { - /* an error occurred, errno holds the reason - notify the user */ - ntfs_log_perror("ntfsinfo error: could not parse volume name"); - } - } else { + } else + ntfs_log_perror("ntfsinfo error: could not parse " + "volume name"); + } else printf("\tVolume Name:\t\t unnamed\n"); - } } /** @@ -981,7 +1066,7 @@ static void ntfs_dump_attr_volume_information(ATTR_RECORD *attr) printf("\tVolume Version:\t\t %d.%d\n", vol_information->major_ver, vol_information->minor_ver); - printf("\tFlags:\t\t\t "); + printf("\tVolume Flags:\t\t "); if (vol_information->flags & VOLUME_IS_DIRTY) printf("DIRTY "); if (vol_information->flags & VOLUME_RESIZE_LOG_FILE) @@ -999,32 +1084,40 @@ static void ntfs_dump_attr_volume_information(ATTR_RECORD *attr) if (vol_information->flags & VOLUME_MODIFIED_BY_CHKDSK) printf("MOD_BY_CHKDSK "); if (vol_information->flags & VOLUME_FLAGS_MASK) { - printf("\n"); - } else { - printf("none set\n"); - } - if (vol_information->flags & (0xFFFF - VOLUME_FLAGS_MASK)) + printf("(0x%04x)\n", + (unsigned)le16_to_cpu(vol_information->flags)); + } else + printf("none set (0x0000)\n"); + if (vol_information->flags & (~VOLUME_FLAGS_MASK)) printf("\t\t\t\t Unknown Flags: 0x%04x\n", - vol_information->flags & (0xFFFF - VOLUME_FLAGS_MASK)); + le16_to_cpu(vol_information->flags & + (~VOLUME_FLAGS_MASK))); } static ntfschar NTFS_DATA_SDS[5] = { const_cpu_to_le16('$'), - const_cpu_to_le16('S'), const_cpu_to_le16('D'), + const_cpu_to_le16('S'), const_cpu_to_le16('D'), const_cpu_to_le16('S'), const_cpu_to_le16('\0') }; static void ntfs_dump_sds_entry(SECURITY_DESCRIPTOR_HEADER *sds) { SECURITY_DESCRIPTOR_RELATIVE *sd; - - ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", le32_to_cpu(sds->hash)); - ntfs_log_verbose("\t\tSecurity id:\t\t %u\n", - le32_to_cpu(sds->security_id)); - ntfs_log_verbose("\t\tOffset:\t\t\t %llu\n", le64_to_cpu(sds->offset)); - ntfs_log_verbose("\t\tLength:\t\t\t %u\n", le32_to_cpu(sds->length)); - + + ntfs_log_verbose("\n"); + ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", + (unsigned)le32_to_cpu(sds->hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(sds->security_id), + (unsigned)le32_to_cpu(sds->security_id)); + ntfs_log_verbose("\t\tOffset:\t\t\t %llu (0x%llx)\n", + (unsigned long long)le64_to_cpu(sds->offset), + (unsigned long long)le64_to_cpu(sds->offset)); + ntfs_log_verbose("\t\tLength:\t\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(sds->length), + (unsigned)le32_to_cpu(sds->length)); + sd = (SECURITY_DESCRIPTOR_RELATIVE *)((char *)sds + sizeof(SECURITY_DESCRIPTOR_HEADER)); - + ntfs_dump_security_descriptor(sd, "\t"); } @@ -1035,22 +1128,22 @@ static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni) int name_len; s64 data_size; u64 inode; - + inode = ni->mft_no; if (ni->nr_extents < 0) inode = ni->base_ni->mft_no; if (FILE_Secure != inode) return; - + name_len = attr->name_length; if (!name_len) return; - + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); if (!ntfs_names_are_equal(NTFS_DATA_SDS, sizeof(NTFS_DATA_SDS) / 2 - 1, name, name_len, 0, NULL, 0)) return; - + sd = sds = ntfs_attr_readall(ni, AT_DATA, name, name_len, &data_size); if (!sd) { ntfs_log_perror("Failed to read $SDS attribute"); @@ -1060,10 +1153,10 @@ static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni) * FIXME: The right way is based on the indexes, so we couldn't * miss real entries. For now, dump until it makes sense. */ - while (sd->length && sd->hash && + while (sd->length && sd->hash && le64_to_cpu(sd->offset) < (u64)data_size && le32_to_cpu(sd->length) < (u64)data_size && - le64_to_cpu(sd->offset) + + le64_to_cpu(sd->offset) + le32_to_cpu(sd->length) < (u64)data_size) { ntfs_dump_sds_entry(sd); sd = (SECURITY_DESCRIPTOR_HEADER *)((char*)sd + @@ -1072,7 +1165,7 @@ static void ntfs_dump_sds(ATTR_RECORD *attr, ntfs_inode *ni) free(sds); } -static const char *get_attribute_type_name(u32 type) +static const char *get_attribute_type_name(le32 type) { switch (type) { case AT_UNUSED: return "$UNUSED"; @@ -1097,22 +1190,53 @@ static const char *get_attribute_type_name(u32 type) return "$UNKNOWN"; } - -static void ntfs_dump_attribute_header(ATTR_RECORD *a, ntfs_volume *vol) -{ - printf("Dumping attribute %s (%#02x)\n", - get_attribute_type_name(a->type), a->type); - ntfs_log_verbose("\tAttribute length:\t %u\n", le32_to_cpu(a->length)); +static const char * ntfs_dump_lcn(LCN lcn) +{ + switch (lcn) { + case LCN_HOLE: + return "\t"; + case LCN_RL_NOT_MAPPED: + return ""; + case LCN_ENOENT: + return "\t"; + case LCN_EINVAL: + return "\t"; + case LCN_EIO: + return "\t"; + default: + ntfs_log_error("Invalid LCN value %llx passed to " + "ntfs_dump_lcn().\n", lcn); + return "???\t"; + } +} + +static void ntfs_dump_attribute_header(ntfs_attr_search_ctx *ctx, + ntfs_volume *vol) +{ + ATTR_RECORD *a = ctx->attr; + + printf("Dumping attribute %s (0x%x) from mft record %lld (0x%llx)\n", + get_attribute_type_name(a->type), + (unsigned)le32_to_cpu(a->type), + (unsigned long long)ctx->ntfs_ino->mft_no, + (unsigned long long)ctx->ntfs_ino->mft_no); + + ntfs_log_verbose("\tAttribute length:\t %u (0x%x)\n", + (unsigned)le32_to_cpu(a->length), + (unsigned)le32_to_cpu(a->length)); printf("\tResident: \t\t %s\n", a->non_resident ? "No" : "Yes"); - ntfs_log_verbose("\tName length:\t\t %u\n", a->name_length); - ntfs_log_verbose("\tName offset:\t\t %u\n", le16_to_cpu(a->name_offset)); + ntfs_log_verbose("\tName length:\t\t %u (0x%x)\n", + (unsigned)a->name_length, (unsigned)a->name_length); + ntfs_log_verbose("\tName offset:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->name_offset), + (unsigned)le16_to_cpu(a->name_offset)); /* Dump the attribute (stream) name */ if (a->name_length) { char *attribute_name = NULL; - attribute_name = ntfs_attr_get_name(a); + attribute_name = ntfs_attr_get_name_mbs(a); if (attribute_name) { printf("\tAttribute name:\t\t '%s'\n", attribute_name); free(attribute_name); @@ -1121,51 +1245,83 @@ static void ntfs_dump_attribute_header(ATTR_RECORD *a, ntfs_volume *vol) } /* TODO: parse the flags */ - printf("\tFlags:\t\t\t 0x%04hx\n",le16_to_cpu(a->flags)); - printf("\tAttribute instance:\t %u\n", le16_to_cpu(a->instance)); + printf("\tAttribute flags:\t 0x%04x\n", + (unsigned)le16_to_cpu(a->flags)); + printf("\tAttribute instance:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->instance), + (unsigned)le16_to_cpu(a->instance)); /* Resident attribute */ if (!a->non_resident) { - printf("\tData size:\t\t %u\n", - (unsigned int)le32_to_cpu(a->value_length)); - ntfs_log_verbose("\tData offset:\t\t %u\n", - (unsigned int)le16_to_cpu(a->value_offset)); + printf("\tData size:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(a->value_length), + (unsigned)le32_to_cpu(a->value_length)); + ntfs_log_verbose("\tData offset:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->value_offset), + (unsigned)le16_to_cpu(a->value_offset)); /* TODO: parse the flags */ - printf("\tResident flags:\t\t 0x%02hhx\n", a->resident_flags); - ntfs_log_verbose("\tReservedR:\t\t %d\n", a->reservedR); + printf("\tResident flags:\t\t 0x%02x\n", + (unsigned)a->resident_flags); + ntfs_log_verbose("\tReservedR:\t\t %d (0x%x)\n", + (unsigned)a->reservedR, (unsigned)a->reservedR); return; } /* Non-resident attribute */ - ntfs_log_verbose("\tLowest VCN\t\t %lld\n", - (long long)sle64_to_cpu(a->lowest_vcn)); - ntfs_log_verbose("\tHighest VCN:\t\t %lld\n", - (long long)sle64_to_cpu(a->highest_vcn)); - ntfs_log_verbose("\tMapping pairs offset:\t %u\n", - le16_to_cpu(a->mapping_pairs_offset)); - printf("\tCompression unit:\t %u\n", a->compression_unit); + ntfs_log_verbose("\tLowest VCN\t\t %lld (0x%llx)\n", + (long long)sle64_to_cpu(a->lowest_vcn), + (unsigned long long)sle64_to_cpu(a->lowest_vcn)); + ntfs_log_verbose("\tHighest VCN:\t\t %lld (0x%llx)\n", + (long long)sle64_to_cpu(a->highest_vcn), + (unsigned long long)sle64_to_cpu(a->highest_vcn)); + ntfs_log_verbose("\tMapping pairs offset:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(a->mapping_pairs_offset), + (unsigned)le16_to_cpu(a->mapping_pairs_offset)); + printf("\tCompression unit:\t %u (0x%x)\n", + (unsigned)a->compression_unit, + (unsigned)a->compression_unit); /* TODO: dump the 5 reserved bytes here in verbose mode */ if (!a->lowest_vcn) { - printf("\tData size:\t\t %llu\n", - (long long)le64_to_cpu(a->data_size)); - printf("\tAllocated size:\t\t %llu\n", - (long long)le64_to_cpu(a->allocated_size)); - printf("\tInitialized size:\t %llu\n", - (long long)le64_to_cpu(a->initialized_size)); - if (a->compression_unit || (a->flags & ATTR_IS_COMPRESSED)) - printf("\tCompressed size:\t %llu\n", - (long long)le64_to_cpu(a->compressed_size)); + printf("\tData size:\t\t %llu (0x%llx)\n", + (long long)sle64_to_cpu(a->data_size), + (unsigned long long)sle64_to_cpu(a->data_size)); + printf("\tAllocated size:\t\t %llu (0x%llx)\n", + (long long)sle64_to_cpu(a->allocated_size), + (unsigned long long) + sle64_to_cpu(a->allocated_size)); + printf("\tInitialized size:\t %llu (0x%llx)\n", + (long long)sle64_to_cpu(a->initialized_size), + (unsigned long long) + sle64_to_cpu(a->initialized_size)); + if (a->compression_unit || a->flags & ATTR_IS_COMPRESSED || + a->flags & ATTR_IS_SPARSE) + printf("\tCompressed size:\t %llu (0x%llx)\n", + (signed long long) + sle64_to_cpu(a->compressed_size), + (signed long long) + sle64_to_cpu(a->compressed_size)); } if (opts.verbose) { - runlist *rl = ntfs_mapping_pairs_decompress(vol, a, 0); + runlist *rl; + + rl = ntfs_mapping_pairs_decompress(vol, a, NULL); if (rl) { runlist *rlc = rl; + + // TODO: Switch this to properly aligned hex... printf("\tRunlist:\tVCN\t\tLCN\t\tLength\n"); while (rlc->length) { - printf("\t\t\t%lld\t\t%lld\t\t%lld\n", - rlc->vcn, rlc->lcn, rlc->length); + if (rlc->lcn >= 0) + printf("\t\t\t0x%llx\t\t0x%llx\t\t" + "0x%llx\n", rlc->vcn, + rlc->lcn, rlc->length); + else + printf("\t\t\t0x%llx\t\t%s\t" + "0x%llx\n", rlc->vcn, + ntfs_dump_lcn(rlc->lcn), + rlc->length); rlc++; } free(rl); @@ -1200,27 +1356,36 @@ static void ntfs_dump_index_key(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) { char *sid; char printable_GUID[37]; - + switch (type) { case INDEX_ATTR_SECURE_SII: - ntfs_log_verbose("\t\tKey security id:\t %u\n", - le32_to_cpu(entry->key.sii.security_id)); + ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n", + (unsigned) + le32_to_cpu(entry->key.sii.security_id), + (unsigned) + le32_to_cpu(entry->key.sii.security_id)); break; case INDEX_ATTR_SECURE_SDH: ntfs_log_verbose("\t\tKey hash:\t\t 0x%08x\n", - le32_to_cpu(entry->key.sdh.hash)); - ntfs_log_verbose("\t\tKey security id:\t %u\n", - le32_to_cpu(entry->key.sdh.security_id)); + (unsigned)le32_to_cpu(entry->key.sdh.hash)); + ntfs_log_verbose("\t\tKey security id:\t %u (0x%x)\n", + (unsigned) + le32_to_cpu(entry->key.sdh.security_id), + (unsigned) + le32_to_cpu(entry->key.sdh.security_id)); break; case INDEX_ATTR_OBJID_O: ntfs_guid_to_mbs(&entry->key.object_id, printable_GUID); ntfs_log_verbose("\t\tKey GUID:\t\t %s\n", printable_GUID); break; case INDEX_ATTR_REPARSE_R: - ntfs_log_verbose("\t\tKey reparse tag:\t 0x%08x\n", - le32_to_cpu(entry->key.reparse.reparse_tag)); - ntfs_log_verbose("\t\tKey file id:\t\t %llu\n", - le64_to_cpu(entry->key.reparse.file_id)); + ntfs_log_verbose("\t\tKey reparse tag:\t 0x%08x\n", (unsigned) + le32_to_cpu(entry->key.reparse.reparse_tag)); + ntfs_log_verbose("\t\tKey file id:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(entry->key.reparse.file_id), + (unsigned long long) + le64_to_cpu(entry->key.reparse.file_id)); break; case INDEX_ATTR_QUOTA_O: sid = ntfs_sid_to_mbs(&entry->key.sid, NULL, 0); @@ -1228,51 +1393,63 @@ static void ntfs_dump_index_key(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) free(sid); break; case INDEX_ATTR_QUOTA_Q: - ntfs_log_verbose("\t\tKey owner id:\t\t %u\n", - le32_to_cpu(entry->key.owner_id)); + ntfs_log_verbose("\t\tKey owner id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(entry->key.owner_id), + (unsigned)le32_to_cpu(entry->key.owner_id)); break; default: ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", - le32_to_cpu(type)); + (unsigned)type); break; } } typedef union { - SII_INDEX_DATA sii; /* $SII index data in $Secure */ - SDH_INDEX_DATA sdh; /* $SDH index data in $Secure */ - QUOTA_O_INDEX_DATA quota_o; /* $O index data in $Quota */ - QUOTA_CONTROL_ENTRY quota_q; /* $Q index data in $Quota */ + SII_INDEX_DATA sii; /* $SII index data in $Secure */ + SDH_INDEX_DATA sdh; /* $SDH index data in $Secure */ + QUOTA_O_INDEX_DATA quota_o; /* $O index data in $Quota */ + QUOTA_CONTROL_ENTRY quota_q; /* $Q index data in $Quota */ } __attribute__((__packed__)) INDEX_ENTRY_DATA; static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) { INDEX_ENTRY_DATA *data; - - data = (INDEX_ENTRY_DATA *)((u8 *)entry + entry->data_offset); - + + data = (INDEX_ENTRY_DATA *)((u8 *)entry + + le16_to_cpu(entry->data_offset)); + switch (type) { case INDEX_ATTR_SECURE_SII: ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", - le32_to_cpu(data->sii.hash)); - ntfs_log_verbose("\t\tSecurity id:\t\t %u\n", - le32_to_cpu(data->sii.security_id)); - ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu\n", - le64_to_cpu(data->sii.offset)); - ntfs_log_verbose("\t\tLength in $SDS:\t\t %u\n", - le32_to_cpu(data->sii.length)); + (unsigned)le32_to_cpu(data->sii.hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sii.security_id), + (unsigned)le32_to_cpu(data->sii.security_id)); + ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(data->sii.offset), + (unsigned long long) + le64_to_cpu(data->sii.offset)); + ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sii.length), + (unsigned)le32_to_cpu(data->sii.length)); break; case INDEX_ATTR_SECURE_SDH: ntfs_log_verbose("\t\tHash:\t\t\t 0x%08x\n", - le32_to_cpu(data->sdh.hash)); - ntfs_log_verbose("\t\tSecurity id:\t\t %u\n", - le32_to_cpu(data->sdh.security_id)); - ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu\n", - le64_to_cpu(data->sdh.offset)); - ntfs_log_verbose("\t\tLength in $SDS:\t\t %u\n", - le32_to_cpu(data->sdh.length)); + (unsigned)le32_to_cpu(data->sdh.hash)); + ntfs_log_verbose("\t\tSecurity id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sdh.security_id), + (unsigned)le32_to_cpu(data->sdh.security_id)); + ntfs_log_verbose("\t\tOffset in $SDS:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(data->sdh.offset), + (unsigned long long) + le64_to_cpu(data->sdh.offset)); + ntfs_log_verbose("\t\tLength in $SDS:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->sdh.length), + (unsigned)le32_to_cpu(data->sdh.length)); ntfs_log_verbose("\t\tUnknown (padding):\t 0x%08x\n", - le32_to_cpu(data->sdh.reserved_II)); + (unsigned)le32_to_cpu(data->sdh.reserved_II)); break; case INDEX_ATTR_OBJID_O: { OBJ_ID_INDEX_DATA *object_id_data; @@ -1303,28 +1480,42 @@ static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) /* TODO */ break; case INDEX_ATTR_QUOTA_O: - ntfs_log_verbose("\t\tOwner id:\t\t %u\n", - le32_to_cpu(data->quota_o.owner_id)); - ntfs_log_verbose("\t\tUnknown:\t\t %u\n", - le32_to_cpu(data->quota_o.unknown)); + ntfs_log_verbose("\t\tOwner id:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->quota_o.owner_id), + (unsigned)le32_to_cpu(data->quota_o.owner_id)); + ntfs_log_verbose("\t\tUnknown:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(data->quota_o.unknown), + (unsigned)le32_to_cpu(data->quota_o.unknown)); break; case INDEX_ATTR_QUOTA_Q: ntfs_log_verbose("\t\tVersion:\t\t %u\n", - le32_to_cpu(data->quota_q.version)); + (unsigned)le32_to_cpu(data->quota_q.version)); ntfs_log_verbose("\t\tQuota flags:\t\t 0x%08x\n", - le32_to_cpu(data->quota_q.flags)); - ntfs_log_verbose("\t\tBytes used:\t\t %llu\n", - le64_to_cpu(data->quota_q.bytes_used)); + (unsigned)le32_to_cpu(data->quota_q.flags)); + ntfs_log_verbose("\t\tBytes used:\t\t %llu (0x%llx)\n", + (unsigned long long) + le64_to_cpu(data->quota_q.bytes_used), + (unsigned long long) + le64_to_cpu(data->quota_q.bytes_used)); ntfs_log_verbose("\t\tLast changed:\t\t %s", - ntfsinfo_time_to_str( - data->quota_q.change_time)); - ntfs_log_verbose("\t\tThreshold:\t\t %lld\n", - le64_to_cpu(data->quota_q.threshold)); - ntfs_log_verbose("\t\tLimit:\t\t\t %lld\n", - le64_to_cpu(data->quota_q.limit)); - ntfs_log_verbose("\t\tExceeded time:\t\t %lld\n", - le64_to_cpu(data->quota_q.exceeded_time)); - if (entry->data_length > 48) { + ntfsinfo_time_to_str( + data->quota_q.change_time)); + ntfs_log_verbose("\t\tThreshold:\t\t %lld (0x%llx)\n", + (unsigned long long) + sle64_to_cpu(data->quota_q.threshold), + (unsigned long long) + sle64_to_cpu(data->quota_q.threshold)); + ntfs_log_verbose("\t\tLimit:\t\t\t %lld (0x%llx)\n", + (unsigned long long) + sle64_to_cpu(data->quota_q.limit), + (unsigned long long) + sle64_to_cpu(data->quota_q.limit)); + ntfs_log_verbose("\t\tExceeded time:\t\t %lld (0x%llx)\n", + (unsigned long long) + sle64_to_cpu(data->quota_q.exceeded_time), + (unsigned long long) + sle64_to_cpu(data->quota_q.exceeded_time)); + if (le16_to_cpu(entry->data_length) > 48) { char *sid; sid = ntfs_sid_to_mbs(&data->quota_q.sid, NULL, 0); ntfs_log_verbose("\t\tOwner SID:\t\t %s\n", sid); @@ -1333,7 +1524,7 @@ static void ntfs_dump_index_data(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) break; default: ntfs_log_verbose("\t\tIndex attr type is UNKNOWN: \t 0x%08x\n", - le32_to_cpu(type)); + (unsigned)type); break; } } @@ -1355,38 +1546,54 @@ static int ntfs_dump_index_entries(INDEX_ENTRY *entry, INDEX_ATTR_TYPE type) numb_entries++; continue; } - ntfs_log_verbose("\t\tEntry length:\t\t %u\n", - le16_to_cpu(entry->length)); - ntfs_log_verbose("\t\tKey length:\t\t %u\n", - le16_to_cpu(entry->key_length)); - ntfs_log_verbose("\t\tFlags:\t\t\t 0x%02x\n", - le16_to_cpu(entry->ie_flags)); + ntfs_log_verbose("\t\tEntry length:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(entry->length), + (unsigned)le16_to_cpu(entry->length)); + ntfs_log_verbose("\t\tKey length:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(entry->key_length), + (unsigned)le16_to_cpu(entry->key_length)); + ntfs_log_verbose("\t\tIndex entry flags:\t 0x%02x\n", + (unsigned)le16_to_cpu(entry->ie_flags)); if (entry->ie_flags & INDEX_ENTRY_NODE) - ntfs_log_verbose("\t\tSubnode VCN:\t\t 0x%llx\n", - sle64_to_cpu(*(VCN*)((u8*)entry + - le16_to_cpu(entry->length) - sizeof(VCN)))); + ntfs_log_verbose("\t\tSubnode VCN:\t\t %lld (0x%llx)\n", + ntfs_ie_get_vcn(entry), + ntfs_ie_get_vcn(entry)); if (entry->ie_flags & INDEX_ENTRY_END) break; switch (type) { case INDEX_ATTR_DIRECTORY_I30: - ntfs_log_verbose("\t\tFILE record number:\t %llu\n", + ntfs_log_verbose("\t\tFILE record number:\t %llu " + "(0x%llx)\n", (unsigned long long) + MREF_LE(entry->indexed_file), + (unsigned long long) MREF_LE(entry->indexed_file)); ntfs_dump_filename("\t\t", &entry->key.file_name); break; default: - ntfs_log_verbose("\t\tData offset:\t\t %u\n", - le16_to_cpu(entry->data_offset)); - ntfs_log_verbose("\t\tData length:\t\t %u\n", - le16_to_cpu(entry->data_length)); + ntfs_log_verbose("\t\tData offset:\t\t %u (0x%x)\n", + (unsigned) + le16_to_cpu(entry->data_offset), + (unsigned) + le16_to_cpu(entry->data_offset)); + ntfs_log_verbose("\t\tData length:\t\t %u (0x%x)\n", + (unsigned) + le16_to_cpu(entry->data_length), + (unsigned) + le16_to_cpu(entry->data_length)); ntfs_dump_index_key(entry, type); ntfs_log_verbose("\t\tKey Data:\n"); ntfs_dump_index_data(entry, type); break; } - entry = (INDEX_ENTRY *)((u8 *)entry + - le16_to_cpu(entry->length)); + if (!entry->length) { + ntfs_log_verbose("\tWARNING: Corrupt index entry, " + "skipping the remainder of this index " + "block.\n"); + break; + } + entry = (INDEX_ENTRY*)((u8*)entry + le16_to_cpu(entry->length)); numb_entries++; ntfs_log_verbose("\n"); } @@ -1406,7 +1613,7 @@ static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr, if (!attr->name_length) return INDEX_ATTR_UNKNOWN; - + if (index_root->type) { if (index_root->type == AT_FILE_NAME) return INDEX_ATTR_DIRECTORY_I30; @@ -1416,12 +1623,12 @@ static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr, index_root->type); return INDEX_ATTR_UNKNOWN; } - + if (utils_is_metadata(ni) <= 0) return INDEX_ATTR_UNKNOWN; if (utils_inode_get_name(ni, file_name, sizeof(file_name)) <= 0) return INDEX_ATTR_UNKNOWN; - + if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SDH)) return INDEX_ATTR_SECURE_SDH; else if (COMPARE_INDEX_NAMES(attr, NTFS_INDEX_SII)) @@ -1438,7 +1645,7 @@ static INDEX_ATTR_TYPE get_index_attr_type(ntfs_inode *ni, ATTR_RECORD *attr, else if (!strcmp(file_name, "/$Extend/$ObjId")) return INDEX_ATTR_OBJID_O; } - + return INDEX_ATTR_UNKNOWN; } @@ -1463,6 +1670,22 @@ static void ntfs_dump_index_attr_type(INDEX_ATTR_TYPE type) printf("\n"); } +static void ntfs_dump_index_header(const char *indent, INDEX_HEADER *idx) +{ + printf("%sEntries Offset:\t\t %u (0x%x)\n", indent, + (unsigned)le32_to_cpu(idx->entries_offset), + (unsigned)le32_to_cpu(idx->entries_offset)); + printf("%sIndex Size:\t\t %u (0x%x)\n", indent, + (unsigned)le32_to_cpu(idx->index_length), + (unsigned)le32_to_cpu(idx->index_length)); + printf("%sAllocated Size:\t\t %u (0x%x)\n", indent, + (unsigned)le32_to_cpu(idx->allocated_size), + (unsigned)le32_to_cpu(idx->allocated_size)); + printf("%sIndex header flags:\t 0x%02x\n", indent, idx->ih_flags); + + /* FIXME: there are 3 reserved bytes here */ +} + /** * ntfs_dump_attr_index_root() * @@ -1480,45 +1703,75 @@ static void ntfs_dump_attr_index_root(ATTR_RECORD *attr, ntfs_inode *ni) type = get_index_attr_type(ni, attr, index_root); printf("\tIndexed Attr Type:\t "); ntfs_dump_index_attr_type(type); - + /* collation rule dumping */ - printf("\tCollation Rule:\t\t %u\n", - (unsigned int)le32_to_cpu(index_root->collation_rule)); + printf("\tCollation Rule:\t\t %u (0x%x)\n", + (unsigned)le32_to_cpu(index_root->collation_rule), + (unsigned)le32_to_cpu(index_root->collation_rule)); /* COLLATION_BINARY, COLLATION_FILE_NAME, COLLATION_UNICODE_STRING, COLLATION_NTOFS_ULONG, COLLATION_NTOFS_SID, COLLATION_NTOFS_SECURITY_HASH, COLLATION_NTOFS_ULONGS */ - printf("\tIndex Block Size:\t %u\n", - (unsigned int)le32_to_cpu(index_root->index_block_size)); - printf("\tClusters Per Block:\t %u\n", - index_root->clusters_per_index_block); + printf("\tIndex Block Size:\t %u (0x%x)\n", + (unsigned)le32_to_cpu(index_root->index_block_size), + (unsigned)le32_to_cpu(index_root->index_block_size)); + printf("\tClusters Per Block:\t %u (0x%x)\n", + (unsigned)index_root->clusters_per_index_block, + (unsigned)index_root->clusters_per_index_block); - /* index header starts here */ - printf("\tAllocated Size:\t\t %u\n", - (unsigned int)le32_to_cpu(index_root->index.allocated_size)); - printf("\tUsed Size:\t\t %u\n", - (unsigned int)le32_to_cpu(index_root->index.index_length)); + ntfs_dump_index_header("\t", &index_root->index); - /* the flags are 8bit long, no need for byte-order handling */ - printf("\tFlags:\t\t\t 0x%02x\n",index_root->index.ih_flags); - - entry = (INDEX_ENTRY *)((u8 *)index_root + + entry = (INDEX_ENTRY*)((u8*)index_root + le32_to_cpu(index_root->index.entries_offset) + 0x10); - ntfs_log_verbose("\tDumping index block:\n"); + ntfs_log_verbose("\tDumping index root:\n"); printf("\tIndex entries total:\t %d\n", ntfs_dump_index_entries(entry, type)); } static void ntfs_dump_usa_lsn(const char *indent, MFT_RECORD *mrec) { - printf("%sUpd. Seq. Array Offset:\t %hu\n", indent, - le16_to_cpu(mrec->usa_ofs)); - printf("%sUpd. Seq. Array Count: \t %hu\n", indent, - le16_to_cpu(mrec->usa_count)); - printf("%sUpd. Seq. Number:\t %hu\n", indent, - *(u16 *)((u8 *)mrec + le16_to_cpu(mrec->usa_ofs))); + printf("%sUpd. Seq. Array Off.:\t %u (0x%x)\n", indent, + (unsigned)le16_to_cpu(mrec->usa_ofs), + (unsigned)le16_to_cpu(mrec->usa_ofs)); + printf("%sUpd. Seq. Array Count:\t %u (0x%x)\n", indent, + (unsigned)le16_to_cpu(mrec->usa_count), + (unsigned)le16_to_cpu(mrec->usa_count)); + printf("%sUpd. Seq. Number:\t %u (0x%x)\n", indent, + (unsigned)le16_to_cpup((u16 *)((u8 *)mrec + + le16_to_cpu(mrec->usa_ofs))), + (unsigned)le16_to_cpup((u16 *)((u8 *)mrec + + le16_to_cpu(mrec->usa_ofs)))); printf("%sLogFile Seq. Number:\t 0x%llx\n", indent, - (long long int)sle64_to_cpu(mrec->lsn)); + (unsigned long long)sle64_to_cpu(mrec->lsn)); +} + + +static s32 ntfs_dump_index_block(INDEX_BLOCK *ib, INDEX_ATTR_TYPE type, + u32 ib_size) +{ + INDEX_ENTRY *entry; + + if (ntfs_mst_post_read_fixup((NTFS_RECORD*)ib, ib_size)) { + ntfs_log_perror("Damaged INDX record"); + return -1; + } + ntfs_log_verbose("\tDumping index block:\n"); + if (opts.verbose) + ntfs_dump_usa_lsn("\t\t", (MFT_RECORD*)ib); + + ntfs_log_verbose("\t\tNode VCN:\t\t %lld (0x%llx)\n", + (unsigned long long)sle64_to_cpu(ib->index_block_vcn), + (unsigned long long)sle64_to_cpu(ib->index_block_vcn)); + + entry = (INDEX_ENTRY*)((u8*)ib + + le32_to_cpu(ib->index.entries_offset) + 0x18); + + if (opts.verbose) { + ntfs_dump_index_header("\t\t", &ib->index); + printf("\n"); + } + + return ntfs_dump_index_entries(entry, type); } /** @@ -1529,8 +1782,7 @@ static void ntfs_dump_usa_lsn(const char *indent, MFT_RECORD *mrec) static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) { INDEX_ALLOCATION *allocation, *tmp_alloc; - INDEX_ENTRY *entry; - INDEX_ROOT *index_root; + INDEX_ROOT *ir; INDEX_ATTR_TYPE type; int total_entries = 0; int total_indx_blocks = 0; @@ -1540,24 +1792,24 @@ static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) u32 name_len; s64 data_size; - index_root = ntfs_index_root_get(ni, attr); - if (!index_root) { + ir = ntfs_index_root_get(ni, attr); + if (!ir) { ntfs_log_perror("Failed to read $INDEX_ROOT attribute"); return; } - - type = get_index_attr_type(ni, attr, index_root); - + + type = get_index_attr_type(ni, attr, ir); + name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset)); name_len = attr->name_length; - + byte = bitmap = ntfs_attr_readall(ni, AT_BITMAP, name, name_len, NULL); if (!byte) { ntfs_log_perror("Failed to read $BITMAP attribute"); goto out_index_root; } - tmp_alloc = allocation = ntfs_attr_readall(ni, AT_INDEX_ALLOCATION, + tmp_alloc = allocation = ntfs_attr_readall(ni, AT_INDEX_ALLOCATION, name, name_len, &data_size); if (!tmp_alloc) { ntfs_log_perror("Failed to read $INDEX_ALLOCATION attribute"); @@ -1567,45 +1819,36 @@ static void ntfs_dump_attr_index_allocation(ATTR_RECORD *attr, ntfs_inode *ni) bit = 0; while ((u8 *)tmp_alloc < (u8 *)allocation + data_size) { if (*byte & (1 << bit)) { - if (ntfs_mst_post_read_fixup((NTFS_RECORD *) tmp_alloc, - index_root->index_block_size)) { - ntfs_log_perror("Damaged INDX record"); - goto out_allocation; + int entries; + + entries = ntfs_dump_index_block(tmp_alloc, type, + le32_to_cpu( + ir->index_block_size)); + if (entries != -1) { + total_entries += entries; + total_indx_blocks++; + ntfs_log_verbose("\tIndex entries:\t\t %d\n", + entries); } - entry = (INDEX_ENTRY *)((u8 *)tmp_alloc + le32_to_cpu( - tmp_alloc->index.entries_offset) + 0x18); - ntfs_log_verbose("\tDumping index block (VCN 0x%llx, " - "used %u/%u, flags 0x%02x):\n", - le64_to_cpu(tmp_alloc->index_block_vcn), - (unsigned int)le32_to_cpu(tmp_alloc-> - index.index_length), (unsigned int) - le32_to_cpu(tmp_alloc->index. - allocated_size), - tmp_alloc->index.ih_flags); - if (opts.verbose) { - ntfs_dump_usa_lsn("\t\t", - (MFT_RECORD *)tmp_alloc); - printf("\n"); - } - total_entries += ntfs_dump_index_entries(entry, type); - total_indx_blocks++; } - tmp_alloc = (INDEX_ALLOCATION *)((u8 *)tmp_alloc + - index_root->index_block_size); + tmp_alloc = (INDEX_ALLOCATION *)((u8 *)tmp_alloc + + le32_to_cpu( + ir->index_block_size)); bit++; if (bit > 7) { bit = 0; byte++; } } + printf("\tIndex entries total:\t %d\n", total_entries); printf("\tINDX blocks total:\t %d\n", total_indx_blocks); -out_allocation: + free(allocation); out_bitmap: free(bitmap); out_index_root: - free(index_root); + free(ir); } /** @@ -1639,10 +1882,14 @@ static void ntfs_dump_attr_ea_information(ATTR_RECORD *attr) ea_info = (EA_INFORMATION*)((u8*)attr + le16_to_cpu(attr->value_offset)); - printf("\tPacked EA length:\t %u\n", le16_to_cpu(ea_info->ea_length)); - printf("\tNEED_EA count:\t\t %u\n", - le16_to_cpu(ea_info->need_ea_count)); - printf("\tUnpacked EA length:\t %u\n", + printf("\tPacked EA length:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(ea_info->ea_length), + (unsigned)le16_to_cpu(ea_info->ea_length)); + printf("\tNEED_EA count:\t\t %u (0x%x)\n", + (unsigned)le16_to_cpu(ea_info->need_ea_count), + (unsigned)le16_to_cpu(ea_info->need_ea_count)); + printf("\tUnpacked EA length:\t %u (0x%x)\n", + (unsigned)le32_to_cpu(ea_info->ea_query_length), (unsigned)le32_to_cpu(ea_info->ea_query_length)); } @@ -1664,13 +1911,12 @@ static void ntfs_dump_attr_ea(ATTR_RECORD *attr, ntfs_volume *vol) if (!opts.verbose) return; /* FIXME: We don't handle fragmented mapping pairs case. */ - rl = ntfs_mapping_pairs_decompress(vol, attr, 0); + rl = ntfs_mapping_pairs_decompress(vol, attr, NULL); if (rl) { s64 bytes_read; - buf = malloc(data_size); + buf = ntfs_malloc(data_size); if (!buf) { - ntfs_log_perror("malloc failed"); free(rl); return; } @@ -1694,24 +1940,28 @@ static void ntfs_dump_attr_ea(ATTR_RECORD *attr, ntfs_volume *vol) ea = (EA_ATTR*)((u8*)attr + le16_to_cpu(attr->value_offset)); } while (1) { - printf("\n\tFlags:\t\t "); + printf("\n\tEA flags:\t\t "); if (ea->flags) { if (ea->flags == NEED_EA) printf("NEED_EA\n"); else - printf("Unknown (0x%02x)\n", ea->flags); + printf("Unknown (0x%02x)\n", + (unsigned)ea->flags); } else printf("NONE\n"); - printf("\tName length:\t %d\n", ea->name_length); - printf("\tValue length:\t %d\n", - le16_to_cpu(ea->value_length)); + printf("\tName length:\t %d (0x%x)\n", + (unsigned)ea->name_length, + (unsigned)ea->name_length); + printf("\tValue length:\t %d (0x%x)\n", + (unsigned)le16_to_cpu(ea->value_length), + (unsigned)le16_to_cpu(ea->value_length)); printf("\tName:\t\t '%s'\n", ea->name); printf("\tValue:\t\t "); if (ea->name_length == 11 && !strncmp((const char*)"SETFILEBITS", (const char*)ea->name, 11)) - printf("0%o\n", le32_to_cpu(*(le32*)(ea->value + - ea->name_length + 1))); + printf("0%o\n", (unsigned)le32_to_cpu(*(le32*) + (ea->value + ea->name_length + 1))); else printf("'%s'\n", ea->value + ea->name_length + 1); if (ea->next_entry_offset) @@ -1735,13 +1985,26 @@ static void ntfs_dump_attr_property_set(ATTR_RECORD *attr __attribute__((unused) /* TODO */ } +static void ntfs_hex_dump(void *buf,unsigned int length); + /** * ntfs_dump_attr_logged_utility_stream() * * dump the property_set attribute */ -static void ntfs_dump_attr_logged_utility_stream(ATTR_RECORD *attr __attribute__((unused))) +static void ntfs_dump_attr_logged_utility_stream(ATTR_RECORD *attr, + ntfs_inode *ni) { + char *buf; + s64 size; + + if (!opts.verbose) + return; + buf = ntfs_attr_readall(ni, AT_LOGGED_UTILITY_STREAM, + ntfs_attr_get_name(attr), attr->name_length, &size); + if (buf) + ntfs_hex_dump(buf, size); + free(buf); /* TODO */ } @@ -1796,8 +2059,8 @@ static void ntfs_dump_attr_unknown(ATTR_RECORD *attr) /* hex dump */ printf("\tDumping some of the attribute data:\n"); ntfs_hex_dump((u8*)attr + le16_to_cpu(attr->value_offset), - (le16_to_cpu(attr->value_length)>128)?128 - :le16_to_cpu(attr->value_length)); + (le32_to_cpu(attr->value_length) > 128) ? + 128 : le32_to_cpu(attr->value_length)); } } @@ -1806,17 +2069,23 @@ static void ntfs_dump_attr_unknown(ATTR_RECORD *attr) */ static void ntfs_dump_inode_general_info(ntfs_inode *inode) { - u16 inode_flags = inode->mrec->flags; + MFT_RECORD *mrec = inode->mrec; + le16 inode_flags = mrec->flags; - printf("Dumping Inode #%llu\n",(long long)inode->mft_no); + printf("Dumping Inode %llu (0x%llx)\n", + (long long)inode->mft_no, + (unsigned long long)inode->mft_no); - ntfs_dump_usa_lsn("", inode->mrec); - printf("MFT Record Seq. Number:\t %hu\n", - (short unsigned int)le16_to_cpu(inode->mrec->sequence_number)); - printf("Number of Hard Links:\t %hu\n", - le16_to_cpu(inode->mrec->link_count)); - printf("Attribute Offset:\t %hu\n", - le16_to_cpu(inode->mrec->attrs_offset)); + ntfs_dump_usa_lsn("", mrec); + printf("MFT Record Seq. Numb.:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->sequence_number), + (unsigned)le16_to_cpu(mrec->sequence_number)); + printf("Number of Hard Links:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->link_count), + (unsigned)le16_to_cpu(mrec->link_count)); + printf("Attribute Offset:\t %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->attrs_offset), + (unsigned)le16_to_cpu(mrec->attrs_offset)); printf("MFT Record Flags:\t "); if (inode_flags) { @@ -1838,23 +2107,36 @@ static void ntfs_dump_inode_general_info(ntfs_inode *inode) inode_flags &= ~MFT_RECORD_IS_VIEW_INDEX; } if (inode_flags) - printf("UNKNOWN: 0x%04hx", inode_flags); + printf("UNKNOWN: 0x%04x", (unsigned)le16_to_cpu( + inode_flags)); } else { printf("none"); } printf("\n"); - printf("Bytes Used:\t\t %u bytes\n", - (unsigned int)le32_to_cpu(inode->mrec->bytes_in_use)); - printf("Bytes Allocated:\t %u bytes\n", - (unsigned int)le32_to_cpu(inode->mrec->bytes_allocated)); + printf("Bytes Used:\t\t %u (0x%x) bytes\n", + (unsigned)le32_to_cpu(mrec->bytes_in_use), + (unsigned)le32_to_cpu(mrec->bytes_in_use)); + printf("Bytes Allocated:\t %u (0x%x) bytes\n", + (unsigned)le32_to_cpu(mrec->bytes_allocated), + (unsigned)le32_to_cpu(mrec->bytes_allocated)); - if (inode->mrec->base_mft_record) { - printf("Base MFT Record:\t %llu\n", - MREF_LE(inode->mrec->base_mft_record)); + if (mrec->base_mft_record) { + printf("Base MFT Record:\t %llu (0x%llx)\n", + (unsigned long long) + MREF_LE(mrec->base_mft_record), + (unsigned long long) + MREF_LE(mrec->base_mft_record)); } - printf("Next Attribute Instance: %hu\n", - le16_to_cpu(inode->mrec->next_attr_instance)); + printf("Next Attribute Instance: %u (0x%x)\n", + (unsigned)le16_to_cpu(mrec->next_attr_instance), + (unsigned)le16_to_cpu(mrec->next_attr_instance)); + + printf("MFT Padding:\t"); + ntfs_dump_bytes((u8 *)mrec, le16_to_cpu(mrec->usa_ofs) + + 2 * le16_to_cpu(mrec->usa_count), + le16_to_cpu(mrec->attrs_offset)); + printf("\n"); } /** @@ -1868,16 +2150,16 @@ static void ntfs_dump_file_attributes(ntfs_inode *inode) see ntfs_attr_lookup documentation for detailed explanation */ ctx = ntfs_attr_get_search_ctx(inode, NULL); while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { - if (ctx->attr->type == AT_END || ctx->attr->type == AT_UNUSED) { printf("Weird: %s attribute type was found, please " - "report this.\n", - get_attribute_type_name(ctx->attr->type)); + "report this.\n", + get_attribute_type_name( + ctx->attr->type)); continue; } - - ntfs_dump_attribute_header(ctx->attr, inode->vol); - + + ntfs_dump_attribute_header(ctx, inode->vol); + switch (ctx->attr->type) { case AT_STANDARD_INFORMATION: ntfs_dump_attr_standard_information(ctx->attr); @@ -1892,7 +2174,8 @@ static void ntfs_dump_file_attributes(ntfs_inode *inode) ntfs_dump_attr_object_id(ctx->attr, inode->vol); break; case AT_SECURITY_DESCRIPTOR: - ntfs_dump_attr_security_descriptor(ctx->attr, inode->vol); + ntfs_dump_attr_security_descriptor(ctx->attr, + inode->vol); break; case AT_VOLUME_NAME: ntfs_dump_attr_volume_name(ctx->attr); @@ -1925,7 +2208,7 @@ static void ntfs_dump_file_attributes(ntfs_inode *inode) ntfs_dump_attr_property_set(ctx->attr); break; case AT_LOGGED_UTILITY_STREAM: - ntfs_dump_attr_logged_utility_stream(ctx->attr); + ntfs_dump_attr_logged_utility_stream(ctx->attr, inode); break; default: ntfs_dump_attr_unknown(ctx->attr); @@ -1935,7 +2218,7 @@ static void ntfs_dump_file_attributes(ntfs_inode *inode) /* if we exited the loop before we're done - notify the user */ if (errno != ENOENT) { ntfs_log_perror("ntfsinfo error: stopped before finished " - "enumerating attributes"); + "enumerating attributes"); } else { printf("End of inode reached\n"); } @@ -1943,8 +2226,6 @@ static void ntfs_dump_file_attributes(ntfs_inode *inode) /* close all data-structures we used */ ntfs_attr_put_search_ctx(ctx); ntfs_inode_close(inode); - - /* happily exit */ } /** @@ -1959,16 +2240,23 @@ int main(int argc, char **argv) { ntfs_volume *vol; + setlinebuf(stdout); + ntfs_log_set_handler(ntfs_log_handler_outerr); - if (!parse_options(argc, argv)) - return 1; + if (!parse_options(argc, argv)) { + printf("Failed to parse command line options\n"); + exit(1); + } utils_set_locale(); - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); - if (!vol) - return 1; + vol = utils_mount_volume(opts.device, MS_RDONLY | + (opts.force ? MS_RECOVER : 0)); + if (!vol) { + printf("Failed to open '%s'.\n", opts.device); + exit(1); + } /* * if opts.mft is not 0, then we will print out information about @@ -1981,7 +2269,8 @@ int main(int argc, char **argv) ntfs_inode *inode; /* obtain the inode */ if (opts.filename) { - inode = ntfs_pathname_to_inode(vol, NULL, opts.filename); + inode = ntfs_pathname_to_inode(vol, NULL, + opts.filename); } else { inode = ntfs_inode_open(vol, MK_MREF(opts.inode, 0)); } diff --git a/ntfsprogs/ntfslabel.c b/ntfsprogs/ntfslabel.c index a0412b56..6d9beb1f 100644 --- a/ntfsprogs/ntfslabel.c +++ b/ntfsprogs/ntfslabel.c @@ -325,7 +325,7 @@ static int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, "allowed. Truncating excess characters.\n", (unsigned)(0x100 / sizeof(ntfschar))); label_len = 0x100; - new_label[label_len / sizeof(ntfschar)] = cpu_to_le16(L'\0'); + new_label[label_len / sizeof(ntfschar)] = 0; } if (a) { if (resize_resident_attribute_value(ctx->mrec, a, label_len)) { @@ -393,8 +393,9 @@ int main(int argc, char **argv) if (!opts.label) opts.noaction++; - vol = utils_mount_volume(opts.device, opts.noaction ? MS_RDONLY : 0, - opts.force); + vol = utils_mount_volume(opts.device, + (opts.noaction ? MS_RDONLY : 0) | + (opts.force ? MS_RECOVER : 0)); if (!vol) return 1; diff --git a/ntfsprogs/ntfsls.c b/ntfsprogs/ntfsls.c index a7c2fa7c..fa3e1f32 100644 --- a/ntfsprogs/ntfsls.c +++ b/ntfsprogs/ntfsls.c @@ -650,7 +650,8 @@ int main(int argc, char **argv) utils_set_locale(); - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + vol = utils_mount_volume(opts.device, MS_RDONLY | + (opts.force ? MS_RECOVER : 0)); if (!vol) { // FIXME: Print error... (AIA) return 2; diff --git a/ntfsprogs/ntfsmove.c b/ntfsprogs/ntfsmove.c index ff2457d4..5eb86106 100644 --- a/ntfsprogs/ntfsmove.c +++ b/ntfsprogs/ntfsmove.c @@ -745,7 +745,8 @@ static s64 move_datarun(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec, } /** - * move_attribute + * move_attribute - + * * > 0 Bytes moved / size to be moved * = 0 Nothing to do * < 0 Error @@ -790,7 +791,8 @@ static s64 move_attribute(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec, } /** - * move_file + * move_file - + * * > 0 Bytes moved / size to be moved * = 0 Nothing to do * < 0 Error @@ -873,8 +875,10 @@ int main(int argc, char *argv[]) if (opts.noaction) flags |= MS_RDONLY; + if (opts.force) + flags |= MS_RECOVER; - vol = utils_mount_volume(opts.device, flags, opts.force); + vol = utils_mount_volume(opts.device, flags); if (!vol) { ntfs_log_info("!vol\n"); return 1; @@ -888,10 +892,19 @@ int main(int argc, char *argv[]) count = move_file(vol, inode, opts.location, 0); if ((count > 0) && (!opts.nodirty)) { - if (ntfs_volume_write_flags(vol, vol->flags | VOLUME_IS_DIRTY) < - 0) { - ntfs_log_error("Couldn't mark volume dirty\n"); + + /* Porting note: libntfs-3g does not automatically set or clear + * dirty flags on mount/unmount. It always preserves them until + * they are explicitly changed with ntfs_volume_write_flags. + * This means that the dirty flag is possibly not set, but + * should be set. So we explicitly set it with a call to + * ntfs_volume_write_flags. */ + if(!(vol->flags & VOLUME_IS_DIRTY) && ntfs_volume_write_flags( + vol, vol->flags | VOLUME_IS_DIRTY)) { + ntfs_log_error("Error: Failed to set volume dirty " + "flag (%d (%s))!\n", errno, strerror(errno)); } + ntfs_log_info("Relocated %lld bytes\n", count); } if (count >= 0) diff --git a/ntfsprogs/ntfsmove.h b/ntfsprogs/ntfsmove.h index 1aeeef2a..ffc1519a 100644 --- a/ntfsprogs/ntfsmove.h +++ b/ntfsprogs/ntfsmove.h @@ -39,7 +39,6 @@ struct options { int verbose; /* Extra output */ int noaction; /* Do not write to disk */ int nodirty; /* Do not mark volume dirty */ - u8 padding[4]; /* Unused: alignment to 64 bit. */ }; #endif /* _NTFSMOVE_H_ */ diff --git a/ntfsprogs/ntfsprogs.8.in b/ntfsprogs/ntfsprogs.8.in index 84aef461..8664d930 100644 --- a/ntfsprogs/ntfsprogs.8.in +++ b/ntfsprogs/ntfsprogs.8.in @@ -1,9 +1,10 @@ .\" Copyright (c) 2002\-2005 Richard Russon. .\" Copyright (c) 2002\-2003 Anton Altaparmakov. .\" Copyright (c) 2005\-2006 Szabolcs Szakacsits. +.\" Copyright (c) 2005\-2007 Yura Pakhuchiy. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH NTFSPROGS 8 "April 2006" "ntfs-3g @VERSION@" +.TH NTFSPROGS 8 "September 2007" "ntfs-3g @VERSION@" .SH NAME ntfsprogs \- tools for doing neat things with NTFS .SH OVERVIEW @@ -28,7 +29,7 @@ available for free and come with full source code. \- Compare two NTFS filesystems and tell the differences. .PP .BR ntfscp (8) -\- Overwrite a file on an NTFS. +\- Copy a file to an NTFS volume. .PP .BR ntfsfix (8) \- Check and fix some common errors, clear the LogFile and make Windows diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index eee459d0..bbbb58eb 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -4,6 +4,7 @@ * Copyright (c) 2002-2006 Szabolcs Szakacsits * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2002-2003 Richard Russon + * Copyright (c) 2007 Yura Pakhuchiy * * This utility will resize an NTFS volume without data loss. * @@ -65,6 +66,7 @@ #include "runlist.h" #include "utils.h" /* #include "version.h" */ +#include "misc.h" static const char *EXEC_NAME = "ntfsresize"; @@ -125,7 +127,7 @@ static const char *many_bad_sectors_msg = "* other reason. We suggest to get a replacement disk as soon as possible. *\n" "***************************************************************************\n"; -struct { +static struct { int verbose; int debug; int ro_flag; @@ -140,7 +142,6 @@ struct { struct bitmap { s64 size; u8 *bm; - u8 padding[4]; /* Unused: padding to 64 bit. */ }; #define NTFS_PROGBAR 0x0001 @@ -152,7 +153,6 @@ struct progress_bar { int resolution; int flags; float unit; - u8 padding[4]; /* Unused: padding to 64 bit. */ }; struct llcn_t { @@ -202,7 +202,7 @@ typedef struct { /* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster allocation related structure, attached to ntfs_resize_t */ -s64 max_free_cluster_range = 0; +static s64 max_free_cluster_range = 0; #define NTFS_MBYTE (1000 * 1000) @@ -349,7 +349,7 @@ static void proceed_question(void) printf("Are you sure you want to proceed (y/[n])? "); buf[0] = 0; fgets(buf, sizeof(buf), stdin); - if (strchr(short_yes, buf[0]) == 0) { + if (!strchr(short_yes, buf[0])) { printf("OK quitting. NO CHANGES have been made to your " "NTFS volume.\n"); exit(1); @@ -369,6 +369,7 @@ static void version(void) printf("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); printf("Copyright (c) 2002-2005 Anton Altaparmakov\n"); printf("Copyright (c) 2002-2003 Richard Russon\n"); + printf("Copyright (c) 2007 Yura Pakhuchiy\n"); printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -803,11 +804,9 @@ static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck) lcn_length); for (j = 0; j < lcn_length; j++) { - u64 k = (u64)lcn + j; if (k >= (u64)vol->nr_clusters) { - long long outsiders = lcn_length - j; fsck->outsider += outsiders; @@ -823,7 +822,7 @@ static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck) if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) { if (++fsck->multi_ref <= 10 || opt.verbose) printf("Cluster %lld is referenced " - "multiply times!\n", + "multiple times!\n", (long long)k); continue; } @@ -1173,7 +1172,7 @@ static void replace_attribute_runlist(ntfs_volume *vol, ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) le32_to_cpu(ctx->mrec->bytes_in_use)); - next_attr = (char *)a + le16_to_cpu(a->length); + next_attr = (char *)a + le32_to_cpu(a->length); l = mp_size - l; ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) @@ -1200,11 +1199,12 @@ static void replace_attribute_runlist(ntfs_volume *vol, memmove(next_attr + l, next_attr, remains_size); ctx->mrec->bytes_in_use = cpu_to_le32(l + le32_to_cpu(ctx->mrec->bytes_in_use)); - a->length += l; + a->length = cpu_to_le32(le32_to_cpu(a->length) + l); } - if (!(mp = calloc(1, mp_size))) - perr_exit("Couldn't get memory"); + mp = ntfs_calloc(mp_size); + if (!mp) + perr_exit("ntfsc_calloc couldn't get memory"); if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl, 0, NULL)) perr_exit("ntfs_mapping_pairs_build"); @@ -1468,8 +1468,9 @@ static void rl_split_run(runlist **rl, int run, s64 pos) size_head = run * sizeof(runlist_element); size_tail = (items - run - 1) * sizeof(runlist_element); - if (!(rl_new = (runlist *)malloc(new_size))) - perr_exit("malloc"); + rl_new = ntfs_malloc(new_size); + if (!rl_new) + perr_exit("ntfs_malloc"); rle_new = rl_new + run; rle = *rl + run; @@ -1611,8 +1612,8 @@ static int is_mftdata(ntfs_resize_t *resize) if (resize->mref == 0) return 1; - if ( MREF(resize->mrec->base_mft_record) == 0 && - MSEQNO(resize->mrec->base_mft_record) != 0) + if (MREF_LE(resize->mrec->base_mft_record) == 0 && + MSEQNO_LE(resize->mrec->base_mft_record) != 0) return 1; return 0; @@ -1719,9 +1720,9 @@ static void relocate_inodes(ntfs_resize_t *resize) progress_init(&resize->progress, 0, resize->relocations, resize->progress.flags); resize->relocations = 0; - resize->mrec = (MFT_RECORD *)malloc(resize->vol->mft_record_size); + resize->mrec = ntfs_malloc(resize->vol->mft_record_size); if (!resize->mrec) - perr_exit("malloc failed"); + perr_exit("ntfs_malloc failed"); nr_mft_records = resize->vol->mft_na->initialized_size >> resize->vol->mft_record_size_bits; @@ -1874,9 +1875,9 @@ static void truncate_badclust_bad_attr(ntfs_resize_t *resize) rl_truncate(&rl_bad, nr_clusters); - a->highest_vcn = cpu_to_le64(nr_clusters - 1LL); - a->allocated_size = cpu_to_le64(nr_clusters * vol->cluster_size); - a->data_size = cpu_to_le64(nr_clusters * vol->cluster_size); + a->highest_vcn = cpu_to_sle64(nr_clusters - 1LL); + a->allocated_size = cpu_to_sle64(nr_clusters * vol->cluster_size); + a->data_size = cpu_to_sle64(nr_clusters * vol->cluster_size); replace_attribute_runlist(vol, resize->ctx, rl_bad); @@ -1951,10 +1952,10 @@ static void truncate_bitmap_data_attr(ntfs_resize_t *resize) realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); } - a->highest_vcn = cpu_to_le64(nr_bm_clusters - 1LL); - a->allocated_size = cpu_to_le64(nr_bm_clusters * vol->cluster_size); - a->data_size = cpu_to_le64(bm_bsize); - a->initialized_size = cpu_to_le64(bm_bsize); + a->highest_vcn = cpu_to_sle64(nr_bm_clusters - 1LL); + a->allocated_size = cpu_to_sle64(nr_bm_clusters * vol->cluster_size); + a->data_size = cpu_to_sle64(bm_bsize); + a->initialized_size = cpu_to_sle64(bm_bsize); replace_attribute_runlist(vol, resize->ctx, rl); @@ -2030,7 +2031,7 @@ static int check_bad_sectors(ntfs_volume *vol) if (!ctx->attr->non_resident) err_exit("Resident attribute in $BadClust! Please report to " "%s\n", NTFS_DEV_LIST); - /* + /* * FIXME: The below would be partial for non-base records in the * not yet supported multi-record case. Alternatively use audited * ntfs_attr_truncate after an umount & mount. @@ -2119,7 +2120,8 @@ static int setup_lcn_bitmap(struct bitmap *bm, s64 nr_clusters) /* Determine lcn bitmap byte size and allocate it. */ bm->size = rounded_up_division(nr_clusters, 8); - if (!(bm->bm = (unsigned char *)calloc(1, bm->size))) + bm->bm = ntfs_calloc(bm->size); + if (!bm->bm) return -1; bitmap_file_data_fixup(nr_clusters, bm); @@ -2152,7 +2154,7 @@ static void update_bootsector(ntfs_resize_t *r) r->progress.flags |= NTFS_PROGBAR_SUPPRESS; copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old, r->mftmir_rl.length); - bs.mftmirr_lcn = cpu_to_le64(r->mftmir_rl.lcn); + bs.mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn); r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS; } @@ -2237,9 +2239,12 @@ static ntfs_volume *mount_volume(void) err_exit("Device '%s' is mounted. " "You must 'umount' it first.\n", opt.volume); } - - if (!(vol = ntfs_mount(opt.volume, opt.ro_flag /*| MS_NOATIME*/))) { - + /* + * Pass NTFS_MNT_FORENSIC so that the mount process does not modify the + * volume at all. We will do the logfile emptying and dirty setting + * later if needed. + */ + if (!(vol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_FORENSIC))) { int err = errno; perr_printf("Opening '%s' as NTFS failed", opt.volume); @@ -2286,31 +2291,26 @@ static ntfs_volume *mount_volume(void) */ static void prepare_volume_fixup(ntfs_volume *vol) { - u16 flags; + printf("Schedule chkdsk for NTFS consistency check at Windows boot " + "time ...\n"); + vol->flags |= VOLUME_IS_DIRTY; + if (ntfs_volume_write_flags(vol, vol->flags)) + perr_exit("Failed to set the volume dirty"); - flags = vol->flags | VOLUME_IS_DIRTY; - if (vol->major_ver >= 2) - flags |= VOLUME_MOUNTED_ON_NT4; - - printf("Schedule chkdsk for NTFS consistency check at Windows " - "boot time ...\n"); - - if (ntfs_volume_write_flags(vol, flags)) - perr_exit("Failed to set $Volume dirty"); + /* Porting note: This flag does not exist in libntfs-3g. The dirty flag + * is never modified by libntfs-3g on unmount and we set it above. We + * can safely comment out this statement. */ + /* NVolSetWasDirty(vol); */ if (vol->dev->d_ops->sync(vol->dev) == -1) perr_exit("Failed to sync device"); - printf("Resetting $LogFile ... (this might take a while)\n"); - if (ntfs_logfile_reset(vol)) perr_exit("Failed to reset $LogFile"); - if (vol->dev->d_ops->sync(vol->dev) == -1) perr_exit("Failed to sync device"); } - static void set_disk_usage_constraint(ntfs_resize_t *resize) { /* last lcn for a filled up volume (no empty space) */ @@ -2393,7 +2393,7 @@ int main(int argc, char **argv) utils_set_locale(); - if ((vol = mount_volume()) == NULL) + if (!(vol = mount_volume())) err_exit("Couldn't open volume '%s'!\n", opt.volume); device_size = ntfs_device_size_get(vol->dev, vol->sector_size); diff --git a/ntfsprogs/ntfstruncate.c b/ntfsprogs/ntfstruncate.c index 89244fa5..9f80569c 100644 --- a/ntfsprogs/ntfstruncate.c +++ b/ntfsprogs/ntfstruncate.c @@ -715,7 +715,7 @@ int main(int argc, char **argv) * Setup a default $AttrDef. FIXME: Should be reading this from the * volume itself, at ntfs_mount() time. */ - attr_defs = (ATTR_DEF*)&attrdef_ntfs12_array; + attr_defs = (ATTR_DEF*)&attrdef_ntfs3x_array; /* Parse command line options. */ parse_options(argc, argv); @@ -807,4 +807,3 @@ int main(int argc, char **argv) ntfs_log_quiet("ntfstruncate completed successfully. Have a nice day.\n"); return 0; } - diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c index 2714e8e0..0326f99d 100644 --- a/ntfsprogs/ntfsundelete.c +++ b/ntfsprogs/ntfsundelete.c @@ -3,7 +3,8 @@ * * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2004-2005 Holger Ohmacht - * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2005 Anton Altaparmakov + * Copyright (c) 2007 Yura Pakhuchiy * * This utility will recover deleted files from an NTFS volume. * @@ -221,10 +222,12 @@ static int parse_inode_arg(void) */ static void version(void) { - ntfs_log_info("\n%s v%s (libntfs-3g) - Recover deleted files from an NTFS " - "Volume.\n\n", EXEC_NAME, VERSION); + ntfs_log_info("\n%s v%s (libntfs-3g) - Recover deleted files from an " + "NTFS Volume.\n\n", EXEC_NAME, VERSION); ntfs_log_info("Copyright (c) 2002-2005 Richard Russon\n" - "Copyright (c) 2004-2005 Holger Ohmacht\n"); + "Copyright (c) 2004-2005 Holger Ohmacht\n" + "Copyright (c) 2005 Anton Altaparmakov\n" + "Copyright (c) 2007 Yura Pakhuchiy\n"); ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -833,7 +836,8 @@ static void get_parent_name(struct filename* name, ntfs_volume* vol) rec = calloc(1, vol->mft_record_size); if (!rec) { - ntfs_log_error("ERROR: Couldn't allocate memory in get_parent_name()\n"); + ntfs_log_error("ERROR: Couldn't allocate memory in " + "get_parent_name()\n"); return; } @@ -841,10 +845,12 @@ static void get_parent_name(struct filename* name, ntfs_volume* vol) if (!mft_data) { ntfs_log_perror("ERROR: Couldn't open $MFT/$DATA"); } else { - inode_num = MREF(name->parent_mref); + inode_num = MREF_LE(name->parent_mref); - if (ntfs_attr_pread(mft_data, vol->mft_record_size * inode_num, vol->mft_record_size, rec) < 1) { - ntfs_log_error("ERROR: Couldn't read MFT Record %lld.\n", inode_num); + if (ntfs_attr_pread(mft_data, vol->mft_record_size * inode_num, + vol->mft_record_size, rec) < 1) { + ntfs_log_error("ERROR: Couldn't read MFT Record %lld" + ".\n", inode_num); } else if ((filename_attr = verify_parent(name, rec))) { if (ntfs_ucstombs(filename_attr->file_name, filename_attr->file_name_length, @@ -905,11 +911,13 @@ static int get_filenames(struct ufile *file, ntfs_volume* vol) while ((rec = find_attribute(AT_FILE_NAME, ctx))) { /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + attr = (FILE_NAME_ATTR *)((char *)rec + + le16_to_cpu(rec->value_offset)); name = calloc(1, sizeof(*name)); if (!name) { - ntfs_log_error("ERROR: Couldn't allocate memory in get_filenames().\n"); + ntfs_log_error("ERROR: Couldn't allocate memory in " + "get_filenames().\n"); count = -1; break; } @@ -989,28 +997,32 @@ static int get_data(struct ufile *file, ntfs_volume *vol) while ((rec = find_attribute(AT_DATA, ctx))) { data = calloc(1, sizeof(*data)); if (!data) { - ntfs_log_error("ERROR: Couldn't allocate memory in get_data().\n"); + ntfs_log_error("ERROR: Couldn't allocate memory in " + "get_data().\n"); count = -1; break; } data->resident = !rec->non_resident; - data->compressed = rec->flags & ATTR_IS_COMPRESSED; - data->encrypted = rec->flags & ATTR_IS_ENCRYPTED; + data->compressed = (rec->flags & ATTR_IS_COMPRESSED) ? 1 : 0; + data->encrypted = (rec->flags & ATTR_IS_ENCRYPTED) ? 1 : 0; if (rec->name_length) { - data->uname = (ntfschar *) ((char *) rec + le16_to_cpu(rec->name_offset)); + data->uname = (ntfschar *)((char *)rec + + le16_to_cpu(rec->name_offset)); data->uname_len = rec->name_length; - if (ntfs_ucstombs(data->uname, data->uname_len, &data->name, - 0) < 0) { - ntfs_log_error("ERROR: Cannot translate name into current locale.\n"); + if (ntfs_ucstombs(data->uname, data->uname_len, + &data->name, 0) < 0) { + ntfs_log_error("ERROR: Cannot translate name " + "into current locale.\n"); } } if (data->resident) { - data->size_data = le32_to_cpu(rec->value_length); - data->data = ((char*) (rec)) + le16_to_cpu(rec->value_offset); + data->size_data = le32_to_cpu(rec->value_length); + data->data = (char*)rec + + le16_to_cpu(rec->value_offset); } else { data->size_alloc = sle64_to_cpu(rec->allocated_size); data->size_data = sle64_to_cpu(rec->data_size); @@ -1168,17 +1180,20 @@ static int calc_percentage(struct ufile *file, ntfs_volume *vol) clusters_free = 0; if (data->encrypted) { - ntfs_log_verbose("File is encrypted, recovery is impossible.\n"); + ntfs_log_verbose("File is encrypted, recovery is " + "impossible.\n"); continue; } if (data->compressed) { - ntfs_log_verbose("File is compressed, recovery not yet implemented.\n"); + ntfs_log_verbose("File is compressed, recovery not yet " + "implemented.\n"); continue; } if (data->resident) { - ntfs_log_verbose("File is resident, therefore recoverable.\n"); + ntfs_log_verbose("File is resident, therefore " + "recoverable.\n"); percent = 100; data->percent = 100; continue; @@ -1186,16 +1201,18 @@ static int calc_percentage(struct ufile *file, ntfs_volume *vol) rl = data->runlist; if (!rl) { - ntfs_log_verbose("File has no runlist, hence no data.\n"); + ntfs_log_verbose("File has no runlist, hence no data." + "\n"); continue; } if (rl[0].length <= 0) { - ntfs_log_verbose("File has an empty runlist, hence no data.\n"); + ntfs_log_verbose("File has an empty runlist, hence no " + "data.\n"); continue; } - if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ + if (rl[0].lcn == LCN_RL_NOT_MAPPED) { /* extended mft record */ ntfs_log_verbose("Missing segment at beginning, %lld " "clusters\n", (long long)rl[0].length); clusters_inuse += rl[0].length; @@ -1287,12 +1304,18 @@ static void dump_record(struct ufile *file) ntfs_log_quiet("Filename: (%d) %s\n", f->name_space, f->name); ntfs_log_quiet("File Flags: "); - if (f->flags & FILE_ATTR_SYSTEM) ntfs_log_quiet("System "); - if (f->flags & FILE_ATTR_DIRECTORY) ntfs_log_quiet("Directory "); - if (f->flags & FILE_ATTR_SPARSE_FILE) ntfs_log_quiet("Sparse "); - if (f->flags & FILE_ATTR_REPARSE_POINT) ntfs_log_quiet("Reparse "); - if (f->flags & FILE_ATTR_COMPRESSED) ntfs_log_quiet("Compressed "); - if (f->flags & FILE_ATTR_ENCRYPTED) ntfs_log_quiet("Encrypted "); + if (f->flags & FILE_ATTR_SYSTEM) + ntfs_log_quiet("System "); + if (f->flags & FILE_ATTR_DIRECTORY) + ntfs_log_quiet("Directory "); + if (f->flags & FILE_ATTR_SPARSE_FILE) + ntfs_log_quiet("Sparse "); + if (f->flags & FILE_ATTR_REPARSE_POINT) + ntfs_log_quiet("Reparse "); + if (f->flags & FILE_ATTR_COMPRESSED) + ntfs_log_quiet("Compressed "); + if (f->flags & FILE_ATTR_ENCRYPTED) + ntfs_log_quiet("Encrypted "); if (!(f->flags & (FILE_ATTR_SYSTEM | FILE_ATTR_DIRECTORY | FILE_ATTR_SPARSE_FILE | FILE_ATTR_REPARSE_POINT | FILE_ATTR_COMPRESSED | FILE_ATTR_ENCRYPTED))) { @@ -1309,13 +1332,17 @@ static void dump_record(struct ufile *file) ntfs_log_quiet("Size alloc: %lld\n", f->size_alloc); ntfs_log_quiet("Size data: %lld\n", f->size_data); - strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_c)); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_c)); ntfs_log_quiet("Date C: %s\n", buffer); - strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_a)); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_a)); ntfs_log_quiet("Date A: %s\n", buffer); - strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_m)); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_m)); ntfs_log_quiet("Date M: %s\n", buffer); - strftime(buffer, sizeof(buffer), "%F %R", localtime(&f->date_r)); + strftime(buffer, sizeof(buffer), "%F %R", + localtime(&f->date_r)); ntfs_log_quiet("Date R: %s\n", buffer); } @@ -1348,7 +1375,8 @@ static void dump_record(struct ufile *file) } } - ntfs_log_quiet("Amount potentially recoverable %d%%\n", d->percent); + ntfs_log_quiet("Amount potentially recoverable %d%%\n", + d->percent); } ntfs_log_quiet("________________________________________\n\n"); @@ -1401,10 +1429,14 @@ static void list_record(struct ufile *file) struct data *d = list_entry(item, struct data, list); if (!d->name) { - if (d->resident) flagr = 'R'; - else flagr = 'N'; - if (d->compressed) flagc = 'C'; /* These two are mutually exclusive */ - if (d->encrypted) flagc = 'E'; + if (d->resident) + flagr = 'R'; + else + flagr = 'N'; + if (d->compressed) + flagc = 'C'; + if (d->encrypted) + flagc = 'E'; percent = max(percent, d->percent); } @@ -2122,7 +2154,8 @@ int main(int argc, char *argv[]) utils_set_locale(); - vol = utils_mount_volume(opts.device, MS_RDONLY, opts.force); + vol = utils_mount_volume(opts.device, MS_RDONLY | + (opts.force ? MS_RECOVER : 0)); if (!vol) return 1; diff --git a/ntfsprogs/ntfsundelete.h b/ntfsprogs/ntfsundelete.h index f4b0271a..15a22dd9 100644 --- a/ntfsprogs/ntfsundelete.h +++ b/ntfsprogs/ntfsundelete.h @@ -2,6 +2,7 @@ * ntfsundelete - Part of the Linux-NTFS project. * * Copyright (c) 2002 Richard Russon + * Copyright (c) 2007 Yura Pakhuchiy * * This utility will recover deleted files from an NTFS volume. * @@ -27,6 +28,7 @@ #include "types.h" #include "list.h" #include "runlist.h" +#include "utils.h" enum optmode { MODE_NONE = 0, @@ -57,7 +59,6 @@ struct options { s64 mft_begin; /* Range for mft copy */ s64 mft_end; char fillbyte; /* Use for unrecoverable sections */ - char padding[7]; /* Unused: padding to 64 bit. */ }; struct filename { @@ -73,9 +74,8 @@ struct filename { time_t date_r; /* read */ char *name; /* Filename in current locale */ FILE_NAME_TYPE_FLAGS name_space; - long long parent_mref; + leMFT_REF parent_mref; char *parent_name; - char padding[7]; /* Unused: padding to 64 bit. */ }; struct data { @@ -93,7 +93,6 @@ struct data { runlist_element *runlist; /* Decoded data runs */ int percent; /* Amount potentially recoverable */ void *data; /* If resident, a pointer to the data */ - char padding[4]; /* Unused: padding to 64 bit. */ }; struct ufile { @@ -107,7 +106,6 @@ struct ufile { int attr_list; /* MFT record may be one of many */ int directory; /* MFT record represents a directory */ MFT_RECORD *mft; /* Raw MFT record */ - char padding[4]; /* Unused: padding to 64 bit. */ }; #endif /* _NTFSUNDELETE_H_ */ diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c index dba61d02..40ec1006 100644 --- a/ntfsprogs/ntfswipe.c +++ b/ntfsprogs/ntfswipe.c @@ -1340,12 +1340,14 @@ int main(int argc, char *argv[]) if (opts.info || opts.noaction) flags = MS_RDONLY; + if (opts.force) + flags |= MS_RECOVER; - vol = utils_mount_volume(opts.device, flags, opts.force); + vol = utils_mount_volume(opts.device, flags); if (!vol) goto free; - if ((vol->flags & VOLUME_IS_DIRTY) && (!opts.force)) + if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force) goto umount; if (opts.info) { diff --git a/ntfsprogs/sd.c b/ntfsprogs/sd.c index 0e4ce809..bfc7341d 100644 --- a/ntfsprogs/sd.c +++ b/ntfsprogs/sd.c @@ -3,25 +3,16 @@ #include "sd.h" /** - * init_system_file_sd + * init_system_file_sd - * - * NTFS 1.2, 3.0, 3.1 - System files security decriptors + * NTFS 3.1 - System files security decriptors * ===================================================== * * Create the security descriptor for system file number @sys_file_no and * return a pointer to the descriptor. * - * $MFT, $MFTMirr, $LogFile, $AttrDef, $Bitmap, $Boot, $BadClus, and $UpCase - * are the same. - * - * $Volume, $Quota, and system files 0xb-0xf are the same. They are almost the - * same as the above, the only difference being that the two SIDs present in - * the DACL grant GENERIC_WRITE and GENERIC_READ equivalent privileges while - * the above only grant GENERIC_READ equivalent privileges. (For some reason - * the flags for GENERIC_READ/GENERIC_WRITE are not set by NT4, even though - * the permissions are equivalent, so we comply. - * - * Root directory system file (".") is different altogether. + * Note the root directory system file (".") is very different and handled by a + * different function. * * The sd is returned in *@sd_val and has length *@sd_val_len. * @@ -46,15 +37,9 @@ void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) sd->revision = 1; sd->alignment = 0; sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; - if (sys_file_no == FILE_root) { - *sd_val_len = 0x50; - sd->owner = const_cpu_to_le32(0x30); - sd->group = const_cpu_to_le32(0x40); - } else { - *sd_val_len = 0x68; - sd->owner = const_cpu_to_le32(0x48); - sd->group = const_cpu_to_le32(0x58); - } + *sd_val_len = 0x64; + sd->owner = const_cpu_to_le32(0x48); + sd->group = const_cpu_to_le32(0x54); sd->sacl = const_cpu_to_le32(0); sd->dacl = const_cpu_to_le32(0x14); /* @@ -64,13 +49,8 @@ void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) acl = (ACL*)((char*)sd + le32_to_cpu(sd->dacl)); acl->revision = 2; acl->alignment1 = 0; - if (sys_file_no == FILE_root) { - acl->size = const_cpu_to_le16(0x1c); - acl->ace_count = const_cpu_to_le16(1); - } else { - acl->size = const_cpu_to_le16(0x34); - acl->ace_count = const_cpu_to_le16(2); - } + acl->size = const_cpu_to_le16(0x34); + acl->ace_count = const_cpu_to_le16(2); acl->alignment2 = const_cpu_to_le16(0); /* * Now at offset 0x1c, just after the DACL's ACL, we have the first @@ -78,31 +58,20 @@ void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) */ aa_ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; - if (sys_file_no == FILE_root) - aa_ace->flags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; - else - aa_ace->flags = 0; + aa_ace->flags = 0; aa_ace->size = const_cpu_to_le16(0x14); switch (sys_file_no) { - case FILE_MFT: case FILE_MFTMirr: case FILE_LogFile: - case FILE_AttrDef: case FILE_Bitmap: case FILE_Boot: - case FILE_BadClus: case FILE_UpCase: + case FILE_AttrDef: + case FILE_Boot: aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA; break; - case FILE_Volume: case FILE_Secure: case 0xb ... 0xffff: + default: aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_WRITE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | FILE_WRITE_DATA | FILE_READ_DATA; break; - case FILE_root: - aa_ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | - FILE_READ_ATTRIBUTES | FILE_DELETE_CHILD | - FILE_TRAVERSE | FILE_WRITE_EA | FILE_READ_EA | - FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | - FILE_LIST_DIRECTORY; - break; } aa_ace->sid.revision = 1; aa_ace->sid.sub_authority_count = 1; @@ -111,67 +80,57 @@ void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) aa_ace->sid.identifier_authority.value[2] = 0; aa_ace->sid.identifier_authority.value[3] = 0; aa_ace->sid.identifier_authority.value[4] = 0; - if (sys_file_no == FILE_root) { - /* SECURITY_WORLD_SID_AUTHORITY (S-1-1) */ - aa_ace->sid.identifier_authority.value[5] = 1; - aa_ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_WORLD_RID); - /* This is S-1-1-0, the WORLD_SID. */ - } else { - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - aa_ace->sid.identifier_authority.value[5] = 5; - aa_ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - } + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[5] = 5; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); /* * Now at offset 0x30 within security descriptor, just after the first * ACE of the DACL. All system files, except the root directory, have * a second ACE. */ - if (sys_file_no != FILE_root) { - /* The second ACE of the DACL. Type is access allowed. */ - aa_ace = (ACCESS_ALLOWED_ACE*)((char*)aa_ace + - le16_to_cpu(aa_ace->size)); - aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; - aa_ace->flags = 0; - aa_ace->size = const_cpu_to_le16(0x18); - switch (sys_file_no) { - case FILE_MFT: case FILE_MFTMirr: - case FILE_LogFile: case FILE_AttrDef: - case FILE_Bitmap: case FILE_Boot: - case FILE_BadClus: case FILE_UpCase: - aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | - FILE_READ_ATTRIBUTES | FILE_READ_EA | - FILE_READ_DATA; - break; - case FILE_Volume: case FILE_Secure: - case 0xb ... 0xffff : - aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | - FILE_WRITE_ATTRIBUTES | - FILE_READ_ATTRIBUTES | FILE_WRITE_EA | - FILE_READ_EA | FILE_APPEND_DATA | - FILE_WRITE_DATA | FILE_READ_DATA; - break; - } - aa_ace->sid.revision = 1; - aa_ace->sid.sub_authority_count = 2; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - aa_ace->sid.identifier_authority.value[0] = 0; - aa_ace->sid.identifier_authority.value[1] = 0; - aa_ace->sid.identifier_authority.value[2] = 0; - aa_ace->sid.identifier_authority.value[3] = 0; - aa_ace->sid.identifier_authority.value[4] = 0; - aa_ace->sid.identifier_authority.value[5] = 5; - aa_ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - aa_ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - /* Now at offset 0x48 into the security descriptor. */ + /* The second ACE of the DACL. Type is access allowed. */ + aa_ace = (ACCESS_ALLOWED_ACE*)((char*)aa_ace + + le16_to_cpu(aa_ace->size)); + aa_ace->type = ACCESS_ALLOWED_ACE_TYPE; + aa_ace->flags = 0; + aa_ace->size = const_cpu_to_le16(0x18); + /* Only $AttrDef and $Boot behave differently to everything else. */ + switch (sys_file_no) { + case FILE_AttrDef: + case FILE_Boot: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_READ_ATTRIBUTES | FILE_READ_EA | + FILE_READ_DATA; + break; + default: + aa_ace->mask = SYNCHRONIZE | STANDARD_RIGHTS_READ | + FILE_WRITE_ATTRIBUTES | + FILE_READ_ATTRIBUTES | FILE_WRITE_EA | + FILE_READ_EA | FILE_APPEND_DATA | + FILE_WRITE_DATA | FILE_READ_DATA; + break; } - /* As specified in the security descriptor, we now have the owner SID.*/ + aa_ace->sid.revision = 1; + aa_ace->sid.sub_authority_count = 2; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + aa_ace->sid.identifier_authority.value[0] = 0; + aa_ace->sid.identifier_authority.value[1] = 0; + aa_ace->sid.identifier_authority.value[2] = 0; + aa_ace->sid.identifier_authority.value[3] = 0; + aa_ace->sid.identifier_authority.value[4] = 0; + aa_ace->sid.identifier_authority.value[5] = 5; + aa_ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + aa_ace->sid.sub_authority[1] = + const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + /* + * Now at offset 0x48 into the security descriptor, as specified in the + * security descriptor, we now have the owner SID. + */ sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); sid->revision = 1; - sid->sub_authority_count = 2; + sid->sub_authority_count = 1; /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ sid->identifier_authority.value[0] = 0; sid->identifier_authority.value[1] = 0; @@ -179,12 +138,10 @@ void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) sid->identifier_authority.value[3] = 0; sid->identifier_authority.value[4] = 0; sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); /* - * Now at offset 0x40 or 0x58 (root directory and the other system - * files, respectively) into the security descriptor, as specified in - * the security descriptor, we have the group SID. + * Now at offset 0x54 into the security descriptor, as specified in the + * security descriptor, we have the group SID. */ sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); sid->revision = 1; @@ -201,56 +158,53 @@ void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len) } /** - * init_root_sd_31 (ERSO) - * creates the security_descriptor for the root folder on ntfs 3.1. - * It is very long; lots of ACE's at first, then large pieces of zeroes; - * the owner user/group is near the end. On a partition created with - * w2k3 the owner user/group at the end is surrounded by 'garbage', which I - * yet do not understand. Here I have replaced the 'garbage' with - * zeros, which seems to work. Chkdsk does not add the 'garbage', nor alter - * this security descriptor in any way. + * init_root_sd - + * + * Creates the security_descriptor for the root folder on ntfs 3.1 as created + * by Windows Vista (when the format is done from the disk management MMC + * snap-in, note this is different from the format done from the disk + * properties in Windows Explorer). */ -void init_root_sd_31(u8 **sd_val, int *sd_val_len) +void init_root_sd(u8 **sd_val, int *sd_val_len) { SECURITY_DESCRIPTOR_RELATIVE *sd; ACL *acl; ACCESS_ALLOWED_ACE *ace; SID *sid; - static char sd_array[0x1030]; - *sd_val_len = 0x1030; + static char sd_array[0x102c]; + *sd_val_len = 0x102c; *sd_val = (u8*)&sd_array; //security descriptor relative sd = (SECURITY_DESCRIPTOR_RELATIVE*)sd_array; - sd->revision = 0x01; - sd->alignment = 0x00; + sd->revision = SECURITY_DESCRIPTOR_REVISION; + sd->alignment = 0; sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; sd->owner = const_cpu_to_le32(0x1014); - sd->group = const_cpu_to_le32(0x1024); - sd->sacl = const_cpu_to_le32(0x00); - sd->dacl = const_cpu_to_le32(0x14); + sd->group = const_cpu_to_le32(0x1020); + sd->sacl = 0; + sd->dacl = const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)); //acl acl = (ACL*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); - acl->revision = 0x02; - acl->alignment1 = 0x00; + acl->revision = ACL_REVISION; + acl->alignment1 = 0; acl->size = const_cpu_to_le16(0x1000); - acl->ace_count = const_cpu_to_le16(0x07); - acl->alignment2 = const_cpu_to_le16(0x00); + acl->ace_count = const_cpu_to_le16(0x08); + acl->alignment2 = 0; //ace1 ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); - ace->type = 0x00; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; ace->size = const_cpu_to_le16(0x18); ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | FILE_LIST_DIRECTORY | FILE_WRITE_DATA | FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES; - - ace->sid.revision = 0x01; + ace->sid.revision = SID_REVISION; ace->sid.sub_authority_count = 0x02; /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; @@ -261,21 +215,17 @@ void init_root_sd_31(u8 **sd_val, int *sd_val_len) ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + ace->sid.sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); //ace2 ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - ace->size = const_cpu_to_le16(0x14); - ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | - FILE_LIST_DIRECTORY | FILE_WRITE_DATA | - FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | - FILE_TRAVERSE | FILE_DELETE_CHILD | - FILE_READ_ATTRIBUTES; - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x01; + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = GENERIC_ALL; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; ace->sid.identifier_authority.value[1] = 0; @@ -284,35 +234,40 @@ void init_root_sd_31(u8 **sd_val, int *sd_val_len) ace->sid.identifier_authority.value[4] = 0; ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); //ace3 ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | - INHERIT_ONLY_ACE; + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; ace->size = const_cpu_to_le16(0x14); - ace->mask = const_cpu_to_le32(0x10000000); - ace->sid.revision = 0x01; + ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | FILE_WRITE_DATA | + FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | + FILE_TRAVERSE | FILE_DELETE_CHILD | + FILE_READ_ATTRIBUTES; + ace->sid.revision = SID_REVISION; ace->sid.sub_authority_count = 0x01; - /* SECURITY_CREATOR_SID_AUTHORITY (S-1-3) */ + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; ace->sid.identifier_authority.value[1] = 0; ace->sid.identifier_authority.value[2] = 0; ace->sid.identifier_authority.value[3] = 0; ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 3; + ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_CREATOR_OWNER_RID); + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); //ace4 ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - ace->size = const_cpu_to_le16(0x18); - ace->mask = const_cpu_to_le32(0x1200A9); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x02; + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = GENERIC_ALL; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; ace->sid.identifier_authority.value[1] = 0; @@ -321,18 +276,20 @@ void init_root_sd_31(u8 **sd_val, int *sd_val_len) ace->sid.identifier_authority.value[4] = 0; ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); //ace5 ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = CONTAINER_INHERIT_ACE; - ace->size = const_cpu_to_le16(0x18); - ace->mask = const_cpu_to_le32(0x04); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x02; + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x14); + ace->mask = SYNCHRONIZE | READ_CONTROL | DELETE | + FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | + FILE_TRAVERSE | FILE_WRITE_EA | FILE_READ_EA | + FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE | + FILE_LIST_DIRECTORY; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; ace->sid.identifier_authority.value[1] = 0; @@ -341,18 +298,17 @@ void init_root_sd_31(u8 **sd_val, int *sd_val_len) ace->sid.identifier_authority.value[4] = 0; ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + const_cpu_to_le32(SECURITY_AUTHENTICATED_USER_RID); //ace6 ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; - ace->size = const_cpu_to_le16(0x18); - ace->mask = const_cpu_to_le32(0x02); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x02; + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x14); + ace->mask = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x01; /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; ace->sid.identifier_authority.value[1] = 0; @@ -361,43 +317,60 @@ void init_root_sd_31(u8 **sd_val, int *sd_val_len) ace->sid.identifier_authority.value[4] = 0; ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + const_cpu_to_le32(SECURITY_AUTHENTICATED_USER_RID); //ace7 ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x14); - ace->mask = const_cpu_to_le32(0x1200A9); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x01; - /* SECURITY_WORLD_SID_AUTHORITY (S-1-1) */ + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = 0; + ace->size = const_cpu_to_le16(0x18); + ace->mask = SYNCHRONIZE | READ_CONTROL | FILE_READ_ATTRIBUTES | + FILE_TRAVERSE | FILE_READ_EA | FILE_LIST_DIRECTORY; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ ace->sid.identifier_authority.value[0] = 0; ace->sid.identifier_authority.value[1] = 0; ace->sid.identifier_authority.value[2] = 0; ace->sid.identifier_authority.value[3] = 0; ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 1; + ace->sid.identifier_authority.value[5] = 5; ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_WORLD_RID); - - //owner sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); + ace->sid.sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //ace8 + ace = (ACCESS_ALLOWED_ACE*)((u8*)ace + le16_to_cpu(ace->size)); + ace->type = ACCESS_ALLOWED_ACE_TYPE; + ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | + INHERIT_ONLY_ACE; + ace->size = const_cpu_to_le16(0x18); + ace->mask = GENERIC_READ | GENERIC_EXECUTE; + ace->sid.revision = SID_REVISION; + ace->sid.sub_authority_count = 0x02; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + ace->sid.identifier_authority.value[0] = 0; + ace->sid.identifier_authority.value[1] = 0; + ace->sid.identifier_authority.value[2] = 0; + ace->sid.identifier_authority.value[3] = 0; + ace->sid.identifier_authority.value[4] = 0; + ace->sid.identifier_authority.value[5] = 5; + ace->sid.sub_authority[0] = + const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); + ace->sid.sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_USERS); + + //owner sid + sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); + sid->revision = 0x01; + sid->sub_authority_count = 0x01; + /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ + sid->identifier_authority.value[0] = 0; + sid->identifier_authority.value[1] = 0; + sid->identifier_authority.value[2] = 0; + sid->identifier_authority.value[3] = 0; + sid->identifier_authority.value[4] = 0; + sid->identifier_authority.value[5] = 5; + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); //group sid sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); @@ -410,419 +383,18 @@ void init_root_sd_31(u8 **sd_val, int *sd_val_len) sid->identifier_authority.value[3] = 0; sid->identifier_authority.value[4] = 0; sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); + sid->sub_authority[0] = const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); } /** - * init_secure - * - * NTFS 3.0 - System files security decriptors - * =========================================== - * Create the security descriptor entries in $SDS data stream like they - * are in a partition, newly formatted with windows 2000 - */ -void init_secure_30(char *sd_val) -{ - SECURITY_DESCRIPTOR_HEADER *sds; - SECURITY_DESCRIPTOR_RELATIVE *sd; - ACL *acl; - ACCESS_ALLOWED_ACE *ace; - SID *sid; - -/* - * security descriptor #1 - */ - //header - sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val); - sds->hash = const_cpu_to_le32(0xF80312F0); - sds->security_id = const_cpu_to_le32(0x0100); - sds->offset = const_cpu_to_le64(0x00); - sds->length = const_cpu_to_le32(0x7C); - //security descriptor relative - sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + - sizeof(SECURITY_DESCRIPTOR_HEADER)); - sd->revision = 0x01; - sd->alignment = 0x00; - sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; - sd->owner = const_cpu_to_le32(0x48); - sd->group = const_cpu_to_le32(0x58); - sd->sacl = const_cpu_to_le32(0x00); - sd->dacl = const_cpu_to_le32(0x14); - - //acl - acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); - acl->revision = 0x02; - acl->alignment1 = 0x00; - acl->size = const_cpu_to_le16(0x34); - acl->ace_count = const_cpu_to_le16(0x02); - acl->alignment2 = 0x00; - - //ace1 - ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x14); - ace->mask = const_cpu_to_le32(0x120089); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x01; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - - //ace2 - ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x18); - ace->mask = const_cpu_to_le32(0x120089); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - - //owner sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - - //group sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - -/* - * security descriptor #2 - */ - //header - sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80); - sds->hash = const_cpu_to_le32(0xB32451); - sds->security_id = const_cpu_to_le32(0x0101); - sds->offset = const_cpu_to_le64(0x80); - sds->length = const_cpu_to_le32(0x7C); - - //security descriptor relative - sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + - sizeof(SECURITY_DESCRIPTOR_HEADER)); - sd->revision = 0x01; - sd->alignment = 0x00; - sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; - sd->owner = const_cpu_to_le32(0x48); - sd->group = const_cpu_to_le32(0x58); - sd->sacl = const_cpu_to_le32(0x00); - sd->dacl = const_cpu_to_le32(0x14); - - //acl - acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); - acl->revision = 0x02; - acl->alignment1 = 0x00; - acl->size = const_cpu_to_le16(0x34); - acl->ace_count = const_cpu_to_le16(0x02); - acl->alignment2 = 0x00; - - //ace1 - ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x14); - ace->mask = const_cpu_to_le32(0x12019F); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x01; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - - //ace2 - ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x18); - ace->mask = const_cpu_to_le32(0x12019F); - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - - //owner sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - - //group sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - -/* - * security descriptor #3 - */ - //header - sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80 + 0x80); - sds->hash = const_cpu_to_le32(0x0A9F9562); - sds->security_id = const_cpu_to_le32(0x0102); - sds->offset = const_cpu_to_le64(0x0100); - sds->length = const_cpu_to_le32(0x60); - - //security descriptor relative - sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + - sizeof(SECURITY_DESCRIPTOR_HEADER)); - sd->revision = 0x01; - sd->alignment = 0x00; - sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; - sd->owner = const_cpu_to_le32(0x30); - sd->group = const_cpu_to_le32(0x40); - sd->sacl = const_cpu_to_le32(0x00); - sd->dacl = const_cpu_to_le32(0x14); - - //acl - acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); - acl->revision = 0x02; - acl->alignment1 = 0x00; - acl->size = const_cpu_to_le16(0x1C); - acl->ace_count = const_cpu_to_le16(0x01); - acl->alignment2 = 0x00; - - //ace1 - ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x14); - ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | - FILE_LIST_DIRECTORY | FILE_WRITE_DATA | - FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | - FILE_TRAVERSE | FILE_DELETE_CHILD | - FILE_READ_ATTRIBUTES; - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x01; - // SECURITY_NT_SID_AUTHORITY (S-1-5) - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - - //owner sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - // SECURITY_NT_SID_AUTHORITY (S-1-5) - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - //group sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); - sid->revision = 0x01; - sid->sub_authority_count = 0x01; - // SECURITY_NT_SID_AUTHORITY (S-1-5) - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - -/* - * security descriptor #4 - */ - //header - sds = (SECURITY_DESCRIPTOR_HEADER*)((char*)sd_val + 0x80 + 0x80 + 0x60); - sds->hash = const_cpu_to_le32(0x453F0A2E); - sds->security_id = const_cpu_to_le32(0x0103); - sds->offset = const_cpu_to_le64(0x0160); - sds->length = const_cpu_to_le32(0x78); - - //security descriptor relative - sd = (SECURITY_DESCRIPTOR_RELATIVE*)((char*)sds + - sizeof(SECURITY_DESCRIPTOR_HEADER)); - sd->revision = 0x01; - sd->alignment = 0x00; - sd->control = SE_SELF_RELATIVE | SE_DACL_PRESENT; - sd->owner = const_cpu_to_le32(0x48); - sd->group = const_cpu_to_le32(0x58); - sd->sacl = const_cpu_to_le32(0x00); - sd->dacl = const_cpu_to_le32(0x14); - - //acl - acl = (ACL*)((char*)sd + sizeof(SECURITY_DESCRIPTOR_RELATIVE)); - acl->revision = 0x02; - acl->alignment1 = 0x00; - acl->size = const_cpu_to_le16(0x34); - acl->ace_count = const_cpu_to_le16(0x02); - acl->alignment2 = 0x00; - - //ace1 - ace = (ACCESS_ALLOWED_ACE*)((char*)acl + sizeof(ACL)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x18); - ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | - FILE_LIST_DIRECTORY | FILE_WRITE_DATA | - FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | - FILE_TRAVERSE | FILE_DELETE_CHILD | - FILE_READ_ATTRIBUTES; - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x02; - // SECURITY_NT_SID_AUTHORITY (S-1-5) - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - ace->sid.sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - //ace2 - ace = (ACCESS_ALLOWED_ACE*)((char*)ace + le16_to_cpu(ace->size)); - ace->type = 0x00; - ace->flags = 0x00; - ace->size = const_cpu_to_le16(0x14); - ace->mask = STANDARD_RIGHTS_ALL | FILE_WRITE_ATTRIBUTES | - FILE_LIST_DIRECTORY | FILE_WRITE_DATA | - FILE_ADD_SUBDIRECTORY | FILE_READ_EA | FILE_WRITE_EA | - FILE_TRAVERSE | FILE_DELETE_CHILD | - FILE_READ_ATTRIBUTES; - ace->sid.revision = 0x01; - ace->sid.sub_authority_count = 0x01; - /* SECURITY_NT_SID_AUTHORITY (S-1-5) */ - ace->sid.identifier_authority.value[0] = 0; - ace->sid.identifier_authority.value[1] = 0; - ace->sid.identifier_authority.value[2] = 0; - ace->sid.identifier_authority.value[3] = 0; - ace->sid.identifier_authority.value[4] = 0; - ace->sid.identifier_authority.value[5] = 5; - ace->sid.sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - - //owner sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->owner)); - sid->revision = 0x01; - sid->sub_authority_count = 0x02; - // SECURITY_NT_SID_AUTHORITY (S-1-5) - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = - const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - - //group sid - sid = (SID*)((char*)sd + le32_to_cpu(sd->group)); - sid->revision = 0x01; - sid->sub_authority_count = 0x01; - // SECURITY_NT_SID_AUTHORITY (S-1-5) - sid->identifier_authority.value[0] = 0; - sid->identifier_authority.value[1] = 0; - sid->identifier_authority.value[2] = 0; - sid->identifier_authority.value[3] = 0; - sid->identifier_authority.value[4] = 0; - sid->identifier_authority.value[5] = 5; - sid->sub_authority[0] = - const_cpu_to_le32(SECURITY_LOCAL_SYSTEM_RID); - - return; -} - -/** - * init_secure_31(char **r, int size); + * init_secure_sds - * * NTFS 3.1 - System files security decriptors * =========================================== * Create the security descriptor entries in $SDS data stream like they * are in a partition, newly formatted with windows 2003 */ -void init_secure_31(char *sd_val) +void init_secure_sds(char *sd_val) { SECURITY_DESCRIPTOR_HEADER *sds; SECURITY_DESCRIPTOR_RELATIVE *sd; diff --git a/ntfsprogs/sd.h b/ntfsprogs/sd.h index aac6b111..7ad3e6a7 100644 --- a/ntfsprogs/sd.h +++ b/ntfsprogs/sd.h @@ -4,9 +4,8 @@ #include "types.h" void init_system_file_sd(int sys_file_no, u8 **sd_val, int *sd_val_len); -void init_root_sd_31(u8 **sd_val, int *sd_val_len); -void init_secure_30(char *sd_val); -void init_secure_31(char *sd_val); +void init_root_sd(u8 **sd_val, int *sd_val_len); +void init_secure_sds(char *sd_val); #endif /* _NTFS_SD_H_ */ diff --git a/ntfsprogs/upcase.c b/ntfsprogs/upcase.c deleted file mode 100644 index 113686cd..00000000 --- a/ntfsprogs/upcase.c +++ /dev/null @@ -1,90 +0,0 @@ -/** - * upcase - Part of the Linux-NTFS project. - * - * Copyright (c) 2001 Richard Russon - * Copyright (c) 2001-2006 Anton Altaparmakov - * - * 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 - */ - -#include "config.h" - -#ifdef HAVE_STRING_H -#include -#endif - -#include "endians.h" -#include "types.h" -#include "upcase.h" - -/** - * init_upcase_table - */ -void init_upcase_table(ntfschar *uc, u32 uc_len) -{ - static int uc_run_table[][3] = { /* Start, End, Add */ - {0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74}, - {0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86}, - {0x00F8, 0x00FF, -32}, {0x0561, 0x0587, -48}, {0x1F76, 0x1F78, 100}, - {0x0256, 0x0258, -205}, {0x1F00, 0x1F08, 8}, {0x1F78, 0x1F7A, 128}, - {0x028A, 0x028C, -217}, {0x1F10, 0x1F16, 8}, {0x1F7A, 0x1F7C, 112}, - {0x03AC, 0x03AD, -38}, {0x1F20, 0x1F28, 8}, {0x1F7C, 0x1F7E, 126}, - {0x03AD, 0x03B0, -37}, {0x1F30, 0x1F38, 8}, {0x1FB0, 0x1FB2, 8}, - {0x03B1, 0x03C2, -32}, {0x1F40, 0x1F46, 8}, {0x1FD0, 0x1FD2, 8}, - {0x03C2, 0x03C3, -31}, {0x1F51, 0x1F52, 8}, {0x1FE0, 0x1FE2, 8}, - {0x03C3, 0x03CC, -32}, {0x1F53, 0x1F54, 8}, {0x1FE5, 0x1FE6, 7}, - {0x03CC, 0x03CD, -64}, {0x1F55, 0x1F56, 8}, {0x2170, 0x2180, -16}, - {0x03CD, 0x03CF, -63}, {0x1F57, 0x1F58, 8}, {0x24D0, 0x24EA, -26}, - {0x0430, 0x0450, -32}, {0x1F60, 0x1F68, 8}, {0xFF41, 0xFF5B, -32}, - {0} - }; - static int uc_dup_table[][2] = { /* Start, End */ - {0x0100, 0x012F}, {0x01A0, 0x01A6}, {0x03E2, 0x03EF}, {0x04CB, 0x04CC}, - {0x0132, 0x0137}, {0x01B3, 0x01B7}, {0x0460, 0x0481}, {0x04D0, 0x04EB}, - {0x0139, 0x0149}, {0x01CD, 0x01DD}, {0x0490, 0x04BF}, {0x04EE, 0x04F5}, - {0x014A, 0x0178}, {0x01DE, 0x01EF}, {0x04BF, 0x04BF}, {0x04F8, 0x04F9}, - {0x0179, 0x017E}, {0x01F4, 0x01F5}, {0x04C1, 0x04C4}, {0x1E00, 0x1E95}, - {0x018B, 0x018B}, {0x01FA, 0x0218}, {0x04C7, 0x04C8}, {0x1EA0, 0x1EF9}, - {0} - }; - static int uc_byte_table[][2] = { /* Offset, Value */ - {0x00FF, 0x0178}, {0x01AD, 0x01AC}, {0x01F3, 0x01F1}, {0x0269, 0x0196}, - {0x0183, 0x0182}, {0x01B0, 0x01AF}, {0x0253, 0x0181}, {0x026F, 0x019C}, - {0x0185, 0x0184}, {0x01B9, 0x01B8}, {0x0254, 0x0186}, {0x0272, 0x019D}, - {0x0188, 0x0187}, {0x01BD, 0x01BC}, {0x0259, 0x018F}, {0x0275, 0x019F}, - {0x018C, 0x018B}, {0x01C6, 0x01C4}, {0x025B, 0x0190}, {0x0283, 0x01A9}, - {0x0192, 0x0191}, {0x01C9, 0x01C7}, {0x0260, 0x0193}, {0x0288, 0x01AE}, - {0x0199, 0x0198}, {0x01CC, 0x01CA}, {0x0263, 0x0194}, {0x0292, 0x01B7}, - {0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197}, - {0} - }; - int i, r; - - memset(uc, 0, uc_len); - uc_len >>= 1; - /* Generate the little endian Unicode upcase table used by ntfs. */ - for (i = 0; (u32)i < uc_len; i++) - uc[i] = cpu_to_le16(i); - for (r = 0; uc_run_table[r][0]; r++) - for (i = uc_run_table[r][0]; i < uc_run_table[r][1]; i++) - uc[i] = cpu_to_le16(le16_to_cpu(uc[i]) + - uc_run_table[r][2]); - for (r = 0; uc_dup_table[r][0]; r++) - for (i = uc_dup_table[r][0]; i < uc_dup_table[r][1]; i += 2) - uc[i + 1] = cpu_to_le16(le16_to_cpu(uc[i + 1]) - 1); - for (r = 0; uc_byte_table[r][0]; r++) - uc[uc_byte_table[r][0]] = cpu_to_le16(uc_byte_table[r][1]); -} diff --git a/ntfsprogs/upcase.h b/ntfsprogs/upcase.h deleted file mode 100644 index 9ec567dc..00000000 --- a/ntfsprogs/upcase.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _NTFS_UPCASE_H_ -#define _NTFS_UPCASE_H_ - -void init_upcase_table(ntfschar *uc, u32 uc_len); - -#endif /* _NTFS_UPCASE_H_ */ - diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c index 65ae8eca..f08f5062 100644 --- a/ntfsprogs/utils.c +++ b/ntfsprogs/utils.c @@ -4,6 +4,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2003 Lode Leroy + * Copyright (c) 2005-2007 Yura Pakhuchiy * * A set of shared functions for ntfs utilities * @@ -71,6 +72,7 @@ #include "dir.h" /* #include "version.h" */ #include "logging.h" +#include "misc.h" const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; const char *ntfs_home = "Linux NTFS homepage: http://www.linux-ntfs.org\n"; @@ -81,6 +83,50 @@ const char *ntfs_gpl = "This program is free software, released under the GNU " "\"COPYING\" distributed with this program, or online at:\n" "http://www.gnu.org/copyleft/gpl.html\n"; +static const char *invalid_ntfs_msg = +"The device '%s' doesn't have a valid NTFS.\n" +"Maybe you selected the wrong device? Or the whole disk instead of a\n" +"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n"; + +static const char *corrupt_volume_msg = +"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" +"The usage of the /f parameter is very IMPORTANT! No modification was\n" +"made to NTFS by this software.\n"; + +static const char *hibernated_volume_msg = +"The NTFS partition is hibernated. Please resume Windows and turned it \n" +"off properly, so mounting could be done safely.\n"; + +static const char *unclean_journal_msg = +"Access is denied because the NTFS journal file is unclean. Choices are:\n" +" A) Shutdown Windows properly.\n" +" B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n" +" notification area before disconnecting the device.\n" +" C) Use 'Eject' from Windows Explorer to safely remove the device.\n" +" D) If you ran chkdsk previously then boot Windows again which will\n" +" automatically initialize the journal.\n" +" E) Submit 'force' option (WARNING: This solution it not recommended).\n" +" F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n"; + +static const char *opened_volume_msg = +"Access is denied because the NTFS volume is already exclusively opened.\n" +"The volume may be already mounted, or another software may use it which\n" +"could be identified for example by the help of the 'fuser' command.\n"; + +static const char *dirty_volume_msg = +"Volume is scheduled for check.\n" +"Please boot into Windows TWICE, or use the 'force' option.\n" +"NOTE: If you had not scheduled check and last time accessed this volume\n" +"using ntfsmount and shutdown system properly, then init scripts in your\n" +"distribution are broken. Please report to your distribution developers\n" +"(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n" +"shutdown instead of proper umount.\n"; + +static const char *fakeraid_msg = +"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n" +"different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n" +"to mount NTFS. Please see the 'dmraid' documentation for help.\n"; + /** * utils_set_locale */ @@ -91,16 +137,16 @@ int utils_set_locale(void) locale = setlocale(LC_ALL, ""); if (!locale) { locale = setlocale(LC_ALL, NULL); - ntfs_log_error("Failed to set locale, using default '%s'.\n", locale); + ntfs_log_error("Failed to set locale, using default '%s'.\n", + locale); return 1; } else { return 0; } } -#ifndef NTFS_RICH /** - * utils_valid_device - Perform some safety checks on the device, before we start + * utils_valid_device - Perform some safety checks on the device, before start * @name: Full pathname of the device/file to work with * @force: Continue regardless of problems * @@ -116,7 +162,7 @@ int utils_valid_device(const char *name, int force) struct stat st; #ifdef __CYGWIN32__ - /* FIXME: This doesn't work for Cygwin, so just return success for now... */ + /* FIXME: This doesn't work for Cygwin, so just return success. */ return 1; #endif if (!name) { @@ -125,37 +171,30 @@ int utils_valid_device(const char *name, int force) } if (stat(name, &st) == -1) { - if (errno == ENOENT) { + if (errno == ENOENT) ntfs_log_error("The device %s doesn't exist\n", name); - } else { - ntfs_log_perror("Error getting information about %s", name); - } + else + ntfs_log_perror("Error getting information about %s", + name); return 0; } - if (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) { - ntfs_log_warning("%s is not a block device, " - "nor regular file.\n", name); - if (!force) { - ntfs_log_error("Use the force option to work with other" - " file types, for your own risk!\n"); - return 0; - } - ntfs_log_warning("Forced to continue.\n"); - } - /* Make sure the file system is not mounted. */ if (ntfs_check_if_mounted(name, &mnt_flags)) { - ntfs_log_perror("Failed to determine whether %s is mounted", name); + ntfs_log_perror("Failed to determine whether %s is mounted", + name); if (!force) { - ntfs_log_error("Use the force option to ignore this error.\n"); + ntfs_log_error("Use the force option to ignore this " + "error.\n"); return 0; } ntfs_log_warning("Forced to continue.\n"); } else if (mnt_flags & NTFS_MF_MOUNTED) { - ntfs_log_warning("The device %s, is mounted.\n", name); if (!force) { - ntfs_log_error("Use the force option to work a mounted filesystem.\n"); + ntfs_log_error("%s", opened_volume_msg); + ntfs_log_error("You can use force option to avoid this " + "check, but this is not recommended\n" + "and may lead to data corruption.\n"); return 0; } ntfs_log_warning("Forced to continue.\n"); @@ -167,7 +206,7 @@ int utils_valid_device(const char *name, int force) /** * utils_mount_volume - Mount an NTFS volume */ -ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force) +ntfs_volume * utils_mount_volume(const char *device, ntfs_mount_flags flags) { ntfs_volume *vol; @@ -176,42 +215,58 @@ ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL f return NULL; } - if (!utils_valid_device(device, force)) + /* Porting notes: + * + * libntfs-3g does not have the 'force' flag in ntfs_mount_flags. + * The 'force' flag in libntfs bypasses two safety checks when mounting + * read/write: + * 1. Do not mount when the VOLUME_IS_DIRTY flag in + * VOLUME_INFORMATION is set. + * 2. Do not mount when the logfile is unclean. + * + * libntfs-3g only has safety check number 2. The dirty flag is simply + * ignored because we are confident that we can handle a dirty volume. + * So we treat MS_RECOVER like NTFS_MNT_FORCE, knowing that the first + * check is always bypassed. + */ + + if (!utils_valid_device(device, flags & MS_RECOVER)) return NULL; vol = ntfs_mount(device, flags); if (!vol) { - int err; - - err = errno; - ntfs_log_perror("Couldn't mount device '%s'", device); - if (err == EPERM) - ntfs_log_error("Windows was hibernated. Try to mount " - "volume in windows, shut down and try " - "again.\n"); - if (err == EOPNOTSUPP) - ntfs_log_error("Windows did not shut down properly. " - "Try to mount volume in windows, " - "shut down and try again.\n"); + ntfs_log_perror("Failed to mount '%s'", device); + if (errno == EINVAL) + ntfs_log_error(invalid_ntfs_msg, device); + else if (errno == EIO) + ntfs_log_error("%s", corrupt_volume_msg); + else if (errno == EPERM) + ntfs_log_error("%s", hibernated_volume_msg); + else if (errno == EOPNOTSUPP) + ntfs_log_error("%s", unclean_journal_msg); + else if (errno == EBUSY) + ntfs_log_error("%s", opened_volume_msg); + else if (errno == ENXIO) + ntfs_log_error("%s", fakeraid_msg); return NULL; } + /* Porting notes: + * libntfs-3g does not record whether the volume log file was dirty + * before mount, so we can only warn if the VOLUME_IS_DIRTY flag is set + * in VOLUME_INFORMATION. */ if (vol->flags & VOLUME_IS_DIRTY) { - ntfs_log_warning("Volume is dirty.\n"); - if (!force) { - ntfs_log_error("Run chkdsk and try again, or use the " - "force option.\n"); + if (!(flags & MS_RECOVER)) { + ntfs_log_error("%s", dirty_volume_msg); ntfs_umount(vol, FALSE); return NULL; } - ntfs_log_warning("Forced to continue.\n"); + ntfs_log_error("WARNING: Dirty volume mount was forced by the " + "'force' mount option.\n"); } - return vol; } -#endif - /** * utils_parse_size - Convert a string representing a size * @value: String to be parsed @@ -333,7 +388,6 @@ int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale) return 1; } -#ifndef NTFS_RICH /** * find_attribute - Find an attribute of the given type * @type: An attribute type, e.g. AT_FILE_NAME @@ -403,7 +457,6 @@ ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) return rec; } -#endif /** * utils_inode_get_name * @@ -414,11 +467,12 @@ ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft) * if parent is 5 (/) stop * get inode of parent */ +#define max_path 20 int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) { // XXX option: names = posix/win32 or dos // flags: path, filename, or both - const int max_path = 20; + ntfs_volume *vol; ntfs_attr_search_ctx *ctx; @@ -426,7 +480,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) FILE_NAME_ATTR *attr; int name_space; MFT_REF parent = FILE_root; - char *names[max_path + 1];// XXX malloc? and make max bigger? + char *names[max_path + 1];// XXX ntfs_malloc? and make max bigger? int i, len, offset = 0; if (!inode || !buffer) { @@ -470,7 +524,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) &names[i], 0) < 0) { char *temp; ntfs_log_error("Couldn't translate filename to current locale.\n"); - temp = malloc(30); + temp = ntfs_malloc(30); if (!temp) return 0; snprintf(temp, 30, "", (unsigned @@ -528,6 +582,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize) return 1; } +#undef max_path /** * utils_attr_get_name @@ -549,7 +604,8 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b name = NULL; namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name)); if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) { - ntfs_log_error("Couldn't translate attribute type to current locale.\n"); + ntfs_log_error("Couldn't translate attribute type to " + "current locale.\n"); // ? return 0; } @@ -573,9 +629,10 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b name = NULL; namelen = attr->name_length; - if (ntfs_ucstombs((ntfschar *)((char *)attr + attr->name_offset), - namelen, &name, 0) < 0) { - ntfs_log_error("Couldn't translate attribute name to current locale.\n"); + if (ntfs_ucstombs((ntfschar *)((char *)attr + le16_to_cpu( + attr->name_offset)), namelen, &name, 0) < 0) { + ntfs_log_error("Couldn't translate attribute name to current " + "locale.\n"); // ? len = snprintf(buffer, bufsize, ""); return 0; @@ -603,7 +660,10 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b * * This function has a static buffer in which it caches a section of $Bitmap. * If the lcn, being tested, lies outside the range, the buffer will be - * refreshed. + * refreshed. @bmplcn stores offset to the first bit (in bits) stored in the + * buffer. + * + * NOTE: Be very carefull with shifts by 3 everywhere in this function. * * Return: 1 Cluster is in use * 0 Cluster is free space @@ -612,8 +672,7 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b int utils_cluster_in_use(ntfs_volume *vol, long long lcn) { static unsigned char buffer[512]; - static long long bmplcn = -sizeof(buffer) - 1; /* Which bit of $Bitmap is in the buffer */ - + static long long bmplcn = -(sizeof(buffer) << 3); int byte, bit; ntfs_attr *attr; @@ -635,7 +694,8 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn) memset(buffer, 0xFF, sizeof(buffer)); bmplcn = lcn & (~((sizeof(buffer) << 3) - 1)); - if (ntfs_attr_pread(attr, (bmplcn>>3), sizeof(buffer), buffer) < 0) { + if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer), + buffer) < 0) { ntfs_log_perror("Couldn't read $Bitmap"); ntfs_attr_close(attr); return -1; @@ -647,7 +707,9 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn) bit = 1 << (lcn & 7); byte = (lcn >> 3) & (sizeof(buffer) - 1); - ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & bit); + ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, " + "in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & + bit); return (buffer[byte] & bit); } @@ -673,15 +735,15 @@ int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref) { static u8 buffer[512]; static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */ - int byte, bit; + ntfs_log_trace("Entering.\n"); + if (!vol) { errno = EINVAL; return -1; } - ntfs_log_trace("entering\n"); /* Does mref lie in the section of $Bitmap we already have cached? */ if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref + (sizeof(buffer) << 3)))) { @@ -756,7 +818,7 @@ int utils_is_metadata(ntfs_inode *inode) file = inode->mrec; if (file && (file->base_mft_record != 0)) { - num = MREF(file->base_mft_record); + num = MREF_LE(file->base_mft_record); if (__metadata(vol, num) == 1) return 1; } @@ -767,9 +829,9 @@ int utils_is_metadata(ntfs_inode *inode) return -1; /* We know this will always be resident. */ - attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset)); + attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset)); - num = MREF(attr->parent_directory); + num = MREF_LE(attr->parent_directory); if ((num != FILE_root) && (__metadata(vol, num) == 1)) return 1; @@ -970,10 +1032,9 @@ int mft_next_record(struct mft_search_ctx *ctx) ctx->inode->mft_no = ctx->mft_num; ctx->inode->vol = ctx->vol; - ctx->inode->mrec = malloc(ctx->vol->mft_record_size); + ctx->inode->mrec = ntfs_malloc(ctx->vol->mft_record_size); if (!ctx->inode->mrec) { free(ctx->inode); // == ntfs_inode_close - ntfs_log_error("Out of memory. Aborting.\n"); return -1; } diff --git a/ntfsprogs/utils.h b/ntfsprogs/utils.h index e5cfbb5d..f35172ad 100644 --- a/ntfsprogs/utils.h +++ b/ntfsprogs/utils.h @@ -52,15 +52,11 @@ int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref); int utils_is_metadata(ntfs_inode *inode); void utils_dump_mem(void *buf, int start, int length, int flags); -#ifndef _NTFS_RICH_H_ ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx); ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft); -#endif -#if !(defined(_NTFS_VOLUME_H) && defined(NTFS_RICH)) int utils_valid_device(const char *name, int force); -ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force); -#endif +ntfs_volume * utils_mount_volume(const char *device, ntfs_mount_flags flags); /** * defines... @@ -154,4 +150,14 @@ static __inline__ int ntfs_mbstoucs_libntfscompat(const char *ins, return ntfs_mbstoucs(ins, outs); } +/* This simple utility function was missing from libntfs-3g. */ +static __inline__ ntfschar *ntfs_attr_get_name(ATTR_RECORD *attr) +{ + return (ntfschar*)((u8*)attr + le16_to_cpu(attr->name_offset)); +} + +/* The define 'leMFT_REF' is not present in libntfs-3g. It is only symbolic so + * typedef it to MFT_REF.*/ +typedef MFT_REF leMFT_REF; + #endif /* _NTFS_UTILS_H_ */