isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
This commit is contained in:
1 parent
3dacbd32c8
commit
73abd6f90d
441 files changed
+187073
-6549
No files matched your search
@@ -0,0 +1,84 @@
|
||||
|
||||
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
|
||||
|
||||
if INSTALL_LIBRARY
|
||||
rootlib_LTLIBRARIES=#Create directory
|
||||
lib_LTLIBRARIES = libntfs-3g.la
|
||||
pkgconfig_DATA = libntfs-3g.pc
|
||||
else
|
||||
noinst_LTLIBRARIES = libntfs-3g.la
|
||||
endif
|
||||
|
||||
libntfs_3g_la_CFLAGS = $(AM_CFLAGS)
|
||||
libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) -I$(top_srcdir)/include/ntfs-3g
|
||||
libntfs_3g_la_LIBADD = $(LIBNTFS_LIBS)
|
||||
libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined
|
||||
|
||||
libntfs_3g_la_SOURCES = \
|
||||
acls.c \
|
||||
attrib.c \
|
||||
attrlist.c \
|
||||
bitmap.c \
|
||||
bootsect.c \
|
||||
cache.c \
|
||||
collate.c \
|
||||
compat.c \
|
||||
compress.c \
|
||||
debug.c \
|
||||
device.c \
|
||||
dir.c \
|
||||
ea.c \
|
||||
efs.c \
|
||||
index.c \
|
||||
inode.c \
|
||||
ioctl.c \
|
||||
lcnalloc.c \
|
||||
logfile.c \
|
||||
logging.c \
|
||||
mft.c \
|
||||
misc.c \
|
||||
mst.c \
|
||||
object_id.c \
|
||||
realpath.c \
|
||||
reparse.c \
|
||||
runlist.c \
|
||||
security.c \
|
||||
unistr.c \
|
||||
volume.c \
|
||||
xattrs.c
|
||||
|
||||
if NTFS_DEVICE_DEFAULT_IO_OPS
|
||||
if WINDOWS
|
||||
libntfs_3g_la_SOURCES += win32_io.c
|
||||
else
|
||||
libntfs_3g_la_SOURCES += unix_io.c
|
||||
endif
|
||||
endif
|
||||
|
||||
# We may need to move .so files to root
|
||||
# And create ldscript or symbolic link from /usr
|
||||
install-exec-hook: install-rootlibLTLIBRARIES
|
||||
if INSTALL_LIBRARY
|
||||
if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \
|
||||
$(MV) -f "$(DESTDIR)/$(libdir)"/libntfs-3g.so* "$(DESTDIR)/$(rootlibdir)"; \
|
||||
fi
|
||||
if GENERATE_LDSCRIPT
|
||||
if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \
|
||||
$(install_sh_PROGRAM) "libntfs-3g.script.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \
|
||||
fi
|
||||
else
|
||||
if [ ! "$(rootlibdir)" -ef "$(libdir)" ]; then \
|
||||
$(LN_S) "$(rootlibdir)/libntfs-3g.so" "$(DESTDIR)/$(libdir)/libntfs-3g.so"; \
|
||||
fi
|
||||
endif
|
||||
endif
|
||||
|
||||
uninstall-local:
|
||||
if INSTALL_LIBRARY
|
||||
$(RM) -f "$(DESTDIR)/$(rootlibdir)"/libntfs-3g.so*
|
||||
endif
|
||||
|
||||
if ENABLE_NTFSPROGS
|
||||
libs: $(lib_LTLIBRARIES)
|
||||
endif
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* attrlist.c - Attribute list attribute handling code. Originated from the Linux-NTFS
|
||||
* project.
|
||||
*
|
||||
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
* 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 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_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "layout.h"
|
||||
#include "attrib.h"
|
||||
#include "attrlist.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_need - check whether inode need attribute list
|
||||
* @ni: opened ntfs inode for which perform check
|
||||
*
|
||||
* Check whether all are attributes belong to one MFT record, in that case
|
||||
* attribute list is not needed.
|
||||
*
|
||||
* Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
|
||||
* to the error code. If function succeed errno set to 0. The following error
|
||||
* codes are defined:
|
||||
* EINVAL - Invalid arguments passed to function or attribute haven't got
|
||||
* attribute list.
|
||||
*/
|
||||
int ntfs_attrlist_need(ntfs_inode *ni)
|
||||
{
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
|
||||
if (!ni) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
|
||||
|
||||
if (!NInoAttrList(ni)) {
|
||||
ntfs_log_trace("Inode haven't got attribute list.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ni->attr_list) {
|
||||
ntfs_log_trace("Corrupt in-memory struct.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
ale = (ATTR_LIST_ENTRY *)ni->attr_list;
|
||||
while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
|
||||
if (MREF_LE(ale->mft_reference) != ni->mft_no)
|
||||
return 1;
|
||||
ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_entry_add - add an attribute list attribute entry
|
||||
* @ni: opened ntfs inode, which contains that attribute
|
||||
* @attr: attribute record to add to attribute list
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL - Invalid arguments passed to function.
|
||||
* ENOMEM - Not enough memory to allocate necessary buffers.
|
||||
* EIO - I/O error occurred or damaged filesystem.
|
||||
* EEXIST - Such attribute already present in attribute list.
|
||||
*/
|
||||
int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
|
||||
{
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
leMFT_REF mref;
|
||||
ntfs_attr *na = NULL;
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
u8 *new_al;
|
||||
int entry_len, entry_offset, err;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
|
||||
(long long) ni->mft_no,
|
||||
(unsigned) le32_to_cpu(attr->type));
|
||||
|
||||
if (!ni || !attr) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
|
||||
|
||||
if (ni->nr_extents == -1)
|
||||
ni = ni->base_ni;
|
||||
|
||||
if (!NInoAttrList(ni)) {
|
||||
ntfs_log_trace("Attribute list isn't present.\n");
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Determine size and allocate memory for new attribute list. */
|
||||
entry_len = (offsetof(ATTR_LIST_ENTRY, name) + sizeof(ntfschar) *
|
||||
attr->name_length + 7) & ~7;
|
||||
new_al = ntfs_calloc(ni->attr_list_size + entry_len);
|
||||
if (!new_al)
|
||||
return -1;
|
||||
|
||||
/* Find place for the new entry. */
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
err = errno;
|
||||
goto err_out;
|
||||
}
|
||||
if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*)
|
||||
((u8*)attr + le16_to_cpu(attr->name_offset)) :
|
||||
AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
|
||||
(attr->non_resident) ? sle64_to_cpu(attr->lowest_vcn) :
|
||||
0, (attr->non_resident) ? NULL : ((u8*)attr +
|
||||
le16_to_cpu(attr->value_offset)), (attr->non_resident) ?
|
||||
0 : le32_to_cpu(attr->value_length), ctx)) {
|
||||
/* Found some extent, check it to be before new extent. */
|
||||
if (ctx->al_entry->lowest_vcn == attr->lowest_vcn) {
|
||||
err = EEXIST;
|
||||
ntfs_log_trace("Such attribute already present in the "
|
||||
"attribute list.\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
goto err_out;
|
||||
}
|
||||
/* Add new entry after this extent. */
|
||||
ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
|
||||
le16_to_cpu(ctx->al_entry->length));
|
||||
} else {
|
||||
/* Check for real errors. */
|
||||
if (errno != ENOENT) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Attribute lookup failed.\n");
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
goto err_out;
|
||||
}
|
||||
/* No previous extents found. */
|
||||
ale = ctx->al_entry;
|
||||
}
|
||||
/* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
||||
/* Determine new entry offset. */
|
||||
entry_offset = ((u8 *)ale - ni->attr_list);
|
||||
/* Set pointer to new entry. */
|
||||
ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
|
||||
/* Zero it to fix valgrind warning. */
|
||||
memset(ale, 0, entry_len);
|
||||
/* Form new entry. */
|
||||
ale->type = attr->type;
|
||||
ale->length = cpu_to_le16(entry_len);
|
||||
ale->name_length = attr->name_length;
|
||||
ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
|
||||
if (attr->non_resident)
|
||||
ale->lowest_vcn = attr->lowest_vcn;
|
||||
else
|
||||
ale->lowest_vcn = const_cpu_to_sle64(0);
|
||||
ale->mft_reference = mref;
|
||||
ale->instance = attr->instance;
|
||||
memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
|
||||
attr->name_length * sizeof(ntfschar));
|
||||
|
||||
/* Resize $ATTRIBUTE_LIST to new length. */
|
||||
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
|
||||
err = errno;
|
||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Copy entries from old attribute list to new. */
|
||||
memcpy(new_al, ni->attr_list, entry_offset);
|
||||
memcpy(new_al + entry_offset + entry_len, ni->attr_list +
|
||||
entry_offset, ni->attr_list_size - entry_offset);
|
||||
|
||||
/* Set new runlist. */
|
||||
free(ni->attr_list);
|
||||
ni->attr_list = new_al;
|
||||
ni->attr_list_size = ni->attr_list_size + entry_len;
|
||||
NInoAttrListSetDirty(ni);
|
||||
/* Done! */
|
||||
ntfs_attr_close(na);
|
||||
return 0;
|
||||
err_out:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
free(new_al);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attrlist_entry_rm - remove an attribute list attribute entry
|
||||
* @ctx: attribute search context describing the attribute list entry
|
||||
*
|
||||
* Remove the attribute list entry @ctx->al_entry from the attribute list.
|
||||
*
|
||||
* Return 0 on success and -1 on error with errno set to the error code.
|
||||
*/
|
||||
int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
|
||||
{
|
||||
u8 *new_al;
|
||||
int new_al_len;
|
||||
ntfs_inode *base_ni;
|
||||
ntfs_attr *na;
|
||||
ATTR_LIST_ENTRY *ale;
|
||||
int err;
|
||||
|
||||
if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
|
||||
ntfs_log_trace("Invalid arguments.\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->base_ntfs_ino)
|
||||
base_ni = ctx->base_ntfs_ino;
|
||||
else
|
||||
base_ni = ctx->ntfs_ino;
|
||||
ale = ctx->al_entry;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
|
||||
(long long) ctx->ntfs_ino->mft_no,
|
||||
(unsigned) le32_to_cpu(ctx->al_entry->type),
|
||||
(long long) sle64_to_cpu(ctx->al_entry->lowest_vcn));
|
||||
|
||||
if (!NInoAttrList(base_ni)) {
|
||||
ntfs_log_trace("Attribute list isn't present.\n");
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate memory for new attribute list. */
|
||||
new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
|
||||
new_al = ntfs_calloc(new_al_len);
|
||||
if (!new_al)
|
||||
return -1;
|
||||
|
||||
/* Reisze $ATTRIBUTE_LIST to new length. */
|
||||
na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, new_al_len)) {
|
||||
err = errno;
|
||||
ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Copy entries from old attribute list to new. */
|
||||
memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
|
||||
memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
|
||||
ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
|
||||
|
||||
/* Set new runlist. */
|
||||
free(base_ni->attr_list);
|
||||
base_ni->attr_list = new_al;
|
||||
base_ni->attr_list_size = new_al_len;
|
||||
NInoAttrListSetDirty(base_ni);
|
||||
/* Done! */
|
||||
ntfs_attr_close(na);
|
||||
return 0;
|
||||
err_out:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
free(new_al);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* bitmap.c - Bitmap handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2004-2008 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 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "bitmap.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_bit_set - set a bit in a field of bits
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to set
|
||||
* @new_value: value to set bit to (0 or 1)
|
||||
*
|
||||
* Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
|
||||
*/
|
||||
void ntfs_bit_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||
{
|
||||
if (!bitmap || new_value > 1)
|
||||
return;
|
||||
if (!new_value)
|
||||
bitmap[bit >> 3] &= ~(1 << (bit & 7));
|
||||
else
|
||||
bitmap[bit >> 3] |= (1 << (bit & 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bit_get - get value of a bit in a field of bits
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to get
|
||||
*
|
||||
* Get and return the value of the bit @bit in @bitmap (0 or 1).
|
||||
* Return -1 on error.
|
||||
*/
|
||||
char ntfs_bit_get(const u8 *bitmap, const u64 bit)
|
||||
{
|
||||
if (!bitmap)
|
||||
return -1;
|
||||
return (bitmap[bit >> 3] >> (bit & 7)) & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
|
||||
* @bitmap: field of bits
|
||||
* @bit: bit to get/set
|
||||
* @new_value: value to set bit to (0 or 1)
|
||||
*
|
||||
* Return the value of the bit @bit and set it to @new_value (0 or 1).
|
||||
* Return -1 on error.
|
||||
*/
|
||||
char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit, const u8 new_value)
|
||||
{
|
||||
register u8 old_bit, shift;
|
||||
|
||||
if (!bitmap || new_value > 1)
|
||||
return -1;
|
||||
shift = bit & 7;
|
||||
old_bit = (bitmap[bit >> 3] >> shift) & 1;
|
||||
if (new_value != old_bit)
|
||||
bitmap[bit >> 3] ^= 1 << shift;
|
||||
return old_bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to set
|
||||
* @count: number of bits to set
|
||||
* @value: value to set the bits to (i.e. 0 or 1)
|
||||
*
|
||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na to @value, where @value is either 0 or 1.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
|
||||
s64 count, int value)
|
||||
{
|
||||
s64 bufsize, br;
|
||||
u8 *buf, *lastbyte_buf;
|
||||
int bit, firstbyte, lastbyte, lastbyte_pos, tmp, ret = -1;
|
||||
|
||||
if (!na || start_bit < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: Invalid argument (%p, %lld, %lld)",
|
||||
__FUNCTION__, na, (long long)start_bit, (long long)count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bit = start_bit & 7;
|
||||
if (bit)
|
||||
firstbyte = 1;
|
||||
else
|
||||
firstbyte = 0;
|
||||
|
||||
/* Calculate the required buffer size in bytes, capping it at 8kiB. */
|
||||
bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
|
||||
if (bufsize > 8192)
|
||||
bufsize = 8192;
|
||||
|
||||
buf = ntfs_malloc(bufsize);
|
||||
if (!buf)
|
||||
return -1;
|
||||
|
||||
/* Depending on @value, zero or set all bits in the allocated buffer. */
|
||||
memset(buf, value ? 0xff : 0, bufsize);
|
||||
|
||||
/* If there is a first partial byte... */
|
||||
if (bit) {
|
||||
/* read it in... */
|
||||
br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
|
||||
if (br != 1) {
|
||||
if (br >= 0)
|
||||
errno = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and set or clear the appropriate bits in it. */
|
||||
while ((bit & 7) && count--) {
|
||||
if (value)
|
||||
*buf |= 1 << bit++;
|
||||
else
|
||||
*buf &= ~(1 << bit++);
|
||||
}
|
||||
/* Update @start_bit to the new position. */
|
||||
start_bit = (start_bit + 7) & ~7;
|
||||
}
|
||||
|
||||
/* Loop until @count reaches zero. */
|
||||
lastbyte = 0;
|
||||
lastbyte_buf = NULL;
|
||||
bit = count & 7;
|
||||
do {
|
||||
/* If there is a last partial byte... */
|
||||
if (count > 0 && bit) {
|
||||
lastbyte_pos = ((count + 7) >> 3) + firstbyte;
|
||||
if (!lastbyte_pos) {
|
||||
// FIXME: Eeek! BUG!
|
||||
ntfs_log_error("Lastbyte is zero. Leaving "
|
||||
"inconsistent metadata.\n");
|
||||
errno = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and it is in the currently loaded bitmap window... */
|
||||
if (lastbyte_pos <= bufsize) {
|
||||
lastbyte_buf = buf + lastbyte_pos - 1;
|
||||
|
||||
/* read the byte in... */
|
||||
br = ntfs_attr_pread(na, (start_bit + count) >>
|
||||
3, 1, lastbyte_buf);
|
||||
if (br != 1) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
if (br >= 0)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Reading of last byte "
|
||||
"failed (%lld). Leaving inconsistent "
|
||||
"metadata", (long long)br);
|
||||
goto free_err_out;
|
||||
}
|
||||
/* and set/clear the appropriate bits in it. */
|
||||
while (bit && count--) {
|
||||
if (value)
|
||||
*lastbyte_buf |= 1 << --bit;
|
||||
else
|
||||
*lastbyte_buf &= ~(1 << --bit);
|
||||
}
|
||||
/* We don't want to come back here... */
|
||||
bit = 0;
|
||||
/* We have a last byte that we have handled. */
|
||||
lastbyte = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the prepared buffer to disk. */
|
||||
tmp = (start_bit >> 3) - firstbyte;
|
||||
br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
|
||||
if (br != bufsize) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
if (br >= 0)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Failed to write buffer to bitmap "
|
||||
"(%lld != %lld). Leaving inconsistent metadata",
|
||||
(long long)br, (long long)bufsize);
|
||||
goto free_err_out;
|
||||
}
|
||||
|
||||
/* Update counters. */
|
||||
tmp = (bufsize - firstbyte - lastbyte) << 3;
|
||||
if (firstbyte) {
|
||||
firstbyte = 0;
|
||||
/*
|
||||
* Re-set the partial first byte so a subsequent write
|
||||
* of the buffer does not have stale, incorrect bits.
|
||||
*/
|
||||
*buf = value ? 0xff : 0;
|
||||
}
|
||||
start_bit += tmp;
|
||||
count -= tmp;
|
||||
if (bufsize > (tmp = (count + 7) >> 3))
|
||||
bufsize = tmp;
|
||||
|
||||
if (lastbyte && count != 0) {
|
||||
// FIXME: Eeek! BUG!
|
||||
ntfs_log_error("Last buffer but count is not zero "
|
||||
"(%lld). Leaving inconsistent metadata.\n",
|
||||
(long long)count);
|
||||
errno = EIO;
|
||||
goto free_err_out;
|
||||
}
|
||||
} while (count > 0);
|
||||
|
||||
ret = 0;
|
||||
|
||||
free_err_out:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_set_run - set a run of bits in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to set
|
||||
* @count: number of bits to set
|
||||
*
|
||||
* Set @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ntfs_log_enter("Set from bit %lld, count %lld\n",
|
||||
(long long)start_bit, (long long)count);
|
||||
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_bitmap_clear_run - clear a run of bits in a bitmap
|
||||
* @na: attribute containing the bitmap
|
||||
* @start_bit: first bit to clear
|
||||
* @count: number of bits to clear
|
||||
*
|
||||
* Clear @count bits starting at bit @start_bit in the bitmap described by the
|
||||
* attribute @na.
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ntfs_log_enter("Clear from bit %lld, count %lld\n",
|
||||
(long long)start_bit, (long long)count);
|
||||
ret = ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* bootsect.c - Boot sector handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
* Copyright (c) 2003-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2005 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_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "param.h"
|
||||
#include "compat.h"
|
||||
#include "bootsect.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector
|
||||
* @b: buffer containing putative boot sector to analyze
|
||||
* @silent: if zero, output progress messages to stderr
|
||||
*
|
||||
* Check if the buffer @b contains a valid ntfs boot sector. The buffer @b
|
||||
* must be at least 512 bytes in size.
|
||||
*
|
||||
* If @silent is zero, output progress messages to stderr. Otherwise, do not
|
||||
* output any messages (except when configured with --enable-debug in which
|
||||
* case warning/debug messages may be displayed).
|
||||
*
|
||||
* Return TRUE if @b contains a valid ntfs boot sector and FALSE if not.
|
||||
*/
|
||||
BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
|
||||
{
|
||||
u32 i;
|
||||
BOOL ret = FALSE;
|
||||
u16 sectors_per_cluster;
|
||||
|
||||
ntfs_log_debug("Beginning bootsector check.\n");
|
||||
|
||||
ntfs_log_debug("Checking OEMid, NTFS signature.\n");
|
||||
if (b->oem_id != const_cpu_to_le64(0x202020205346544eULL)) { /* "NTFS " */
|
||||
ntfs_log_error("NTFS signature is missing.\n");
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking bytes per sector.\n");
|
||||
if (le16_to_cpu(b->bpb.bytes_per_sector) < 256 ||
|
||||
le16_to_cpu(b->bpb.bytes_per_sector) > 4096) {
|
||||
ntfs_log_error("Unexpected bytes per sector value (%d).\n",
|
||||
le16_to_cpu(b->bpb.bytes_per_sector));
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking sectors per cluster.\n");
|
||||
switch (b->bpb.sectors_per_cluster) {
|
||||
case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
|
||||
break;
|
||||
default:
|
||||
if ((b->bpb.sectors_per_cluster < 240)
|
||||
|| (b->bpb.sectors_per_cluster > 253)) {
|
||||
if (b->bpb.sectors_per_cluster > 128)
|
||||
ntfs_log_error("Unexpected sectors"
|
||||
" per cluster value (code 0x%x)\n",
|
||||
b->bpb.sectors_per_cluster);
|
||||
else
|
||||
ntfs_log_error("Unexpected sectors"
|
||||
" per cluster value (%d).\n",
|
||||
b->bpb.sectors_per_cluster);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking cluster size.\n");
|
||||
if (b->bpb.sectors_per_cluster > 128)
|
||||
sectors_per_cluster = 1 << (256 - b->bpb.sectors_per_cluster);
|
||||
else
|
||||
sectors_per_cluster = b->bpb.sectors_per_cluster;
|
||||
i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * sectors_per_cluster;
|
||||
if (i > NTFS_MAX_CLUSTER_SIZE) {
|
||||
ntfs_log_error("Unexpected cluster size (%d).\n", i);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking reserved fields are zero.\n");
|
||||
if (le16_to_cpu(b->bpb.reserved_sectors) ||
|
||||
le16_to_cpu(b->bpb.root_entries) ||
|
||||
le16_to_cpu(b->bpb.sectors) ||
|
||||
le16_to_cpu(b->bpb.sectors_per_fat) ||
|
||||
le32_to_cpu(b->bpb.large_sectors) ||
|
||||
b->bpb.fats) {
|
||||
ntfs_log_error("Reserved fields aren't zero "
|
||||
"(%d, %d, %d, %d, %d, %d).\n",
|
||||
le16_to_cpu(b->bpb.reserved_sectors),
|
||||
le16_to_cpu(b->bpb.root_entries),
|
||||
le16_to_cpu(b->bpb.sectors),
|
||||
le16_to_cpu(b->bpb.sectors_per_fat),
|
||||
le32_to_cpu(b->bpb.large_sectors),
|
||||
b->bpb.fats);
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking clusters per mft record.\n");
|
||||
if ((u8)b->clusters_per_mft_record < 0xe1 ||
|
||||
(u8)b->clusters_per_mft_record > 0xf7) {
|
||||
switch (b->clusters_per_mft_record) {
|
||||
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected clusters per mft record "
|
||||
"(%d).\n", b->clusters_per_mft_record);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
ntfs_log_debug("Checking clusters per index block.\n");
|
||||
if ((u8)b->clusters_per_index_record < 0xe1 ||
|
||||
(u8)b->clusters_per_index_record > 0xf7) {
|
||||
switch (b->clusters_per_index_record) {
|
||||
case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
|
||||
break;
|
||||
default:
|
||||
ntfs_log_error("Unexpected clusters per index record "
|
||||
"(%d).\n", b->clusters_per_index_record);
|
||||
goto not_ntfs;
|
||||
}
|
||||
}
|
||||
|
||||
/* MFT and MFTMirr may not overlap the boot sector or be the same */
|
||||
if (((s64)sle64_to_cpu(b->mft_lcn) <= 0)
|
||||
|| ((s64)sle64_to_cpu(b->mftmirr_lcn) <= 0)
|
||||
|| (b->mft_lcn == b->mftmirr_lcn)) {
|
||||
ntfs_log_error("Invalid location of MFT or MFTMirr.\n");
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
||||
if (b->end_of_sector_marker != const_cpu_to_le16(0xaa55))
|
||||
ntfs_log_debug("Warning: Bootsector has invalid end of sector "
|
||||
"marker.\n");
|
||||
|
||||
ntfs_log_debug("Bootsector check completed successfully.\n");
|
||||
|
||||
ret = TRUE;
|
||||
not_ntfs:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *last_sector_error =
|
||||
"HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,\n"
|
||||
" or it was not setup correctly (e.g. by not using mdadm --build ...),\n"
|
||||
" or a wrong device is tried to be mounted,\n"
|
||||
" or the partition table is corrupt (partition is smaller than NTFS),\n"
|
||||
" or the NTFS boot sector is corrupt (NTFS size is not valid).\n";
|
||||
|
||||
/**
|
||||
* ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector
|
||||
* @vol: ntfs_volume to setup
|
||||
* @bs: buffer containing ntfs boot sector to parse
|
||||
*
|
||||
* Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the
|
||||
* obtained values.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code EINVAL.
|
||||
*/
|
||||
int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
||||
{
|
||||
s64 sectors;
|
||||
u16 sectors_per_cluster;
|
||||
s8 c;
|
||||
|
||||
/* We return -1 with errno = EINVAL on error. */
|
||||
errno = EINVAL;
|
||||
|
||||
vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
|
||||
vol->sector_size_bits = ffs(vol->sector_size) - 1;
|
||||
ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size);
|
||||
ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits);
|
||||
/*
|
||||
* The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
|
||||
* below or equal the number_of_clusters) really belong in the
|
||||
* ntfs_boot_sector_is_ntfs but in this way we can just do this once.
|
||||
*/
|
||||
if (bs->bpb.sectors_per_cluster > 128)
|
||||
sectors_per_cluster = 1 << (256 - bs->bpb.sectors_per_cluster);
|
||||
else
|
||||
sectors_per_cluster = bs->bpb.sectors_per_cluster;
|
||||
ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
|
||||
if (sectors_per_cluster & (sectors_per_cluster - 1)) {
|
||||
ntfs_log_error("sectors_per_cluster (%d) is not a power of 2."
|
||||
"\n", sectors_per_cluster);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sectors = sle64_to_cpu(bs->number_of_sectors);
|
||||
ntfs_log_debug("NumberOfSectors = %lld\n", (long long)sectors);
|
||||
if (!sectors) {
|
||||
ntfs_log_error("Volume size is set to zero.\n");
|
||||
return -1;
|
||||
}
|
||||
if (vol->dev->d_ops->seek(vol->dev,
|
||||
(sectors - 1) << vol->sector_size_bits,
|
||||
SEEK_SET) == -1) {
|
||||
ntfs_log_perror("Failed to read last sector (%lld)",
|
||||
(long long)(sectors - 1));
|
||||
ntfs_log_error("%s", last_sector_error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vol->nr_clusters = sectors >> (ffs(sectors_per_cluster) - 1);
|
||||
|
||||
vol->mft_lcn = sle64_to_cpu(bs->mft_lcn);
|
||||
vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn);
|
||||
ntfs_log_debug("MFT LCN = %lld\n", (long long)vol->mft_lcn);
|
||||
ntfs_log_debug("MFTMirr LCN = %lld\n", (long long)vol->mftmirr_lcn);
|
||||
if ((vol->mft_lcn < 0 || vol->mft_lcn > vol->nr_clusters) ||
|
||||
(vol->mftmirr_lcn < 0 || vol->mftmirr_lcn > vol->nr_clusters)) {
|
||||
ntfs_log_error("$MFT LCN (%lld) or $MFTMirr LCN (%lld) is "
|
||||
"greater than the number of clusters (%lld).\n",
|
||||
(long long)vol->mft_lcn, (long long)vol->mftmirr_lcn,
|
||||
(long long)vol->nr_clusters);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vol->cluster_size = sectors_per_cluster * vol->sector_size;
|
||||
if (vol->cluster_size & (vol->cluster_size - 1)) {
|
||||
ntfs_log_error("cluster_size (%d) is not a power of 2.\n",
|
||||
vol->cluster_size);
|
||||
return -1;
|
||||
}
|
||||
vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
|
||||
/*
|
||||
* Need to get the clusters per mft record and handle it if it is
|
||||
* negative. Then calculate the mft_record_size. A value of 0x80 is
|
||||
* illegal, thus signed char is actually ok!
|
||||
*/
|
||||
c = bs->clusters_per_mft_record;
|
||||
ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size);
|
||||
ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits);
|
||||
ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c);
|
||||
/*
|
||||
* When clusters_per_mft_record is negative, it means that it is to
|
||||
* be taken to be the negative base 2 logarithm of the mft_record_size
|
||||
* min bytes. Then:
|
||||
* mft_record_size = 2^(-clusters_per_mft_record) bytes.
|
||||
*/
|
||||
if (c < 0)
|
||||
vol->mft_record_size = 1 << -c;
|
||||
else
|
||||
vol->mft_record_size = c << vol->cluster_size_bits;
|
||||
if (vol->mft_record_size & (vol->mft_record_size - 1)) {
|
||||
ntfs_log_error("mft_record_size (%d) is not a power of 2.\n",
|
||||
vol->mft_record_size);
|
||||
return -1;
|
||||
}
|
||||
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
||||
ntfs_log_debug("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size);
|
||||
ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
|
||||
/* Same as above for INDX record. */
|
||||
c = bs->clusters_per_index_record;
|
||||
ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c);
|
||||
if (c < 0)
|
||||
vol->indx_record_size = 1 << -c;
|
||||
else
|
||||
vol->indx_record_size = c << vol->cluster_size_bits;
|
||||
vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
|
||||
ntfs_log_debug("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size);
|
||||
ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
|
||||
/*
|
||||
* Work out the size of the MFT mirror in number of mft records. If the
|
||||
* cluster size is less than or equal to the size taken by four mft
|
||||
* records, the mft mirror stores the first four mft records. If the
|
||||
* cluster size is bigger than the size taken by four mft records, the
|
||||
* mft mirror contains as many mft records as will fit into one
|
||||
* cluster.
|
||||
*/
|
||||
if (vol->cluster_size <= 4 * vol->mft_record_size)
|
||||
vol->mftmirr_size = 4;
|
||||
else
|
||||
vol->mftmirr_size = vol->cluster_size / vol->mft_record_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,606 @@
|
||||
/**
|
||||
* cache.c : deal with LRU caches
|
||||
*
|
||||
* Copyright (c) 2008-2010 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "security.h"
|
||||
#include "cache.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
/*
|
||||
* General functions to deal with LRU caches
|
||||
*
|
||||
* The cached data have to be organized in a structure in which
|
||||
* the first fields must follow a mandatory pattern and further
|
||||
* fields may contain any fixed size data. They are stored in an
|
||||
* LRU list.
|
||||
*
|
||||
* A compare function must be provided for finding a wanted entry
|
||||
* in the cache. Another function may be provided for invalidating
|
||||
* an entry to facilitate multiple invalidation.
|
||||
*
|
||||
* These functions never return error codes. When there is a
|
||||
* shortage of memory, data is simply not cached.
|
||||
* When there is a hashing bug, hashing is dropped, and sequential
|
||||
* searches are used.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Enter a new hash index, after a new record has been inserted
|
||||
*
|
||||
* Do not call when a record has been modified (with no key change)
|
||||
*/
|
||||
|
||||
static void inserthashindex(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *current)
|
||||
{
|
||||
int h;
|
||||
struct HASH_ENTRY *link;
|
||||
struct HASH_ENTRY *first;
|
||||
|
||||
if (cache->dohash) {
|
||||
h = cache->dohash(current);
|
||||
if ((h >= 0) && (h < cache->max_hash)) {
|
||||
/* get a free link and insert at top of hash list */
|
||||
link = cache->free_hash;
|
||||
if (link) {
|
||||
cache->free_hash = link->next;
|
||||
first = cache->first_hash[h];
|
||||
if (first)
|
||||
link->next = first;
|
||||
else
|
||||
link->next = NULL;
|
||||
link->entry = current;
|
||||
cache->first_hash[h] = link;
|
||||
} else {
|
||||
ntfs_log_error("No more hash entries,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Illegal hash value,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop a hash index when a record is about to be deleted
|
||||
*/
|
||||
|
||||
static void drophashindex(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *current, int hash)
|
||||
{
|
||||
struct HASH_ENTRY *link;
|
||||
struct HASH_ENTRY *previous;
|
||||
|
||||
if (cache->dohash) {
|
||||
if ((hash >= 0) && (hash < cache->max_hash)) {
|
||||
/* find the link and unlink */
|
||||
link = cache->first_hash[hash];
|
||||
previous = (struct HASH_ENTRY*)NULL;
|
||||
while (link && (link->entry != current)) {
|
||||
previous = link;
|
||||
link = link->next;
|
||||
}
|
||||
if (link) {
|
||||
if (previous)
|
||||
previous->next = link->next;
|
||||
else
|
||||
cache->first_hash[hash] = link->next;
|
||||
link->next = cache->free_hash;
|
||||
cache->free_hash = link;
|
||||
} else {
|
||||
ntfs_log_error("Bad hash list,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Illegal hash value,"
|
||||
" cache %s hashing dropped\n",
|
||||
cache->name);
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch an entry from cache
|
||||
*
|
||||
* returns the cache entry, or NULL if not available
|
||||
* The returned entry may be modified, but not freed
|
||||
*/
|
||||
|
||||
struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *wanted, cache_compare compare)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *previous;
|
||||
struct HASH_ENTRY *link;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache) {
|
||||
if (cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* locate the entry if present
|
||||
*/
|
||||
h = cache->dohash(wanted);
|
||||
link = cache->first_hash[h];
|
||||
while (link && compare(link->entry, wanted))
|
||||
link = link->next;
|
||||
if (link)
|
||||
current = link->entry;
|
||||
}
|
||||
if (!cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list if no hash table
|
||||
* or if hashing has just failed
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
while (current
|
||||
&& compare(current, wanted)) {
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
if (current) {
|
||||
previous = current->previous;
|
||||
cache->hits++;
|
||||
if (previous) {
|
||||
/*
|
||||
* found and not at head of list, unlink from current
|
||||
* position and relink as head of list
|
||||
*/
|
||||
previous->next = current->next;
|
||||
if (current->next)
|
||||
current->next->previous
|
||||
= current->previous;
|
||||
else
|
||||
cache->oldest_entry
|
||||
= current->previous;
|
||||
current->next = cache->most_recent_entry;
|
||||
current->previous
|
||||
= (struct CACHED_GENERIC*)NULL;
|
||||
cache->most_recent_entry->previous = current;
|
||||
cache->most_recent_entry = current;
|
||||
}
|
||||
}
|
||||
cache->reads++;
|
||||
}
|
||||
return (current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter an inode number into cache
|
||||
* returns the cache entry or NULL if not possible
|
||||
*/
|
||||
|
||||
struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *item,
|
||||
cache_compare compare)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *before;
|
||||
struct HASH_ENTRY *link;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache) {
|
||||
if (cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* find out whether the entry if present
|
||||
*/
|
||||
h = cache->dohash(item);
|
||||
link = cache->first_hash[h];
|
||||
while (link && compare(link->entry, item))
|
||||
link = link->next;
|
||||
if (link) {
|
||||
current = link->entry;
|
||||
}
|
||||
}
|
||||
if (!cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list to locate the end,
|
||||
* and find out whether the entry is already in list
|
||||
* As we normally go to the end, no statistics is
|
||||
* kept.
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
while (current
|
||||
&& compare(current, item)) {
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (!current) {
|
||||
/*
|
||||
* Not in list, get a free entry or reuse the
|
||||
* last entry, and relink as head of list
|
||||
* Note : we assume at least three entries, so
|
||||
* before, previous and first are different when
|
||||
* an entry is reused.
|
||||
*/
|
||||
|
||||
if (cache->free_entry) {
|
||||
current = cache->free_entry;
|
||||
cache->free_entry = cache->free_entry->next;
|
||||
if (item->varsize) {
|
||||
current->variable = ntfs_malloc(
|
||||
item->varsize);
|
||||
} else
|
||||
current->variable = (void*)NULL;
|
||||
current->varsize = item->varsize;
|
||||
if (!cache->oldest_entry)
|
||||
cache->oldest_entry = current;
|
||||
} else {
|
||||
/* reusing the oldest entry */
|
||||
current = cache->oldest_entry;
|
||||
before = current->previous;
|
||||
before->next = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,current,
|
||||
cache->dohash(current));
|
||||
if (cache->dofree)
|
||||
cache->dofree(current);
|
||||
cache->oldest_entry = current->previous;
|
||||
if (item->varsize) {
|
||||
if (current->varsize)
|
||||
current->variable = realloc(
|
||||
current->variable,
|
||||
item->varsize);
|
||||
else
|
||||
current->variable = ntfs_malloc(
|
||||
item->varsize);
|
||||
} else {
|
||||
if (current->varsize)
|
||||
free(current->variable);
|
||||
current->variable = (void*)NULL;
|
||||
}
|
||||
current->varsize = item->varsize;
|
||||
}
|
||||
current->next = cache->most_recent_entry;
|
||||
current->previous = (struct CACHED_GENERIC*)NULL;
|
||||
if (cache->most_recent_entry)
|
||||
cache->most_recent_entry->previous = current;
|
||||
cache->most_recent_entry = current;
|
||||
memcpy(current->payload, item->payload, cache->fixed_size);
|
||||
if (item->varsize) {
|
||||
if (current->variable) {
|
||||
memcpy(current->variable,
|
||||
item->variable, item->varsize);
|
||||
} else {
|
||||
/*
|
||||
* no more memory for variable part
|
||||
* recycle entry in free list
|
||||
* not an error, just uncacheable
|
||||
*/
|
||||
cache->most_recent_entry = current->next;
|
||||
current->next = cache->free_entry;
|
||||
cache->free_entry = current;
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
}
|
||||
} else {
|
||||
current->variable = (void*)NULL;
|
||||
current->varsize = 0;
|
||||
}
|
||||
if (cache->dohash && current)
|
||||
inserthashindex(cache,current);
|
||||
}
|
||||
cache->writes++;
|
||||
}
|
||||
return (current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate a cache entry
|
||||
* The entry is moved to the free entry list
|
||||
* A specific function may be called for entry deletion
|
||||
*/
|
||||
|
||||
static void do_invalidate(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *current, int flags)
|
||||
{
|
||||
struct CACHED_GENERIC *previous;
|
||||
|
||||
previous = current->previous;
|
||||
if ((flags & CACHE_FREE) && cache->dofree)
|
||||
cache->dofree(current);
|
||||
/*
|
||||
* Relink into free list
|
||||
*/
|
||||
if (current->next)
|
||||
current->next->previous = current->previous;
|
||||
else
|
||||
cache->oldest_entry = current->previous;
|
||||
if (previous)
|
||||
previous->next = current->next;
|
||||
else
|
||||
cache->most_recent_entry = current->next;
|
||||
current->next = cache->free_entry;
|
||||
cache->free_entry = current;
|
||||
if (current->variable)
|
||||
free(current->variable);
|
||||
current->varsize = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Invalidate entries in cache
|
||||
*
|
||||
* Several entries may have to be invalidated (at least for inodes
|
||||
* associated to directories which have been renamed), a different
|
||||
* compare function may be provided to select entries to invalidate
|
||||
*
|
||||
* Returns the number of deleted entries, this can be used by
|
||||
* the caller to signal a cache corruption if the entry was
|
||||
* supposed to be found.
|
||||
*/
|
||||
|
||||
int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
|
||||
const struct CACHED_GENERIC *item, cache_compare compare,
|
||||
int flags)
|
||||
{
|
||||
struct CACHED_GENERIC *current;
|
||||
struct CACHED_GENERIC *next;
|
||||
struct HASH_ENTRY *link;
|
||||
int count;
|
||||
int h;
|
||||
|
||||
current = (struct CACHED_GENERIC*)NULL;
|
||||
count = 0;
|
||||
if (cache) {
|
||||
if (!(flags & CACHE_NOHASH) && cache->dohash) {
|
||||
/*
|
||||
* When possible, use the hash table to
|
||||
* find out whether the entry if present
|
||||
*/
|
||||
h = cache->dohash(item);
|
||||
link = cache->first_hash[h];
|
||||
while (link) {
|
||||
if (compare(link->entry, item))
|
||||
link = link->next;
|
||||
else {
|
||||
current = link->entry;
|
||||
link = link->next;
|
||||
if (current) {
|
||||
drophashindex(cache,current,h);
|
||||
do_invalidate(cache,
|
||||
current,flags);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((flags & CACHE_NOHASH) || !cache->dohash) {
|
||||
/*
|
||||
* Search sequentially in LRU list
|
||||
*/
|
||||
current = cache->most_recent_entry;
|
||||
while (current) {
|
||||
if (!compare(current, item)) {
|
||||
next = current->next;
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,current,
|
||||
cache->dohash(current));
|
||||
do_invalidate(cache,current,flags);
|
||||
current = next;
|
||||
count++;
|
||||
} else {
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
int ntfs_remove_cache(struct CACHE_HEADER *cache,
|
||||
struct CACHED_GENERIC *item, int flags)
|
||||
{
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
if (cache) {
|
||||
if (cache->dohash)
|
||||
drophashindex(cache,item,cache->dohash(item));
|
||||
do_invalidate(cache,item,flags);
|
||||
count++;
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free memory allocated to a cache
|
||||
*/
|
||||
|
||||
static void ntfs_free_cache(struct CACHE_HEADER *cache)
|
||||
{
|
||||
struct CACHED_GENERIC *entry;
|
||||
|
||||
if (cache) {
|
||||
for (entry=cache->most_recent_entry; entry; entry=entry->next) {
|
||||
if (cache->dofree)
|
||||
cache->dofree(entry);
|
||||
if (entry->variable)
|
||||
free(entry->variable);
|
||||
}
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a cache
|
||||
*
|
||||
* Returns the cache header, or NULL if the cache could not be created
|
||||
*/
|
||||
|
||||
static struct CACHE_HEADER *ntfs_create_cache(const char *name,
|
||||
cache_free dofree, cache_hash dohash,
|
||||
int full_item_size,
|
||||
int item_count, int max_hash)
|
||||
{
|
||||
struct CACHE_HEADER *cache;
|
||||
struct CACHED_GENERIC *pc;
|
||||
struct CACHED_GENERIC *qc;
|
||||
struct HASH_ENTRY *ph;
|
||||
struct HASH_ENTRY *qh;
|
||||
struct HASH_ENTRY **px;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = offsetof(struct CACHE_HEADER, entry) + item_count*full_item_size;
|
||||
if (max_hash)
|
||||
size += item_count*sizeof(struct HASH_ENTRY)
|
||||
+ max_hash*sizeof(struct HASH_ENTRY*);
|
||||
cache = (struct CACHE_HEADER*)ntfs_malloc(size);
|
||||
if (cache) {
|
||||
/* header */
|
||||
cache->name = name;
|
||||
cache->dofree = dofree;
|
||||
if (dohash && max_hash) {
|
||||
cache->dohash = dohash;
|
||||
cache->max_hash = max_hash;
|
||||
} else {
|
||||
cache->dohash = (cache_hash)NULL;
|
||||
cache->max_hash = 0;
|
||||
}
|
||||
cache->fixed_size = full_item_size - offsetof(struct CACHED_GENERIC, payload);
|
||||
cache->reads = 0;
|
||||
cache->writes = 0;
|
||||
cache->hits = 0;
|
||||
/* chain the data entries, and mark an invalid entry */
|
||||
cache->most_recent_entry = (struct CACHED_GENERIC*)NULL;
|
||||
cache->oldest_entry = (struct CACHED_GENERIC*)NULL;
|
||||
cache->free_entry = &cache->entry[0];
|
||||
pc = &cache->entry[0];
|
||||
for (i=0; i<(item_count - 1); i++) {
|
||||
qc = (struct CACHED_GENERIC*)((char*)pc
|
||||
+ full_item_size);
|
||||
pc->next = qc;
|
||||
pc->variable = (void*)NULL;
|
||||
pc->varsize = 0;
|
||||
pc = qc;
|
||||
}
|
||||
/* special for the last entry */
|
||||
pc->next = (struct CACHED_GENERIC*)NULL;
|
||||
pc->variable = (void*)NULL;
|
||||
pc->varsize = 0;
|
||||
|
||||
if (max_hash) {
|
||||
/* chain the hash entries */
|
||||
ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size);
|
||||
cache->free_hash = ph;
|
||||
for (i=0; i<(item_count - 1); i++) {
|
||||
qh = &ph[1];
|
||||
ph->next = qh;
|
||||
ph = qh;
|
||||
}
|
||||
/* special for the last entry */
|
||||
if (item_count) {
|
||||
ph->next = (struct HASH_ENTRY*)NULL;
|
||||
}
|
||||
/* create and initialize the hash indexes */
|
||||
px = (struct HASH_ENTRY**)&ph[1];
|
||||
cache->first_hash = px;
|
||||
for (i=0; i<max_hash; i++)
|
||||
px[i] = (struct HASH_ENTRY*)NULL;
|
||||
} else {
|
||||
cache->free_hash = (struct HASH_ENTRY*)NULL;
|
||||
cache->first_hash = (struct HASH_ENTRY**)NULL;
|
||||
}
|
||||
}
|
||||
return (cache);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create all LRU caches
|
||||
*
|
||||
* No error return, if creation is not possible, cacheing will
|
||||
* just be not available
|
||||
*/
|
||||
|
||||
void ntfs_create_lru_caches(ntfs_volume *vol)
|
||||
{
|
||||
#if CACHE_INODE_SIZE
|
||||
/* inode cache */
|
||||
vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL,
|
||||
ntfs_dir_inode_hash, sizeof(struct CACHED_INODE),
|
||||
CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE);
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
/* idata cache */
|
||||
vol->nidata_cache = ntfs_create_cache("nidata",
|
||||
ntfs_inode_nidata_free, ntfs_inode_nidata_hash,
|
||||
sizeof(struct CACHED_NIDATA),
|
||||
CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE);
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
/* lookup cache */
|
||||
vol->lookup_cache = ntfs_create_cache("lookup",
|
||||
(cache_free)NULL, ntfs_dir_lookup_hash,
|
||||
sizeof(struct CACHED_LOOKUP),
|
||||
CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE);
|
||||
#endif
|
||||
vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL,
|
||||
(cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0);
|
||||
#if CACHE_LEGACY_SIZE
|
||||
vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL,
|
||||
(cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all LRU caches
|
||||
*/
|
||||
|
||||
void ntfs_free_lru_caches(ntfs_volume *vol)
|
||||
{
|
||||
#if CACHE_INODE_SIZE
|
||||
ntfs_free_cache(vol->xinode_cache);
|
||||
#endif
|
||||
#if CACHE_NIDATA_SIZE
|
||||
ntfs_free_cache(vol->nidata_cache);
|
||||
#endif
|
||||
#if CACHE_LOOKUP_SIZE
|
||||
ntfs_free_cache(vol->lookup_cache);
|
||||
#endif
|
||||
ntfs_free_cache(vol->securid_cache);
|
||||
#if CACHE_LEGACY_SIZE
|
||||
ntfs_free_cache(vol->legacy_cache);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* collate.c - NTFS collation handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "index.h"
|
||||
#include "collate.h"
|
||||
#include "debug.h"
|
||||
#include "unistr.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_collate_binary - Which of two binary objects should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
rc = memcmp(data1, data2, min(data1_len, data2_len));
|
||||
if (!rc && (data1_len != data2_len)) {
|
||||
if (data1_len < data2_len)
|
||||
rc = -1;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_ulong - Which of two long ints should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
u32 d1, d2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (data1_len != data2_len || data1_len != 4) {
|
||||
ntfs_log_error("data1_len or/and data2_len not equal to 4.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
d1 = le32_to_cpup(data1);
|
||||
d2 = le32_to_cpup(data2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 == d2)
|
||||
rc = 0;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_ulongs - Which of two le32 arrays should be listed first
|
||||
*
|
||||
* Returns: -1, 0 or 1 depending of how the arrays compare
|
||||
*/
|
||||
|
||||
static int ntfs_collate_ntofs_ulongs(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
int len;
|
||||
const le32 *p1, *p2;
|
||||
u32 d1, d2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if ((data1_len != data2_len) || (data1_len <= 0) || (data1_len & 3)) {
|
||||
ntfs_log_error("data1_len or data2_len not valid\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
p1 = (const le32*)data1;
|
||||
p2 = (const le32*)data2;
|
||||
len = data1_len;
|
||||
do {
|
||||
d1 = le32_to_cpup(p1);
|
||||
p1++;
|
||||
d2 = le32_to_cpup(p2);
|
||||
p2++;
|
||||
} while ((d1 == d2) && ((len -= 4) > 0));
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 == d2)
|
||||
rc = 0;
|
||||
else
|
||||
rc = 1;
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_ntofs_security_hash - Which of two security descriptors
|
||||
* should be listed first
|
||||
* @vol: unused
|
||||
* @data1:
|
||||
* @data1_len:
|
||||
* @data2:
|
||||
* @data2_len:
|
||||
*
|
||||
* JPA compare two security hash keys made of two unsigned le32
|
||||
*
|
||||
* Returns: -1, 0 or 1 depending of how the keys compare
|
||||
*/
|
||||
static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unused)),
|
||||
const void *data1, const int data1_len,
|
||||
const void *data2, const int data2_len)
|
||||
{
|
||||
int rc;
|
||||
u32 d1, d2;
|
||||
const le32 *p1, *p2;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
if (data1_len != data2_len || data1_len != 8) {
|
||||
ntfs_log_error("data1_len or/and data2_len not equal to 8.\n");
|
||||
return NTFS_COLLATION_ERROR;
|
||||
}
|
||||
p1 = (const le32*)data1;
|
||||
p2 = (const le32*)data2;
|
||||
d1 = le32_to_cpup(p1);
|
||||
d2 = le32_to_cpup(p2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 > d2)
|
||||
rc = 1;
|
||||
else {
|
||||
p1++;
|
||||
p2++;
|
||||
d1 = le32_to_cpup(p1);
|
||||
d2 = le32_to_cpup(p2);
|
||||
if (d1 < d2)
|
||||
rc = -1;
|
||||
else {
|
||||
if (d1 > d2)
|
||||
rc = 1;
|
||||
else
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_collate_file_name - Which of two filenames should be listed first
|
||||
* @vol:
|
||||
* @data1:
|
||||
* @data1_len: unused
|
||||
* @data2:
|
||||
* @data2_len: unused
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_collate_file_name(ntfs_volume *vol,
|
||||
const void *data1, const int data1_len __attribute__((unused)),
|
||||
const void *data2, const int data2_len __attribute__((unused)))
|
||||
{
|
||||
const FILE_NAME_ATTR *file_name_attr1;
|
||||
const FILE_NAME_ATTR *file_name_attr2;
|
||||
int rc;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
file_name_attr1 = (const FILE_NAME_ATTR*)data1;
|
||||
file_name_attr2 = (const FILE_NAME_ATTR*)data2;
|
||||
rc = ntfs_names_full_collate(
|
||||
(ntfschar*)&file_name_attr1->file_name,
|
||||
file_name_attr1->file_name_length,
|
||||
(ntfschar*)&file_name_attr2->file_name,
|
||||
file_name_attr2->file_name_length,
|
||||
CASE_SENSITIVE, vol->upcase, vol->upcase_len);
|
||||
ntfs_log_trace("Done, returning %i.\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a pointer to appropriate collation function.
|
||||
*
|
||||
* Returns NULL if the needed function is not implemented
|
||||
*/
|
||||
|
||||
COLLATE ntfs_get_collate_function(COLLATION_RULES cr)
|
||||
{
|
||||
COLLATE collate;
|
||||
|
||||
switch (cr) {
|
||||
case COLLATION_BINARY :
|
||||
collate = ntfs_collate_binary;
|
||||
break;
|
||||
case COLLATION_FILE_NAME :
|
||||
collate = ntfs_collate_file_name;
|
||||
break;
|
||||
case COLLATION_NTOFS_SECURITY_HASH :
|
||||
collate = ntfs_collate_ntofs_security_hash;
|
||||
break;
|
||||
case COLLATION_NTOFS_ULONG :
|
||||
collate = ntfs_collate_ntofs_ulong;
|
||||
break;
|
||||
case COLLATION_NTOFS_ULONGS :
|
||||
collate = ntfs_collate_ntofs_ulongs;
|
||||
break;
|
||||
default :
|
||||
errno = EOPNOTSUPP;
|
||||
collate = (COLLATE)NULL;
|
||||
break;
|
||||
}
|
||||
return (collate);
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* compat.c - Tweaks for Windows compatibility
|
||||
*
|
||||
* Copyright (c) 2002 Richard Russon
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
*
|
||||
* 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
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#ifndef HAVE_FFS
|
||||
/**
|
||||
* ffs - Find the first set bit in an int
|
||||
* @x:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
int ffs(int x)
|
||||
{
|
||||
int r = 1;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff)) {
|
||||
x >>= 16;
|
||||
r += 16;
|
||||
}
|
||||
if (!(x & 0xff)) {
|
||||
x >>= 8;
|
||||
r += 8;
|
||||
}
|
||||
if (!(x & 0xf)) {
|
||||
x >>= 4;
|
||||
r += 4;
|
||||
}
|
||||
if (!(x & 3)) {
|
||||
x >>= 2;
|
||||
r += 2;
|
||||
}
|
||||
if (!(x & 1)) {
|
||||
x >>= 1;
|
||||
r += 1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif /* HAVE_FFS */
|
||||
|
||||
#ifndef HAVE_DAEMON
|
||||
/* ************************************************************
|
||||
* From: src.opensolaris.org
|
||||
* src/lib/libresolv2/common/bsd/daemon.c
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 1997-2000 by Sun Microsystems, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93";
|
||||
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int daemon(int nochdir, int noclose) {
|
||||
int fd;
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
return (-1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (setsid() == -1)
|
||||
return (-1);
|
||||
|
||||
if (!nochdir)
|
||||
(void)chdir("/");
|
||||
|
||||
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
|
||||
(void)dup2(fd, 0);
|
||||
(void)dup2(fd, 1);
|
||||
(void)dup2(fd, 2);
|
||||
if (fd > 2)
|
||||
(void)close (fd);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* End: src/lib/libresolv2/common/bsd/daemon.c
|
||||
*************************************************************/
|
||||
#endif /* HAVE_DAEMON */
|
||||
|
||||
#ifndef HAVE_STRSEP
|
||||
/* ************************************************************
|
||||
* From: src.opensolaris.org
|
||||
* src/lib/libresolv2/common/bsd/strsep.c
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 1997, by Sun Microsystems, Inc.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93";
|
||||
static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get next token from string *stringp, where tokens are possibly-empty
|
||||
* strings separated by characters from delim.
|
||||
*
|
||||
* Writes NULs into the string at *stringp to end tokens.
|
||||
* delim need not remain constant from call to call.
|
||||
* On return, *stringp points past the last NUL written (if there might
|
||||
* be further tokens), or is NULL (if there are definitely no more tokens).
|
||||
*
|
||||
* If *stringp is NULL, strsep returns NULL.
|
||||
*/
|
||||
char *strsep(char **stringp, const char *delim) {
|
||||
char *s;
|
||||
const char *spanp;
|
||||
int c, sc;
|
||||
char *tok;
|
||||
|
||||
if ((s = *stringp) == NULL)
|
||||
return (NULL);
|
||||
for (tok = s;;) {
|
||||
c = *s++;
|
||||
spanp = delim;
|
||||
do {
|
||||
if ((sc = *spanp++) == c) {
|
||||
if (c == 0)
|
||||
s = NULL;
|
||||
else
|
||||
s[-1] = 0;
|
||||
*stringp = s;
|
||||
return (tok);
|
||||
}
|
||||
} while (sc != 0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* End: src/lib/libresolv2/common/bsd/strsep.c
|
||||
*************************************************************/
|
||||
#endif /* HAVE_STRSEP */
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* debug.c - Debugging output functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004-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 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_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "runlist.h"
|
||||
#include "debug.h"
|
||||
#include "logging.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* ntfs_debug_runlist_dump - Dump a runlist.
|
||||
* @rl:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
void ntfs_debug_runlist_dump(const runlist_element *rl)
|
||||
{
|
||||
int i = 0;
|
||||
const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED",
|
||||
"LCN_ENOENT ", "LCN_EINVAL ",
|
||||
"LCN_unknown " };
|
||||
|
||||
ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n");
|
||||
if (!rl) {
|
||||
ntfs_log_debug("Run list not present.\n");
|
||||
return;
|
||||
}
|
||||
ntfs_log_debug("VCN LCN Run length\n");
|
||||
do {
|
||||
LCN lcn = (rl + i)->lcn;
|
||||
|
||||
if (lcn < (LCN)0) {
|
||||
int idx = -lcn - 1;
|
||||
|
||||
if (idx > -LCN_EINVAL - 1)
|
||||
idx = 4;
|
||||
ntfs_log_debug("%-16lld %s %-16lld%s\n",
|
||||
(long long)rl[i].vcn, lcn_str[idx],
|
||||
(long long)rl[i].length,
|
||||
rl[i].length ? "" : " (runlist end)");
|
||||
} else
|
||||
ntfs_log_debug("%-16lld %-16lld %-16lld%s\n",
|
||||
(long long)rl[i].vcn, (long long)rl[i].lcn,
|
||||
(long long)rl[i].length,
|
||||
rl[i].length ? "" : " (runlist end)");
|
||||
} while (rl[i++].length);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,932 @@
|
||||
/**
|
||||
* device.c - Low level device io functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2004-2013 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||
* Copyright (c) 2010 Jean-Pierre Andre
|
||||
* Copyright (c) 2008-2013 Tuxera Inc.
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_DISK_H
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FD_H
|
||||
#include <linux/fd.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_HDREG_H
|
||||
#include <linux/hdreg.h>
|
||||
#endif
|
||||
#ifdef ENABLE_HD
|
||||
#include <hd.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mst.h"
|
||||
#include "debug.h"
|
||||
#include "device.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
|
||||
#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
|
||||
#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */
|
||||
#endif
|
||||
#if defined(linux) && !defined(HDIO_GETGEO)
|
||||
#define HDIO_GETGEO 0x0301 /* Get device geometry. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
|
||||
# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
|
||||
#endif
|
||||
#if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
|
||||
# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
|
||||
* @name: name of the device (must be present)
|
||||
* @state: initial device state (usually zero)
|
||||
* @dops: ntfs device operations to use with the device (must be present)
|
||||
* @priv_data: pointer to private data (optional)
|
||||
*
|
||||
* Allocate an ntfs device structure and pre-initialize it with the user-
|
||||
* specified device operations @dops, device state @state, device name @name,
|
||||
* and optional private data @priv_data.
|
||||
*
|
||||
* Note, @name is copied and can hence be freed after this functions returns.
|
||||
*
|
||||
* On success return a pointer to the allocated ntfs device structure and on
|
||||
* error return NULL with errno set to the error code returned by ntfs_malloc().
|
||||
*/
|
||||
struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
|
||||
struct ntfs_device_operations *dops, void *priv_data)
|
||||
{
|
||||
struct ntfs_device *dev;
|
||||
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = ntfs_malloc(sizeof(struct ntfs_device));
|
||||
if (dev) {
|
||||
if (!(dev->d_name = strdup(name))) {
|
||||
int eo = errno;
|
||||
free(dev);
|
||||
errno = eo;
|
||||
return NULL;
|
||||
}
|
||||
dev->d_ops = dops;
|
||||
dev->d_state = state;
|
||||
dev->d_private = priv_data;
|
||||
dev->d_heads = -1;
|
||||
dev->d_sectors_per_track = -1;
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_free - free an ntfs device structure
|
||||
* @dev: ntfs device structure to free
|
||||
*
|
||||
* Free the ntfs device structure @dev.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL Invalid pointer @dev.
|
||||
* EBUSY Device is still open. Close it before freeing it!
|
||||
*/
|
||||
int ntfs_device_free(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (NDevOpen(dev)) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
free(dev->d_name);
|
||||
free(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync the device
|
||||
*
|
||||
* returns zero if successful.
|
||||
*/
|
||||
|
||||
int ntfs_device_sync(struct ntfs_device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ntfs_device_operations *dops;
|
||||
|
||||
if (NDevDirty(dev)) {
|
||||
dops = dev->d_ops;
|
||||
ret = dops->sync(dev);
|
||||
} else
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_pread - positioned read from disk
|
||||
* @dev: device to read from
|
||||
* @pos: position in device to read from
|
||||
* @count: number of bytes to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* This function will read @count bytes from device @dev at position @pos into
|
||||
* the data buffer @b.
|
||||
*
|
||||
* On success, return the number of successfully read bytes. If this number is
|
||||
* lower than @count this means that we have either reached end of file or
|
||||
* encountered an error during the read so that the read is partial. 0 means
|
||||
* end of file or nothing to read (@count is 0).
|
||||
*
|
||||
* On error and nothing has been read, return -1 with errno set appropriately
|
||||
* to the return code of either seek, read, or set to EINVAL in case of
|
||||
* invalid arguments.
|
||||
*/
|
||||
s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b)
|
||||
{
|
||||
s64 br, total;
|
||||
struct ntfs_device_operations *dops;
|
||||
|
||||
ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
|
||||
|
||||
if (!b || count < 0 || pos < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
dops = dev->d_ops;
|
||||
|
||||
for (total = 0; count; count -= br, total += br) {
|
||||
br = dops->pread(dev, (char*)b + total, count, pos + total);
|
||||
/* If everything ok, continue. */
|
||||
if (br > 0)
|
||||
continue;
|
||||
/* If EOF or error return number of bytes read. */
|
||||
if (!br || total)
|
||||
return total;
|
||||
/* Nothing read and error, return error status. */
|
||||
return br;
|
||||
}
|
||||
/* Finally, return the number of bytes read. */
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_pwrite - positioned write to disk
|
||||
* @dev: device to write to
|
||||
* @pos: position in file descriptor to write to
|
||||
* @count: number of bytes to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* This function will write @count bytes from data buffer @b to the device @dev
|
||||
* at position @pos.
|
||||
*
|
||||
* On success, return the number of successfully written bytes. If this number
|
||||
* is lower than @count this means that the write has been interrupted in
|
||||
* flight or that an error was encountered during the write so that the write
|
||||
* is partial. 0 means nothing was written (also return 0 when @count is 0).
|
||||
*
|
||||
* On error and nothing has been written, return -1 with errno set
|
||||
* appropriately to the return code of either seek, write, or set
|
||||
* to EINVAL in case of invalid arguments.
|
||||
*/
|
||||
s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const void *b)
|
||||
{
|
||||
s64 written, total, ret = -1;
|
||||
struct ntfs_device_operations *dops;
|
||||
|
||||
ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
|
||||
|
||||
if (!b || count < 0 || pos < 0) {
|
||||
errno = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dops = dev->d_ops;
|
||||
|
||||
NDevSetDirty(dev);
|
||||
for (total = 0; count; count -= written, total += written) {
|
||||
written = dops->pwrite(dev, (const char*)b + total, count,
|
||||
pos + total);
|
||||
/* If everything ok, continue. */
|
||||
if (written > 0)
|
||||
continue;
|
||||
/*
|
||||
* If nothing written or error return number of bytes written.
|
||||
*/
|
||||
if (!written || total)
|
||||
break;
|
||||
/* Nothing written and error, return error status. */
|
||||
total = written;
|
||||
break;
|
||||
}
|
||||
if (NDevSync(dev) && total && dops->sync(dev)) {
|
||||
total--; /* on sync error, return partially written */
|
||||
}
|
||||
ret = total;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pread - multi sector transfer (mst) positioned read
|
||||
* @dev: device to read from
|
||||
* @pos: position in file descriptor to read from
|
||||
* @count: number of blocks to read
|
||||
* @bksize: size of each block that needs mst deprotecting
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Multi sector transfer (mst) positioned read. This function will read @count
|
||||
* blocks of size @bksize bytes each from device @dev at position @pos into the
|
||||
* the data buffer @b.
|
||||
*
|
||||
* On success, return the number of successfully read blocks. If this number is
|
||||
* lower than @count this means that we have reached end of file, that the read
|
||||
* was interrupted, or that an error was encountered during the read so that
|
||||
* the read is partial. 0 means end of file or nothing was read (also return 0
|
||||
* when @count or @bksize are 0).
|
||||
*
|
||||
* On error and nothing was read, return -1 with errno set appropriately to the
|
||||
* return code of either seek, read, or set to EINVAL in case of invalid
|
||||
* arguments.
|
||||
*
|
||||
* NOTE: If an incomplete multi sector transfer has been detected the magic
|
||||
* will have been changed to magic_BAAD but no error will be returned. Thus it
|
||||
* is possible that we return count blocks as being read but that any number
|
||||
* (between zero and count!) of these blocks is actually subject to a multi
|
||||
* sector transfer error. This should be detected by the caller by checking for
|
||||
* the magic being "BAAD".
|
||||
*/
|
||||
s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b)
|
||||
{
|
||||
s64 br, i;
|
||||
|
||||
if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Do the read. */
|
||||
br = ntfs_pread(dev, pos, count * bksize, b);
|
||||
if (br < 0)
|
||||
return br;
|
||||
/*
|
||||
* Apply fixups to successfully read data, disregarding any errors
|
||||
* returned from the MST fixup function. This is because we want to
|
||||
* fixup everything possible and we rely on the fact that the "BAAD"
|
||||
* magic will be detected later on.
|
||||
*/
|
||||
count = br / bksize;
|
||||
for (i = 0; i < count; ++i)
|
||||
ntfs_mst_post_read_fixup((NTFS_RECORD*)
|
||||
((u8*)b + i * bksize), bksize);
|
||||
/* Finally, return the number of complete blocks read. */
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pwrite - multi sector transfer (mst) positioned write
|
||||
* @dev: device to write to
|
||||
* @pos: position in file descriptor to write to
|
||||
* @count: number of blocks to write
|
||||
* @bksize: size of each block that needs mst protecting
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* Multi sector transfer (mst) positioned write. This function will write
|
||||
* @count blocks of size @bksize bytes each from data buffer @b to the device
|
||||
* @dev at position @pos.
|
||||
*
|
||||
* On success, return the number of successfully written blocks. If this number
|
||||
* is lower than @count this means that the write has been interrupted or that
|
||||
* an error was encountered during the write so that the write is partial. 0
|
||||
* means nothing was written (also return 0 when @count or @bksize are 0).
|
||||
*
|
||||
* On error and nothing has been written, return -1 with errno set
|
||||
* appropriately to the return code of either seek, write, or set
|
||||
* to EINVAL in case of invalid arguments.
|
||||
*
|
||||
* NOTE: We mst protect the data, write it, then mst deprotect it using a quick
|
||||
* deprotect algorithm (no checking). This saves us from making a copy before
|
||||
* the write and at the same time causes the usn to be incremented in the
|
||||
* buffer. This conceptually fits in better with the idea that cached data is
|
||||
* always deprotected and protection is performed when the data is actually
|
||||
* going to hit the disk and the cache is immediately deprotected again
|
||||
* simulating an mst read on the written data. This way cache coherency is
|
||||
* achieved.
|
||||
*/
|
||||
s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
|
||||
const u32 bksize, void *b)
|
||||
{
|
||||
s64 written, i;
|
||||
|
||||
if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
/* Prepare data for writing. */
|
||||
for (i = 0; i < count; ++i) {
|
||||
int err;
|
||||
|
||||
err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
|
||||
((u8*)b + i * bksize), bksize);
|
||||
if (err < 0) {
|
||||
/* Abort write at this position. */
|
||||
if (!i)
|
||||
return err;
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Write the prepared data. */
|
||||
written = ntfs_pwrite(dev, pos, count * bksize, b);
|
||||
/* Quickly deprotect the data again. */
|
||||
for (i = 0; i < count; ++i)
|
||||
ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
|
||||
if (written <= 0)
|
||||
return written;
|
||||
/* Finally, return the number of complete blocks written. */
|
||||
return written / bksize;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_read - read ntfs clusters
|
||||
* @vol: volume to read from
|
||||
* @lcn: starting logical cluster number
|
||||
* @count: number of clusters to read
|
||||
* @b: output data buffer
|
||||
*
|
||||
* Read @count ntfs clusters starting at logical cluster number @lcn from
|
||||
* volume @vol into buffer @b. Return number of clusters read or -1 on error,
|
||||
* with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
|
||||
void *b)
|
||||
{
|
||||
s64 br;
|
||||
|
||||
if (!vol || lcn < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (vol->nr_clusters < lcn + count) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to read outside of volume "
|
||||
"(%lld < %lld)", (long long)vol->nr_clusters,
|
||||
(long long)lcn + count);
|
||||
return -1;
|
||||
}
|
||||
br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits,
|
||||
count << vol->cluster_size_bits, b);
|
||||
if (br < 0) {
|
||||
ntfs_log_perror("Error reading cluster(s)");
|
||||
return br;
|
||||
}
|
||||
return br >> vol->cluster_size_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_write - write ntfs clusters
|
||||
* @vol: volume to write to
|
||||
* @lcn: starting logical cluster number
|
||||
* @count: number of clusters to write
|
||||
* @b: data buffer to write to disk
|
||||
*
|
||||
* Write @count ntfs clusters starting at logical cluster number @lcn from
|
||||
* buffer @b to volume @vol. Return the number of clusters written or -1 on
|
||||
* error, with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
|
||||
const s64 count, const void *b)
|
||||
{
|
||||
s64 bw;
|
||||
|
||||
if (!vol || lcn < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (vol->nr_clusters < lcn + count) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to write outside of volume "
|
||||
"(%lld < %lld)", (long long)vol->nr_clusters,
|
||||
(long long)lcn + count);
|
||||
return -1;
|
||||
}
|
||||
if (!NVolReadOnly(vol))
|
||||
bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits,
|
||||
count << vol->cluster_size_bits, b);
|
||||
else
|
||||
bw = count << vol->cluster_size_bits;
|
||||
if (bw < 0) {
|
||||
ntfs_log_perror("Error writing cluster(s)");
|
||||
return bw;
|
||||
}
|
||||
return bw >> vol->cluster_size_bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_offset_valid - test if a device offset is valid
|
||||
* @dev: open device
|
||||
* @ofs: offset to test for validity
|
||||
*
|
||||
* Test if the offset @ofs is an existing location on the device described
|
||||
* by the open device structure @dev.
|
||||
*
|
||||
* Return 0 if it is valid and -1 if it is not valid.
|
||||
*/
|
||||
static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
|
||||
dev->d_ops->read(dev, &ch, 1) == 1)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_size_get - return the size of a device in blocks
|
||||
* @dev: open device
|
||||
* @block_size: block size in bytes in which to return the result
|
||||
*
|
||||
* Return the number of @block_size sized blocks in the device described by the
|
||||
* open device @dev.
|
||||
*
|
||||
* Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o.
|
||||
*
|
||||
* On error return -1 with errno set to the error code.
|
||||
*/
|
||||
s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size)
|
||||
{
|
||||
s64 high, low;
|
||||
|
||||
if (!dev || block_size <= 0 || (block_size - 1) & block_size) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKGETSIZE64
|
||||
{ u64 size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) {
|
||||
ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n",
|
||||
(unsigned long long)size,
|
||||
(unsigned long long)size);
|
||||
return (s64)size / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef BLKGETSIZE
|
||||
{ unsigned long size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) {
|
||||
ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n",
|
||||
size, size);
|
||||
return (s64)size * 512 / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef FDGETPRM
|
||||
{ struct floppy_struct this_floppy;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) {
|
||||
ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n",
|
||||
(unsigned long)this_floppy.size,
|
||||
(unsigned long)this_floppy.size);
|
||||
return (s64)this_floppy.size * 512 / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef DIOCGMEDIASIZE
|
||||
{
|
||||
/* FreeBSD */
|
||||
off_t size;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) {
|
||||
ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n",
|
||||
(unsigned long long)size,
|
||||
(unsigned long long)size);
|
||||
return (s64)size / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef DKIOCGETBLOCKCOUNT
|
||||
{
|
||||
/* Mac OS X */
|
||||
uint64_t blocks;
|
||||
int sector_size;
|
||||
|
||||
sector_size = ntfs_device_sector_size_get(dev);
|
||||
if (sector_size >= 0 && dev->d_ops->ioctl(dev,
|
||||
DKIOCGETBLOCKCOUNT, &blocks) >= 0)
|
||||
{
|
||||
ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n",
|
||||
(unsigned long long) blocks,
|
||||
(unsigned long long) blocks);
|
||||
return blocks * sector_size / block_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* We couldn't figure it out by using a specialized ioctl,
|
||||
* so do binary search to find the size of the device.
|
||||
*/
|
||||
low = 0LL;
|
||||
for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1)
|
||||
low = high;
|
||||
while (low < high - 1LL) {
|
||||
const s64 mid = (low + high) / 2;
|
||||
|
||||
if (!ntfs_device_offset_valid(dev, mid))
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
dev->d_ops->seek(dev, 0LL, SEEK_SET);
|
||||
return (low + 1LL) / block_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_partition_start_sector_get - get starting sector of a partition
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the starting sector of the partition @dev in the parent
|
||||
* block device of @dev. On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
*/
|
||||
s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n",
|
||||
geo.start, geo.start);
|
||||
return geo.start;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ntfs_device_get_geo(struct ntfs_device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
err = EOPNOTSUPP;
|
||||
#ifdef ENABLE_HD
|
||||
{
|
||||
hd_data_t *hddata;
|
||||
hd_t *hd, *devlist, *partlist = NULL;
|
||||
str_list_t *names;
|
||||
hd_res_t *res;
|
||||
const int d_name_len = strlen(dev->d_name) + 1;
|
||||
int done = 0;
|
||||
|
||||
hddata = calloc(1, sizeof(*hddata));
|
||||
if (!hddata) {
|
||||
err = ENOMEM;
|
||||
goto skip_hd;
|
||||
}
|
||||
/* List all "disk" class devices on the system. */
|
||||
devlist = hd_list(hddata, hw_disk, 1, NULL);
|
||||
if (!devlist) {
|
||||
free(hddata);
|
||||
err = ENOMEM;
|
||||
goto skip_hd;
|
||||
}
|
||||
/*
|
||||
* Loop over each disk device looking for the device with the
|
||||
* same unix name as @dev.
|
||||
*/
|
||||
for (hd = devlist; hd; hd = hd->next) {
|
||||
if (hd->unix_dev_name && !strncmp(dev->d_name,
|
||||
hd->unix_dev_name, d_name_len))
|
||||
goto got_hd;
|
||||
if (hd->unix_dev_name2 && !strncmp(dev->d_name,
|
||||
hd->unix_dev_name2, d_name_len))
|
||||
goto got_hd;
|
||||
for (names = hd->unix_dev_names; names;
|
||||
names = names->next) {
|
||||
if (names->str && !strncmp(dev->d_name,
|
||||
names->str, d_name_len))
|
||||
goto got_hd;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Device was not a whole disk device. Unless it is a file it
|
||||
* is likely to be a partition device. List all "partition"
|
||||
* class devices on the system.
|
||||
*/
|
||||
partlist = hd_list(hddata, hw_partition, 1, NULL);
|
||||
for (hd = partlist; hd; hd = hd->next) {
|
||||
if (hd->unix_dev_name && !strncmp(dev->d_name,
|
||||
hd->unix_dev_name, d_name_len))
|
||||
goto got_part_hd;
|
||||
if (hd->unix_dev_name2 && !strncmp(dev->d_name,
|
||||
hd->unix_dev_name2, d_name_len))
|
||||
goto got_part_hd;
|
||||
for (names = hd->unix_dev_names; names;
|
||||
names = names->next) {
|
||||
if (names->str && !strncmp(dev->d_name,
|
||||
names->str, d_name_len))
|
||||
goto got_part_hd;
|
||||
}
|
||||
}
|
||||
/* Failed to find the device. Stop trying and clean up. */
|
||||
goto end_hd;
|
||||
got_part_hd:
|
||||
/* Get the whole block device the partition device is on. */
|
||||
hd = hd_get_device_by_idx(hddata, hd->attached_to);
|
||||
if (!hd)
|
||||
goto end_hd;
|
||||
got_hd:
|
||||
/*
|
||||
* @hd is now the whole block device either being formatted or
|
||||
* that the partition being formatted is on.
|
||||
*
|
||||
* Loop over each resource of the disk device looking for the
|
||||
* BIOS legacy geometry obtained from EDD which is what Windows
|
||||
* needs to boot.
|
||||
*/
|
||||
for (res = hd->res; res; res = res->next) {
|
||||
/* geotype 3 is BIOS legacy. */
|
||||
if (res->any.type != res_disk_geo ||
|
||||
res->disk_geo.geotype != 3)
|
||||
continue;
|
||||
dev->d_heads = res->disk_geo.heads;
|
||||
dev->d_sectors_per_track = res->disk_geo.sectors;
|
||||
done = 1;
|
||||
}
|
||||
end_hd:
|
||||
if (partlist)
|
||||
hd_free_hd_list(partlist);
|
||||
hd_free_hd_list(devlist);
|
||||
hd_free_hd_data(hddata);
|
||||
free(hddata);
|
||||
if (done) {
|
||||
ntfs_log_debug("EDD/BIOD legacy heads = %u, sectors "
|
||||
"per track = %u\n", dev->d_heads,
|
||||
dev->d_sectors_per_track);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
skip_hd:
|
||||
#endif
|
||||
#ifdef HDIO_GETGEO
|
||||
{ struct hd_geometry geo;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
|
||||
dev->d_heads = geo.heads;
|
||||
dev->d_sectors_per_track = geo.sectors;
|
||||
ntfs_log_debug("HDIO_GETGEO heads = %u, sectors per "
|
||||
"track = %u\n", dev->d_heads,
|
||||
dev->d_sectors_per_track);
|
||||
return 0;
|
||||
}
|
||||
err = errno;
|
||||
}
|
||||
#endif
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_heads_get - get number of heads of device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the number of heads on the device @dev. On error return
|
||||
* -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
* ENOMEM Not enough memory to complete the request
|
||||
*/
|
||||
int ntfs_device_heads_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (dev->d_heads == -1) {
|
||||
if (ntfs_device_get_geo(dev) == -1)
|
||||
return -1;
|
||||
if (dev->d_heads == -1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return dev->d_heads;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_sectors_per_track_get - get number of sectors per track of device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the number of sectors per track on the device @dev. On
|
||||
* error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support HDIO_GETGEO ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
|
||||
* ENOMEM Not enough memory to complete the request
|
||||
*/
|
||||
int ntfs_device_sectors_per_track_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (dev->d_sectors_per_track == -1) {
|
||||
if (ntfs_device_get_geo(dev) == -1)
|
||||
return -1;
|
||||
if (dev->d_sectors_per_track == -1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return dev->d_sectors_per_track;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_sector_size_get - get sector size of a device
|
||||
* @dev: open device
|
||||
*
|
||||
* On success, return the sector size in bytes of the device @dev.
|
||||
* On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support BLKSSZGET ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting BLKSSZGET
|
||||
*/
|
||||
int ntfs_device_sector_size_get(struct ntfs_device *dev)
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKSSZGET
|
||||
{
|
||||
int sect_size = 0;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, BLKSSZGET, §_size)) {
|
||||
ntfs_log_debug("BLKSSZGET sector size = %d bytes\n",
|
||||
sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#elif defined(DIOCGSECTORSIZE)
|
||||
{
|
||||
/* FreeBSD */
|
||||
size_t sect_size = 0;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, §_size)) {
|
||||
ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n",
|
||||
(int) sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#elif defined(DKIOCGETBLOCKSIZE)
|
||||
{
|
||||
/* Mac OS X */
|
||||
uint32_t sect_size = 0;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) {
|
||||
ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n",
|
||||
(int) sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_block_size_set - set block size of a device
|
||||
* @dev: open device
|
||||
* @block_size: block size to set @dev to
|
||||
*
|
||||
* On success, return 0.
|
||||
* On error return -1 with errno set to the error code.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL Input parameter error
|
||||
* EOPNOTSUPP System does not support BLKBSZSET ioctl
|
||||
* ENOTTY @dev is a file or a device not supporting BLKBSZSET
|
||||
*/
|
||||
int ntfs_device_block_size_set(struct ntfs_device *dev,
|
||||
int block_size __attribute__((unused)))
|
||||
{
|
||||
if (!dev) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef BLKBSZSET
|
||||
{
|
||||
size_t s_block_size = block_size;
|
||||
if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) {
|
||||
ntfs_log_debug("Used BLKBSZSET to set block size to "
|
||||
"%d bytes.\n", block_size);
|
||||
return 0;
|
||||
}
|
||||
/* If not a block device, pretend it was successful. */
|
||||
if (!NDevBlock(dev))
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/* If not a block device, pretend it was successful. */
|
||||
if (!NDevBlock(dev))
|
||||
return 0;
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,519 @@
|
||||
/**
|
||||
* ea.c - Processing of EA's
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2014-2021 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
#include <sys/mkdev.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "layout.h"
|
||||
#include "attrib.h"
|
||||
#include "index.h"
|
||||
#include "dir.h"
|
||||
#include "ea.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
static const char lxdev[] = "$LXDEV";
|
||||
static const char lxmod[] = "$LXMOD";
|
||||
|
||||
|
||||
/*
|
||||
* Create a needed attribute (EA or EA_INFORMATION)
|
||||
*
|
||||
* Returns 0 if successful,
|
||||
* -1 otherwise, with errno indicating why it failed.
|
||||
*/
|
||||
|
||||
static int ntfs_need_ea(ntfs_inode *ni, ATTR_TYPES type, int size, int flags)
|
||||
{
|
||||
u8 dummy;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
if (!ntfs_attr_exist(ni,type, AT_UNNAMED,0)) {
|
||||
if (!(flags & XATTR_REPLACE)) {
|
||||
/*
|
||||
* no needed attribute : add one,
|
||||
* apparently, this does not feed the new value in
|
||||
* Note : NTFS version must be >= 3
|
||||
*/
|
||||
if (ni->vol->major_ver >= 3) {
|
||||
res = ntfs_attr_add(ni, type,
|
||||
AT_UNNAMED,0,&dummy,(s64)size);
|
||||
if (!res) {
|
||||
NInoFileNameSetDirty(ni);
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
} else {
|
||||
errno = EOPNOTSUPP;
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the old EA_INFORMATION or delete the current one,
|
||||
* when EA cannot be updated.
|
||||
*
|
||||
* As this is used in the context of some other error, the caller
|
||||
* is responsible for returning the proper error, and errno is
|
||||
* left unchanged.
|
||||
* Only double errors are logged here.
|
||||
*/
|
||||
|
||||
static void restore_ea_info(ntfs_attr *nai, const EA_INFORMATION *old_ea_info)
|
||||
{
|
||||
s64 written;
|
||||
int olderrno;
|
||||
|
||||
olderrno = errno;
|
||||
if (old_ea_info) {
|
||||
written = ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION),
|
||||
old_ea_info);
|
||||
if ((size_t)written != sizeof(EA_INFORMATION)) {
|
||||
ntfs_log_error("Could not restore the EA_INFORMATION,"
|
||||
" possible inconsistency in inode %lld\n",
|
||||
(long long)nai->ni->mft_no);
|
||||
}
|
||||
} else {
|
||||
if (ntfs_attr_rm(nai)) {
|
||||
ntfs_log_error("Could not delete the EA_INFORMATION,"
|
||||
" possible inconsistency in inode %lld\n",
|
||||
(long long)nai->ni->mft_no);
|
||||
}
|
||||
}
|
||||
errno = olderrno;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update both EA and EA_INFORMATION
|
||||
*/
|
||||
|
||||
static int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size,
|
||||
const EA_INFORMATION *ea_info,
|
||||
const EA_INFORMATION *old_ea_info)
|
||||
{
|
||||
ntfs_attr *na;
|
||||
ntfs_attr *nai;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
|
||||
if (nai) {
|
||||
na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
/*
|
||||
* Set EA_INFORMATION first, it is easier to
|
||||
* restore the old value, if setting EA fails.
|
||||
*/
|
||||
if (ntfs_attr_pwrite(nai, 0, sizeof(EA_INFORMATION),
|
||||
ea_info)
|
||||
!= (s64)sizeof(EA_INFORMATION)) {
|
||||
res = -errno;
|
||||
} else {
|
||||
if (((na->data_size > (s64)size)
|
||||
&& ntfs_attr_truncate(na, size))
|
||||
|| (ntfs_attr_pwrite(na, 0, size, value)
|
||||
!= (s64)size)) {
|
||||
res = -errno;
|
||||
if (old_ea_info)
|
||||
restore_ea_info(nai,
|
||||
old_ea_info);
|
||||
}
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
ntfs_attr_close(nai);
|
||||
} else {
|
||||
res = -errno;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the existing EA
|
||||
*
|
||||
* The EA_INFORMATION is not examined and the consistency of the
|
||||
* existing EA is not checked.
|
||||
*
|
||||
* If successful, the full attribute is returned unchanged
|
||||
* and its size is returned.
|
||||
* If the designated buffer is too small, the needed size is
|
||||
* returned, and the buffer is left unchanged.
|
||||
* If there is an error, a negative value is returned and errno
|
||||
* is set according to the error.
|
||||
*/
|
||||
|
||||
int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
s64 ea_size;
|
||||
void *ea_buf;
|
||||
int res = 0;
|
||||
|
||||
if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) {
|
||||
ea_buf = ntfs_attr_readall(ni, AT_EA, (ntfschar*)NULL, 0,
|
||||
&ea_size);
|
||||
if (ea_buf) {
|
||||
if (value && (ea_size <= (s64)size))
|
||||
memcpy(value, ea_buf, ea_size);
|
||||
free(ea_buf);
|
||||
res = ea_size;
|
||||
} else {
|
||||
ntfs_log_error("Failed to read EA from inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
errno = ENODATA;
|
||||
res = -errno;
|
||||
}
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -errno;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a new EA, and set EA_INFORMATION accordingly
|
||||
*
|
||||
* This is roughly the same as ZwSetEaFile() on Windows, however
|
||||
* the "offset to next" of the last EA should not be cleared.
|
||||
*
|
||||
* Consistency of the new EA is first checked.
|
||||
*
|
||||
* EA_INFORMATION is set first, and it is restored to its former
|
||||
* state if setting EA fails.
|
||||
*
|
||||
* Returns 0 if successful
|
||||
* a negative value if an error occurred.
|
||||
*/
|
||||
|
||||
int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags)
|
||||
{
|
||||
EA_INFORMATION ea_info;
|
||||
EA_INFORMATION *old_ea_info;
|
||||
s64 old_ea_size;
|
||||
int res;
|
||||
size_t offs;
|
||||
size_t nextoffs;
|
||||
BOOL ok;
|
||||
int ea_count;
|
||||
int ea_packed;
|
||||
const EA_ATTR *p_ea;
|
||||
|
||||
res = -1;
|
||||
if (value && (size > 0)) {
|
||||
/* do consistency checks */
|
||||
offs = 0;
|
||||
ok = TRUE;
|
||||
ea_count = 0;
|
||||
ea_packed = 0;
|
||||
nextoffs = 0;
|
||||
while (ok && (offs < size)) {
|
||||
p_ea = (const EA_ATTR*)&value[offs];
|
||||
nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset);
|
||||
/* null offset to next not allowed */
|
||||
ok = (nextoffs > offs)
|
||||
&& (nextoffs <= size)
|
||||
&& !(nextoffs & 3)
|
||||
&& p_ea->name_length
|
||||
/* zero sized value are allowed */
|
||||
&& ((offs + offsetof(EA_ATTR,name)
|
||||
+ p_ea->name_length + 1
|
||||
+ le16_to_cpu(p_ea->value_length))
|
||||
<= nextoffs)
|
||||
&& ((offs + offsetof(EA_ATTR,name)
|
||||
+ p_ea->name_length + 1
|
||||
+ le16_to_cpu(p_ea->value_length))
|
||||
>= (nextoffs - 3))
|
||||
&& !p_ea->name[p_ea->name_length];
|
||||
/* name not checked, as chkdsk accepts any chars */
|
||||
if (ok) {
|
||||
if (p_ea->flags & NEED_EA)
|
||||
ea_count++;
|
||||
/*
|
||||
* Assume ea_packed includes :
|
||||
* 4 bytes for header (flags and lengths)
|
||||
* + name length + 1
|
||||
* + value length
|
||||
*/
|
||||
ea_packed += 5 + p_ea->name_length
|
||||
+ le16_to_cpu(p_ea->value_length);
|
||||
offs = nextoffs;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* EA and REPARSE_POINT compatibility not checked any more,
|
||||
* required by Windows 10, but having both may lead to
|
||||
* problems with earlier versions.
|
||||
*/
|
||||
if (ok) {
|
||||
ea_info.ea_length = cpu_to_le16(ea_packed);
|
||||
ea_info.need_ea_count = cpu_to_le16(ea_count);
|
||||
ea_info.ea_query_length = cpu_to_le32(nextoffs);
|
||||
|
||||
old_ea_size = 0;
|
||||
old_ea_info = NULL;
|
||||
/* Try to save the old EA_INFORMATION */
|
||||
if (ntfs_attr_exist(ni, AT_EA_INFORMATION,
|
||||
AT_UNNAMED, 0)) {
|
||||
old_ea_info = ntfs_attr_readall(ni,
|
||||
AT_EA_INFORMATION,
|
||||
(ntfschar*)NULL, 0, &old_ea_size);
|
||||
}
|
||||
/*
|
||||
* no EA or EA_INFORMATION : add them
|
||||
*/
|
||||
if (!ntfs_need_ea(ni, AT_EA_INFORMATION,
|
||||
sizeof(EA_INFORMATION), flags)
|
||||
&& !ntfs_need_ea(ni, AT_EA, 0, flags)) {
|
||||
res = ntfs_update_ea(ni, value, size,
|
||||
&ea_info, old_ea_info);
|
||||
} else {
|
||||
res = -errno;
|
||||
}
|
||||
if (old_ea_info)
|
||||
free(old_ea_info);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -errno;
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -errno;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the EA (including EA_INFORMATION)
|
||||
*
|
||||
* EA_INFORMATION is removed first, and it is restored to its former
|
||||
* state if removing EA fails.
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_remove_ntfs_ea(ntfs_inode *ni)
|
||||
{
|
||||
EA_INFORMATION *old_ea_info;
|
||||
s64 old_ea_size;
|
||||
int res;
|
||||
ntfs_attr *na;
|
||||
ntfs_attr *nai;
|
||||
|
||||
res = 0;
|
||||
if (ni) {
|
||||
/*
|
||||
* open and delete the EA_INFORMATION and the EA
|
||||
*/
|
||||
nai = ntfs_attr_open(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
|
||||
if (nai) {
|
||||
na = ntfs_attr_open(ni, AT_EA, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
/* Try to save the old EA_INFORMATION */
|
||||
old_ea_info = ntfs_attr_readall(ni,
|
||||
AT_EA_INFORMATION,
|
||||
(ntfschar*)NULL, 0, &old_ea_size);
|
||||
res = ntfs_attr_rm(na);
|
||||
NInoFileNameSetDirty(ni);
|
||||
if (!res) {
|
||||
res = ntfs_attr_rm(nai);
|
||||
if (res && old_ea_info) {
|
||||
/*
|
||||
* Failed to remove the EA, try to
|
||||
* restore the EA_INFORMATION
|
||||
*/
|
||||
restore_ea_info(nai,
|
||||
old_ea_info);
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Failed to remove the"
|
||||
" EA_INFORMATION from inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
free(old_ea_info);
|
||||
ntfs_attr_close(na);
|
||||
} else {
|
||||
/* EA_INFORMATION present, but no EA */
|
||||
res = ntfs_attr_rm(nai);
|
||||
NInoFileNameSetDirty(ni);
|
||||
}
|
||||
ntfs_attr_close(nai);
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for the presence of an EA "$LXDEV" (used by WSL)
|
||||
* and return its value as a device address
|
||||
*
|
||||
* Returns zero if successful
|
||||
* -1 if failed, with errno set
|
||||
*/
|
||||
|
||||
int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp)
|
||||
{
|
||||
const EA_ATTR *p_ea;
|
||||
int bufsize;
|
||||
char *buf;
|
||||
int lth;
|
||||
int res;
|
||||
int offset;
|
||||
int next;
|
||||
BOOL found;
|
||||
struct {
|
||||
le32 major;
|
||||
le32 minor;
|
||||
} device;
|
||||
|
||||
res = -EOPNOTSUPP;
|
||||
bufsize = 256; /* expected to be enough */
|
||||
buf = (char*)malloc(bufsize);
|
||||
if (buf) {
|
||||
lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
|
||||
/* retry if short buf */
|
||||
if (lth > bufsize) {
|
||||
free(buf);
|
||||
bufsize = lth;
|
||||
buf = (char*)malloc(bufsize);
|
||||
if (buf)
|
||||
lth = ntfs_get_ntfs_ea(ni, buf, bufsize);
|
||||
}
|
||||
}
|
||||
if (buf && (lth > 0) && (lth <= bufsize)) {
|
||||
offset = 0;
|
||||
found = FALSE;
|
||||
do {
|
||||
p_ea = (const EA_ATTR*)&buf[offset];
|
||||
next = le32_to_cpu(p_ea->next_entry_offset);
|
||||
found = ((next > (int)(sizeof(lxdev) + sizeof(device)))
|
||||
&& (p_ea->name_length == (sizeof(lxdev) - 1))
|
||||
&& (p_ea->value_length
|
||||
== const_cpu_to_le16(sizeof(device)))
|
||||
&& !memcmp(p_ea->name, lxdev, sizeof(lxdev)));
|
||||
if (!found)
|
||||
offset += next;
|
||||
} while (!found && (next > 0) && (offset < lth));
|
||||
if (found) {
|
||||
/* beware of alignment */
|
||||
memcpy(&device, &p_ea->name[p_ea->name_length + 1],
|
||||
sizeof(device));
|
||||
*rdevp = makedev(le32_to_cpu(device.major),
|
||||
le32_to_cpu(device.minor));
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
return (res);
|
||||
}
|
||||
|
||||
int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t type, dev_t dev)
|
||||
{
|
||||
le32 mode;
|
||||
struct {
|
||||
le32 major;
|
||||
le32 minor;
|
||||
} device;
|
||||
struct EA_WSL {
|
||||
struct EA_LXMOD { /* always inserted */
|
||||
EA_ATTR base;
|
||||
char name[sizeof(lxmod)];
|
||||
char value[sizeof(mode)];
|
||||
char stuff[3 & -(sizeof(lxmod) + sizeof(mode))];
|
||||
} mod;
|
||||
struct EA_LXDEV { /* char or block devices only */
|
||||
EA_ATTR base;
|
||||
char name[sizeof(lxdev)];
|
||||
char value[sizeof(device)];
|
||||
char stuff[3 & -(sizeof(lxdev) + sizeof(device))];
|
||||
} dev;
|
||||
} attr;
|
||||
int len;
|
||||
int res;
|
||||
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
mode = cpu_to_le32((u32)(type | 0644));
|
||||
attr.mod.base.next_entry_offset
|
||||
= const_cpu_to_le32(sizeof(attr.mod));
|
||||
attr.mod.base.flags = 0;
|
||||
attr.mod.base.name_length = sizeof(lxmod) - 1;
|
||||
attr.mod.base.value_length = const_cpu_to_le16(sizeof(mode));
|
||||
memcpy(attr.mod.name, lxmod, sizeof(lxmod));
|
||||
memcpy(attr.mod.value, &mode, sizeof(mode));
|
||||
len = sizeof(attr.mod);
|
||||
|
||||
if (S_ISCHR(type) || S_ISBLK(type)) {
|
||||
device.major = cpu_to_le32(major(dev));
|
||||
device.minor = cpu_to_le32(minor(dev));
|
||||
attr.dev.base.next_entry_offset
|
||||
= const_cpu_to_le32(sizeof(attr.dev));
|
||||
attr.dev.base.flags = 0;
|
||||
attr.dev.base.name_length = sizeof(lxdev) - 1;
|
||||
attr.dev.base.value_length = const_cpu_to_le16(sizeof(device));
|
||||
memcpy(attr.dev.name, lxdev, sizeof(lxdev));
|
||||
memcpy(attr.dev.value, &device, sizeof(device));
|
||||
len += sizeof(attr.dev);
|
||||
}
|
||||
res = ntfs_set_ntfs_ea(ni, (char*)&attr, len, 0);
|
||||
return (res);
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
/**
|
||||
* efs.c - Limited processing of encrypted files
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2009 Martin Bene
|
||||
* Copyright (c) 2009-2010 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_SYSMACROS_H
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "attrib.h"
|
||||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
#include "efs.h"
|
||||
#include "index.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
#include "efs.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
static ntfschar logged_utility_stream_name[] = {
|
||||
const_cpu_to_le16('$'),
|
||||
const_cpu_to_le16('E'),
|
||||
const_cpu_to_le16('F'),
|
||||
const_cpu_to_le16('S'),
|
||||
const_cpu_to_le16(0)
|
||||
} ;
|
||||
|
||||
|
||||
/*
|
||||
* Get the ntfs EFS info into an extended attribute
|
||||
*/
|
||||
|
||||
int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
EFS_ATTR_HEADER *efs_info;
|
||||
s64 attr_size = 0;
|
||||
|
||||
if (ni) {
|
||||
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||
efs_info = (EFS_ATTR_HEADER*)ntfs_attr_readall(ni,
|
||||
AT_LOGGED_UTILITY_STREAM,(ntfschar*)NULL, 0,
|
||||
&attr_size);
|
||||
if (efs_info
|
||||
&& (le32_to_cpu(efs_info->length) == attr_size)) {
|
||||
if (attr_size <= (s64)size) {
|
||||
if (value)
|
||||
memcpy(value,efs_info,attr_size);
|
||||
else {
|
||||
errno = EFAULT;
|
||||
attr_size = 0;
|
||||
}
|
||||
} else
|
||||
if (size) {
|
||||
errno = ERANGE;
|
||||
attr_size = 0;
|
||||
}
|
||||
free (efs_info);
|
||||
} else {
|
||||
if (efs_info) {
|
||||
free(efs_info);
|
||||
ntfs_log_error("Bad efs_info for inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
} else {
|
||||
ntfs_log_error("Could not get efsinfo"
|
||||
" for inode %lld\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
errno = EIO;
|
||||
attr_size = 0;
|
||||
}
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
ntfs_log_trace("Inode %lld is not encrypted\n",
|
||||
(long long)ni->mft_no);
|
||||
}
|
||||
}
|
||||
return (attr_size ? (int)attr_size : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix all encrypted AT_DATA attributes of an inode
|
||||
*
|
||||
* The fix may require making an attribute non resident, which
|
||||
* requires more space in the MFT record, and may cause some
|
||||
* attribute to be expelled and the full record to be reorganized.
|
||||
* When this happens, the search for data attributes has to be
|
||||
* reinitialized.
|
||||
*
|
||||
* Returns zero if successful.
|
||||
* -1 if there is a problem.
|
||||
*/
|
||||
|
||||
static int fixup_loop(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
ntfs_attr *na;
|
||||
ATTR_RECORD *a;
|
||||
BOOL restart;
|
||||
int cnt;
|
||||
int maxcnt;
|
||||
int res = 0;
|
||||
|
||||
maxcnt = 0;
|
||||
do {
|
||||
restart = FALSE;
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_log_error("Failed to get ctx for efs\n");
|
||||
res = -1;
|
||||
}
|
||||
cnt = 0;
|
||||
while (!restart && !res
|
||||
&& !ntfs_attr_lookup(AT_DATA, NULL, 0,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
cnt++;
|
||||
a = ctx->attr;
|
||||
na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA,
|
||||
(ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
|
||||
a->name_length);
|
||||
if (!na) {
|
||||
ntfs_log_error("can't open DATA Attribute\n");
|
||||
res = -1;
|
||||
}
|
||||
if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) {
|
||||
if (!NAttrNonResident(na)
|
||||
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||
/*
|
||||
* ntfs_attr_make_non_resident fails if there
|
||||
* is not enough space in the MFT record.
|
||||
* When this happens, force making non-resident
|
||||
* so that some other attribute is expelled.
|
||||
*/
|
||||
if (ntfs_attr_force_non_resident(na)) {
|
||||
res = -1;
|
||||
} else {
|
||||
/* make sure there is some progress */
|
||||
if (cnt <= maxcnt) {
|
||||
errno = EIO;
|
||||
ntfs_log_error("Multiple failure"
|
||||
" making non resident\n");
|
||||
res = -1;
|
||||
} else {
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
ctx = (ntfs_attr_search_ctx*)NULL;
|
||||
restart = TRUE;
|
||||
maxcnt = cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!restart && !res
|
||||
&& ntfs_efs_fixup_attribute(ctx, na)) {
|
||||
ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
} while (restart && !res);
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the efs data from an extended attribute
|
||||
* Warning : the new data is not checked
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size,
|
||||
int flags)
|
||||
|
||||
{
|
||||
int res;
|
||||
int written;
|
||||
ntfs_attr *na;
|
||||
const EFS_ATTR_HEADER *info_header;
|
||||
|
||||
res = 0;
|
||||
if (ni && value && size) {
|
||||
if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) {
|
||||
if (ni->flags & FILE_ATTR_ENCRYPTED) {
|
||||
ntfs_log_trace("Inode %lld already encrypted\n",
|
||||
(long long)ni->mft_no);
|
||||
errno = EEXIST;
|
||||
} else {
|
||||
/*
|
||||
* Possible problem : if encrypted file was
|
||||
* restored in a compressed directory, it was
|
||||
* restored as compressed.
|
||||
* TODO : decompress first.
|
||||
*/
|
||||
ntfs_log_error("Inode %lld cannot be encrypted and compressed\n",
|
||||
(long long)ni->mft_no);
|
||||
errno = EIO;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
info_header = (const EFS_ATTR_HEADER*)value;
|
||||
/* make sure we get a likely efsinfo */
|
||||
if (le32_to_cpu(info_header->length) != size) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (!ntfs_attr_exist(ni,AT_LOGGED_UTILITY_STREAM,
|
||||
(ntfschar*)NULL,0)) {
|
||||
if (!(flags & XATTR_REPLACE)) {
|
||||
/*
|
||||
* no logged_utility_stream attribute : add one,
|
||||
* apparently, this does not feed the new value in
|
||||
*/
|
||||
res = ntfs_attr_add(ni,AT_LOGGED_UTILITY_STREAM,
|
||||
logged_utility_stream_name,4,
|
||||
(u8*)NULL,(s64)size);
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
errno = EEXIST;
|
||||
res = -1;
|
||||
}
|
||||
if (!res) {
|
||||
/*
|
||||
* open and update the existing efs data
|
||||
*/
|
||||
na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM,
|
||||
logged_utility_stream_name, 4);
|
||||
if (na) {
|
||||
/* resize attribute */
|
||||
res = ntfs_attr_truncate(na, (s64)size);
|
||||
/* overwrite value if any */
|
||||
if (!res && value) {
|
||||
written = (int)ntfs_attr_pwrite(na,
|
||||
(s64)0, (s64)size, value);
|
||||
if (written != (s64)size) {
|
||||
ntfs_log_error("Failed to "
|
||||
"update efs data\n");
|
||||
errno = EIO;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
} else
|
||||
res = -1;
|
||||
}
|
||||
if (!res) {
|
||||
/* Don't handle AT_DATA Attribute(s) if inode is a directory */
|
||||
if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
|
||||
/* iterate over AT_DATA attributes */
|
||||
/* set encrypted flag, truncate attribute to match padding bytes */
|
||||
|
||||
if (fixup_loop(ni))
|
||||
return -1;
|
||||
}
|
||||
ni->flags |= FILE_ATTR_ENCRYPTED;
|
||||
NInoSetDirty(ni);
|
||||
NInoFileNameSetDirty(ni);
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixup raw encrypted AT_DATA Attribute
|
||||
* read padding length from last two bytes
|
||||
* truncate attribute, make non-resident,
|
||||
* set data size to match padding length
|
||||
* set ATTR_IS_ENCRYPTED flag on attribute
|
||||
*
|
||||
* Return 0 if successful
|
||||
* -1 if failed (errno tells why)
|
||||
*/
|
||||
|
||||
int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na)
|
||||
{
|
||||
s64 newsize;
|
||||
s64 oldsize;
|
||||
le16 appended_bytes;
|
||||
u16 padding_length;
|
||||
ntfs_inode *ni;
|
||||
BOOL close_ctx = FALSE;
|
||||
|
||||
if (!na) {
|
||||
ntfs_log_error("no na specified for efs_fixup_attribute\n");
|
||||
goto err_out;
|
||||
}
|
||||
if (!ctx) {
|
||||
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
|
||||
if (!ctx) {
|
||||
ntfs_log_error("Failed to get ctx for efs\n");
|
||||
goto err_out;
|
||||
}
|
||||
close_ctx = TRUE;
|
||||
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
||||
goto err_out;
|
||||
}
|
||||
} else {
|
||||
if (!NAttrNonResident(na)) {
|
||||
ntfs_log_error("Cannot make non resident"
|
||||
" when a context has been allocated\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* no extra bytes are added to void attributes */
|
||||
oldsize = na->data_size;
|
||||
if (oldsize) {
|
||||
/* make sure size is valid for a raw encrypted stream */
|
||||
if ((oldsize & 511) != 2) {
|
||||
ntfs_log_error("Bad raw encrypted stream\n");
|
||||
goto err_out;
|
||||
}
|
||||
/* read padding length from last two bytes of attribute */
|
||||
if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) {
|
||||
ntfs_log_error("Error reading padding length\n");
|
||||
goto err_out;
|
||||
}
|
||||
padding_length = le16_to_cpu(appended_bytes);
|
||||
if (padding_length > 511 || padding_length > na->data_size-2) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_error("invalid padding length %d for data_size %lld\n",
|
||||
padding_length, (long long)oldsize);
|
||||
goto err_out;
|
||||
}
|
||||
newsize = oldsize - padding_length - 2;
|
||||
/*
|
||||
* truncate attribute to possibly free clusters allocated
|
||||
* for the last two bytes, but do not truncate to new size
|
||||
* to avoid losing useful data
|
||||
*/
|
||||
if (ntfs_attr_truncate(na, oldsize - 2)) {
|
||||
ntfs_log_error("Error truncating attribute\n");
|
||||
goto err_out;
|
||||
}
|
||||
} else
|
||||
newsize = 0;
|
||||
|
||||
/*
|
||||
* Encrypted AT_DATA Attributes MUST be non-resident
|
||||
* This has to be done after the attribute is resized, as
|
||||
* resizing down to zero may cause the attribute to be made
|
||||
* resident.
|
||||
*/
|
||||
if (!NAttrNonResident(na)
|
||||
&& ntfs_attr_make_non_resident(na, ctx)) {
|
||||
if (!close_ctx
|
||||
|| ntfs_attr_force_non_resident(na)) {
|
||||
ntfs_log_error("Error making DATA attribute non-resident\n");
|
||||
goto err_out;
|
||||
} else {
|
||||
/*
|
||||
* must reinitialize context after forcing
|
||||
* non-resident. We need a context for updating
|
||||
* the state, and at this point, we are sure
|
||||
* the context is not used elsewhere.
|
||||
*/
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n");
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
ni = na->ni;
|
||||
if (!na->name_len) {
|
||||
ni->data_size = newsize;
|
||||
ni->allocated_size = na->allocated_size;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
NInoFileNameSetDirty(ni);
|
||||
|
||||
ctx->attr->data_size = cpu_to_sle64(newsize);
|
||||
if (sle64_to_cpu(ctx->attr->initialized_size) > newsize)
|
||||
ctx->attr->initialized_size = ctx->attr->data_size;
|
||||
ctx->attr->flags |= ATTR_IS_ENCRYPTED;
|
||||
if (close_ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
|
||||
return (0);
|
||||
err_out:
|
||||
if (close_ctx && ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return (-1);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,423 @@
|
||||
/**
|
||||
* ioctl.c - Processing of ioctls
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2014-2019 Jean-Pierre Andre
|
||||
* Copyright (c) 2014 Red Hat, Inc.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#include <syslog.h>
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_MKDEV
|
||||
#include <sys/mkdev.h>
|
||||
#endif
|
||||
#ifdef MAJOR_IN_SYSMACROS
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
#include "compat.h"
|
||||
#include "debug.h"
|
||||
#include "bitmap.h"
|
||||
#include "attrib.h"
|
||||
#include "inode.h"
|
||||
#include "layout.h"
|
||||
#include "volume.h"
|
||||
#include "index.h"
|
||||
#include "logging.h"
|
||||
#include "ntfstime.h"
|
||||
#include "unistr.h"
|
||||
#include "dir.h"
|
||||
#include "security.h"
|
||||
#include "ioctl.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if defined(FITRIM) && defined(BLKDISCARD)
|
||||
|
||||
/* Issue a TRIM request to the underlying device for the given clusters. */
|
||||
static int fstrim_clusters(ntfs_volume *vol, LCN lcn, s64 length)
|
||||
{
|
||||
struct ntfs_device *dev = vol->dev;
|
||||
uint64_t range[2];
|
||||
|
||||
ntfs_log_debug("fstrim_clusters: %lld length %lld\n",
|
||||
(long long) lcn, (long long) length);
|
||||
|
||||
range[0] = lcn << vol->cluster_size_bits;
|
||||
range[1] = length << vol->cluster_size_bits;
|
||||
|
||||
if (dev->d_ops->ioctl(dev, BLKDISCARD, range) == -1) {
|
||||
ntfs_log_debug("fstrim_one_cluster: ioctl failed: %m\n");
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_line(const char *path, char *line, size_t max_bytes)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (fp == NULL)
|
||||
return -errno;
|
||||
if (fgets(line, max_bytes, fp) == NULL) {
|
||||
int ret = -EIO; /* fgets doesn't set errno */
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
fclose (fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_u64(const char *path, u64 *n)
|
||||
{
|
||||
char line[64];
|
||||
int ret;
|
||||
|
||||
ret = read_line(path, line, sizeof line);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (sscanf(line, "%" SCNu64, n) != 1)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find discard limits for current backing device.
|
||||
*/
|
||||
static int fstrim_limits(ntfs_volume *vol,
|
||||
u64 *discard_alignment,
|
||||
u64 *discard_granularity,
|
||||
u64 *discard_max_bytes)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char path1[40]; /* holds "/sys/dev/block/%d:%d" */
|
||||
char path2[40 + sizeof(path1)]; /* less than 40 bytes more than path1 */
|
||||
int ret;
|
||||
|
||||
/* Stat the backing device. Caller has ensured it is a block device. */
|
||||
if (stat(vol->dev->d_name, &statbuf) == -1) {
|
||||
ntfs_log_debug("fstrim_limits: could not stat %s\n",
|
||||
vol->dev->d_name);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* For whole devices,
|
||||
* /sys/dev/block/MAJOR:MINOR/discard_alignment
|
||||
* /sys/dev/block/MAJOR:MINOR/queue/discard_granularity
|
||||
* /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes
|
||||
* will exist.
|
||||
* For partitions, we also need to check the parent device:
|
||||
* /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity
|
||||
* /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes
|
||||
*/
|
||||
snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d",
|
||||
major(statbuf.st_rdev), minor(statbuf.st_rdev));
|
||||
|
||||
snprintf(path2, sizeof path2, "%s/discard_alignment", path1);
|
||||
ret = read_u64(path2, discard_alignment);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
else
|
||||
/* We would expect this file to exist on all
|
||||
* modern kernels. But for the sake of very
|
||||
* old kernels:
|
||||
*/
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1);
|
||||
ret = read_u64(path2, discard_granularity);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
else {
|
||||
snprintf(path2, sizeof path2,
|
||||
"%s/../queue/discard_granularity", path1);
|
||||
ret = read_u64(path2, discard_granularity);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
else
|
||||
goto not_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1);
|
||||
ret = read_u64(path2, discard_max_bytes);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
else {
|
||||
snprintf(path2, sizeof path2,
|
||||
"%s/../queue/discard_max_bytes", path1);
|
||||
ret = read_u64(path2, discard_max_bytes);
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return ret;
|
||||
else
|
||||
goto not_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
not_found:
|
||||
/* If we reach here then we didn't find the device. This is
|
||||
* not an error, but set discard_max_bytes = 0 to indicate
|
||||
* that discard is not available.
|
||||
*/
|
||||
*discard_alignment = 0;
|
||||
*discard_granularity = 0;
|
||||
*discard_max_bytes = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline LCN align_up(ntfs_volume *vol, LCN lcn, u64 granularity)
|
||||
{
|
||||
u64 aligned;
|
||||
|
||||
aligned = (lcn << vol->cluster_size_bits) + granularity - 1;
|
||||
aligned -= aligned % granularity;
|
||||
return (aligned >> vol->cluster_size_bits);
|
||||
}
|
||||
|
||||
static inline u64 align_down(ntfs_volume *vol, u64 count, u64 granularity)
|
||||
{
|
||||
u64 aligned;
|
||||
|
||||
aligned = count << vol->cluster_size_bits;
|
||||
aligned -= aligned % granularity;
|
||||
return (aligned >> vol->cluster_size_bits);
|
||||
}
|
||||
|
||||
#define FSTRIM_BUFSIZ 4096
|
||||
|
||||
/* Trim the filesystem.
|
||||
*
|
||||
* Free blocks between 'start' and 'start+len-1' (both byte offsets)
|
||||
* are found and TRIM requests are sent to the block device. 'minlen'
|
||||
* is the minimum continguous free range to discard.
|
||||
*/
|
||||
static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed)
|
||||
{
|
||||
struct fstrim_range *range = data;
|
||||
u64 start = range->start;
|
||||
u64 len = range->len;
|
||||
u64 minlen = range->minlen;
|
||||
u64 discard_alignment, discard_granularity, discard_max_bytes;
|
||||
u8 *buf = NULL;
|
||||
LCN start_buf;
|
||||
int ret;
|
||||
|
||||
ntfs_log_debug("fstrim: start=%llu len=%llu minlen=%llu\n",
|
||||
(unsigned long long) start,
|
||||
(unsigned long long) len,
|
||||
(unsigned long long) minlen);
|
||||
|
||||
*trimmed = 0;
|
||||
|
||||
/* Fail if user tries to use the fstrim -o/-l/-m options.
|
||||
* XXX We could fix these limitations in future.
|
||||
*/
|
||||
if (start != 0 || len != (uint64_t)-1) {
|
||||
ntfs_log_error("fstrim: setting start or length is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (minlen > vol->cluster_size) {
|
||||
ntfs_log_error("fstrim: minlen > cluster size is not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only block devices are supported. It would be possible to
|
||||
* support backing files (ie. without using loop) but the
|
||||
* ioctls used to punch holes in files are completely
|
||||
* different.
|
||||
*/
|
||||
if (!NDevBlock(vol->dev)) {
|
||||
ntfs_log_error("fstrim: not supported for non-block-device\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ret = fstrim_limits(vol, &discard_alignment,
|
||||
&discard_granularity, &discard_max_bytes);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (discard_alignment != 0) {
|
||||
ntfs_log_error("fstrim: backing device is not aligned for discards\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (discard_max_bytes == 0) {
|
||||
ntfs_log_error("fstrim: backing device does not support discard (discard_max_bytes == 0)\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Sync the device before doing anything. */
|
||||
ret = ntfs_device_sync(vol->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read through the bitmap. */
|
||||
buf = ntfs_malloc(FSTRIM_BUFSIZ);
|
||||
if (buf == NULL)
|
||||
return -errno;
|
||||
for (start_buf = 0; start_buf < vol->nr_clusters;
|
||||
start_buf += FSTRIM_BUFSIZ * 8) {
|
||||
s64 count;
|
||||
s64 br;
|
||||
LCN end_buf, start_lcn;
|
||||
|
||||
/* start_buf is LCN of first cluster in the current buffer.
|
||||
* end_buf is LCN of last cluster + 1 in the current buffer.
|
||||
*/
|
||||
end_buf = start_buf + FSTRIM_BUFSIZ*8;
|
||||
if (end_buf > vol->nr_clusters)
|
||||
end_buf = vol->nr_clusters;
|
||||
count = (end_buf - start_buf) / 8;
|
||||
|
||||
br = ntfs_attr_pread(vol->lcnbmp_na, start_buf/8, count, buf);
|
||||
if (br != count) {
|
||||
if (br >= 0)
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = -errno;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
/* Trim the clusters in large as possible blocks, but
|
||||
* not larger than discard_max_bytes, and compatible
|
||||
* with the supported trim granularity.
|
||||
*/
|
||||
for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) {
|
||||
if (!ntfs_bit_get(buf, start_lcn-start_buf)) {
|
||||
LCN end_lcn;
|
||||
LCN aligned_lcn;
|
||||
u64 aligned_count;
|
||||
|
||||
/* Cluster 'start_lcn' is not in use,
|
||||
* find end of this run.
|
||||
*/
|
||||
end_lcn = start_lcn+1;
|
||||
while (end_lcn < end_buf &&
|
||||
(u64) (end_lcn-start_lcn) << vol->cluster_size_bits
|
||||
< discard_max_bytes &&
|
||||
!ntfs_bit_get(buf, end_lcn-start_buf))
|
||||
end_lcn++;
|
||||
aligned_lcn = align_up(vol, start_lcn,
|
||||
discard_granularity);
|
||||
if (aligned_lcn >= end_lcn)
|
||||
aligned_count = 0;
|
||||
else {
|
||||
aligned_count =
|
||||
align_down(vol,
|
||||
end_lcn - aligned_lcn,
|
||||
discard_granularity);
|
||||
}
|
||||
if (aligned_count) {
|
||||
ret = fstrim_clusters(vol,
|
||||
aligned_lcn, aligned_count);
|
||||
if (ret)
|
||||
goto free_out;
|
||||
|
||||
*trimmed += aligned_count
|
||||
<< vol->cluster_size_bits;
|
||||
}
|
||||
start_lcn = end_lcn-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
free_out:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* FITRIM && BLKDISCARD */
|
||||
|
||||
int ntfs_ioctl(ntfs_inode *ni, unsigned long cmd,
|
||||
void *arg __attribute__((unused)),
|
||||
unsigned int flags __attribute__((unused)), void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
#if defined(FITRIM) && defined(BLKDISCARD)
|
||||
case FITRIM:
|
||||
if (!ni || !data)
|
||||
ret = -EINVAL;
|
||||
else {
|
||||
u64 trimmed;
|
||||
struct fstrim_range *range = (struct fstrim_range*)data;
|
||||
|
||||
ret = fstrim(ni->vol, data, &trimmed);
|
||||
range->len = trimmed;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
#ifdef _MSC_VER
|
||||
#pragma message("ntfs-3g: trimming not supported (FITRIM or BLKDISCARD not defined)")
|
||||
#else
|
||||
#warning Trimming not supported : FITRIM or BLKDISCARD not defined
|
||||
#endif
|
||||
#endif
|
||||
default :
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
@@ -0,0 +1,774 @@
|
||||
/**
|
||||
* lcnalloc.c - Cluster (de)allocation code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2008-2009 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "attrib.h"
|
||||
#include "bitmap.h"
|
||||
#include "debug.h"
|
||||
#include "runlist.h"
|
||||
#include "volume.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Plenty possibilities for big optimizations all over in the cluster
|
||||
* allocation, however at the moment the dominant bottleneck (~ 90%) is
|
||||
* the update of the mapping pairs which converges to the cubic Faulhaber's
|
||||
* formula as the function of the number of extents (fragments, runs).
|
||||
*/
|
||||
#define NTFS_LCNALLOC_BSIZE 4096
|
||||
#define NTFS_LCNALLOC_SKIP NTFS_LCNALLOC_BSIZE
|
||||
|
||||
enum {
|
||||
ZONE_MFT = 1,
|
||||
ZONE_DATA1 = 2,
|
||||
ZONE_DATA2 = 4
|
||||
} ;
|
||||
|
||||
static void ntfs_cluster_set_zone_pos(LCN start, LCN end, LCN *pos, LCN tc)
|
||||
{
|
||||
ntfs_log_trace("pos: %lld tc: %lld\n", (long long)*pos, (long long)tc);
|
||||
|
||||
if (tc >= end)
|
||||
*pos = start;
|
||||
else if (tc >= start)
|
||||
*pos = tc;
|
||||
}
|
||||
|
||||
static void ntfs_cluster_update_zone_pos(ntfs_volume *vol, u8 zone, LCN tc)
|
||||
{
|
||||
ntfs_log_trace("tc = %lld, zone = %d\n", (long long)tc, zone);
|
||||
|
||||
if (zone == ZONE_MFT)
|
||||
ntfs_cluster_set_zone_pos(vol->mft_lcn, vol->mft_zone_end,
|
||||
&vol->mft_zone_pos, tc);
|
||||
else if (zone == ZONE_DATA1)
|
||||
ntfs_cluster_set_zone_pos(vol->mft_zone_end, vol->nr_clusters,
|
||||
&vol->data1_zone_pos, tc);
|
||||
else /* zone == ZONE_DATA2 */
|
||||
ntfs_cluster_set_zone_pos(0, vol->mft_zone_start,
|
||||
&vol->data2_zone_pos, tc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmark full zones when a cluster has been freed in a full zone
|
||||
*
|
||||
* Next allocation will reuse the freed cluster
|
||||
*/
|
||||
|
||||
static void update_full_status(ntfs_volume *vol, LCN lcn)
|
||||
{
|
||||
if (lcn >= vol->mft_zone_end) {
|
||||
if (vol->full_zones & ZONE_DATA1) {
|
||||
ntfs_cluster_update_zone_pos(vol, ZONE_DATA1, lcn);
|
||||
vol->full_zones &= ~ZONE_DATA1;
|
||||
}
|
||||
} else
|
||||
if (lcn < vol->mft_zone_start) {
|
||||
if (vol->full_zones & ZONE_DATA2) {
|
||||
ntfs_cluster_update_zone_pos(vol, ZONE_DATA2, lcn);
|
||||
vol->full_zones &= ~ZONE_DATA2;
|
||||
}
|
||||
} else {
|
||||
if (vol->full_zones & ZONE_MFT) {
|
||||
ntfs_cluster_update_zone_pos(vol, ZONE_MFT, lcn);
|
||||
vol->full_zones &= ~ZONE_MFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static s64 max_empty_bit_range(unsigned char *buf, int size)
|
||||
{
|
||||
int i, j, run = 0;
|
||||
int max_range = 0;
|
||||
s64 start_pos = -1;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
i = 0;
|
||||
while (i < size) {
|
||||
switch (*buf) {
|
||||
case 0 :
|
||||
do {
|
||||
buf++;
|
||||
run += 8;
|
||||
i++;
|
||||
} while ((i < size) && !*buf);
|
||||
break;
|
||||
case 255 :
|
||||
if (run > max_range) {
|
||||
max_range = run;
|
||||
start_pos = (s64)i * 8 - run;
|
||||
}
|
||||
run = 0;
|
||||
do {
|
||||
buf++;
|
||||
i++;
|
||||
} while ((i < size) && (*buf == 255));
|
||||
break;
|
||||
default :
|
||||
for (j = 0; j < 8; j++) {
|
||||
|
||||
int bit = *buf & (1 << j);
|
||||
|
||||
if (bit) {
|
||||
if (run > max_range) {
|
||||
max_range = run;
|
||||
start_pos = (s64)i * 8 + (j - run);
|
||||
}
|
||||
run = 0;
|
||||
} else
|
||||
run++;
|
||||
}
|
||||
i++;
|
||||
buf++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (run > max_range)
|
||||
start_pos = (s64)i * 8 - run;
|
||||
|
||||
return start_pos;
|
||||
}
|
||||
|
||||
static int bitmap_writeback(ntfs_volume *vol, s64 pos, s64 size, void *b,
|
||||
u8 *writeback)
|
||||
{
|
||||
s64 written;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
if (!*writeback)
|
||||
return 0;
|
||||
|
||||
*writeback = 0;
|
||||
|
||||
written = ntfs_attr_pwrite(vol->lcnbmp_na, pos, size, b);
|
||||
if (written != size) {
|
||||
if (!written)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Bitmap write error (%lld, %lld)",
|
||||
(long long)pos, (long long)size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_alloc - allocate clusters on an ntfs volume
|
||||
* @vol: mounted ntfs volume on which to allocate the clusters
|
||||
* @start_vcn: vcn to use for the first allocated cluster
|
||||
* @count: number of clusters to allocate
|
||||
* @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
|
||||
* @zone: zone from which to allocate the clusters
|
||||
*
|
||||
* Allocate @count clusters preferably starting at cluster @start_lcn or at the
|
||||
* current allocator position if @start_lcn is -1, on the mounted ntfs volume
|
||||
* @vol. @zone is either DATA_ZONE for allocation of normal clusters and
|
||||
* MFT_ZONE for allocation of clusters for the master file table, i.e. the
|
||||
* $MFT/$DATA attribute.
|
||||
*
|
||||
* On success return a runlist describing the allocated cluster(s).
|
||||
*
|
||||
* On error return NULL with errno set to the error code.
|
||||
*
|
||||
* Notes on the allocation algorithm
|
||||
* =================================
|
||||
*
|
||||
* There are two data zones. First is the area between the end of the mft zone
|
||||
* and the end of the volume, and second is the area between the start of the
|
||||
* volume and the start of the mft zone. On unmodified/standard NTFS 1.x
|
||||
* volumes, the second data zone doesn't exist due to the mft zone being
|
||||
* expanded to cover the start of the volume in order to reserve space for the
|
||||
* mft bitmap attribute.
|
||||
*
|
||||
* The complexity stems from the need of implementing the mft vs data zoned
|
||||
* approach and from the fact that we have access to the lcn bitmap via up to
|
||||
* NTFS_LCNALLOC_BSIZE bytes at a time, so we need to cope with crossing over
|
||||
* boundaries of two buffers. Further, the fact that the allocator allows for
|
||||
* caller supplied hints as to the location of where allocation should begin
|
||||
* and the fact that the allocator keeps track of where in the data zones the
|
||||
* next natural allocation should occur, contribute to the complexity of the
|
||||
* function. But it should all be worthwhile, because this allocator:
|
||||
* 1) implements MFT zone reservation
|
||||
* 2) causes reduction in fragmentation.
|
||||
* The code is not optimized for speed.
|
||||
*/
|
||||
runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
||||
LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone)
|
||||
{
|
||||
LCN zone_start, zone_end; /* current search range */
|
||||
LCN last_read_pos, lcn;
|
||||
LCN bmp_pos; /* current bit position inside the bitmap */
|
||||
LCN prev_lcn = 0, prev_run_len = 0;
|
||||
s64 clusters, br;
|
||||
runlist *rl = NULL, *trl;
|
||||
u8 *buf, *byte, bit, writeback;
|
||||
u8 pass = 1; /* 1: inside zone; 2: start of zone */
|
||||
u8 search_zone; /* 4: data2 (start) 1: mft (middle) 2: data1 (end) */
|
||||
u8 done_zones = 0;
|
||||
u8 has_guess, used_zone_pos;
|
||||
int err = 0, rlpos, rlsize, buf_size;
|
||||
|
||||
ntfs_log_enter("Entering with count = 0x%llx, start_lcn = 0x%llx, "
|
||||
"zone = %s_ZONE.\n", (long long)count, (long long)
|
||||
start_lcn, zone == MFT_ZONE ? "MFT" : "DATA");
|
||||
|
||||
if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na ||
|
||||
(s8)zone < FIRST_ZONE || zone > LAST_ZONE) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: vcn: %lld, count: %lld, lcn: %lld",
|
||||
__FUNCTION__, (long long)start_vcn,
|
||||
(long long)count, (long long)start_lcn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Return empty runlist if @count == 0 */
|
||||
if (!count) {
|
||||
rl = ntfs_malloc(0x1000);
|
||||
if (rl) {
|
||||
rl[0].vcn = start_vcn;
|
||||
rl[0].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[0].length = 0;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = ntfs_malloc(NTFS_LCNALLOC_BSIZE);
|
||||
if (!buf)
|
||||
goto out;
|
||||
/*
|
||||
* If no @start_lcn was requested, use the current zone
|
||||
* position otherwise use the requested @start_lcn.
|
||||
*/
|
||||
has_guess = 1;
|
||||
zone_start = start_lcn;
|
||||
|
||||
if (zone_start < 0) {
|
||||
if (zone == DATA_ZONE)
|
||||
zone_start = vol->data1_zone_pos;
|
||||
else
|
||||
zone_start = vol->mft_zone_pos;
|
||||
has_guess = 0;
|
||||
}
|
||||
|
||||
used_zone_pos = has_guess ? 0 : 1;
|
||||
|
||||
if (!zone_start || zone_start == vol->mft_zone_start ||
|
||||
zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
|
||||
if (zone_start < vol->mft_zone_start) {
|
||||
zone_end = vol->mft_zone_start;
|
||||
search_zone = ZONE_DATA2;
|
||||
} else if (zone_start < vol->mft_zone_end) {
|
||||
zone_end = vol->mft_zone_end;
|
||||
search_zone = ZONE_MFT;
|
||||
} else {
|
||||
zone_end = vol->nr_clusters;
|
||||
search_zone = ZONE_DATA1;
|
||||
}
|
||||
|
||||
bmp_pos = zone_start;
|
||||
|
||||
/* Loop until all clusters are allocated. */
|
||||
clusters = count;
|
||||
rlpos = rlsize = 0;
|
||||
while (1) {
|
||||
/* check whether we have exhausted the current zone */
|
||||
if (search_zone & vol->full_zones)
|
||||
goto zone_pass_done;
|
||||
last_read_pos = bmp_pos >> 3;
|
||||
br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos,
|
||||
NTFS_LCNALLOC_BSIZE, buf);
|
||||
if (br <= 0) {
|
||||
if (!br)
|
||||
goto zone_pass_done;
|
||||
err = errno;
|
||||
ntfs_log_perror("Reading $BITMAP failed");
|
||||
goto err_ret;
|
||||
}
|
||||
/*
|
||||
* We might have read less than NTFS_LCNALLOC_BSIZE bytes
|
||||
* if we are close to the end of the attribute.
|
||||
*/
|
||||
buf_size = (int)br << 3;
|
||||
lcn = bmp_pos & 7;
|
||||
bmp_pos &= ~7;
|
||||
writeback = 0;
|
||||
|
||||
while (lcn < buf_size) {
|
||||
byte = buf + (lcn >> 3);
|
||||
bit = 1 << (lcn & 7);
|
||||
if (has_guess) {
|
||||
if (*byte & bit) {
|
||||
has_guess = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
lcn = max_empty_bit_range(buf, br);
|
||||
if (lcn < 0)
|
||||
break;
|
||||
has_guess = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* First free bit is at lcn + bmp_pos. */
|
||||
|
||||
/* Reallocate memory if necessary. */
|
||||
if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) {
|
||||
rlsize += 4096;
|
||||
trl = realloc(rl, rlsize);
|
||||
if (!trl) {
|
||||
err = ENOMEM;
|
||||
ntfs_log_perror("realloc() failed");
|
||||
goto wb_err_ret;
|
||||
}
|
||||
rl = trl;
|
||||
}
|
||||
|
||||
/* Allocate the bitmap bit. */
|
||||
*byte |= bit;
|
||||
writeback = 1;
|
||||
if (NVolFreeSpaceKnown(vol)) {
|
||||
if (vol->free_clusters <= 0)
|
||||
ntfs_log_error("Non-positive free"
|
||||
" clusters (%lld)!\n",
|
||||
(long long)vol->free_clusters);
|
||||
else
|
||||
vol->free_clusters--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Coalesce with previous run if adjacent LCNs.
|
||||
* Otherwise, append a new run.
|
||||
*/
|
||||
if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {
|
||||
ntfs_log_debug("Cluster coalesce: prev_lcn: "
|
||||
"%lld lcn: %lld bmp_pos: %lld "
|
||||
"prev_run_len: %lld\n",
|
||||
(long long)prev_lcn,
|
||||
(long long)lcn, (long long)bmp_pos,
|
||||
(long long)prev_run_len);
|
||||
rl[rlpos - 1].length = ++prev_run_len;
|
||||
} else {
|
||||
if (rlpos)
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn +
|
||||
prev_run_len;
|
||||
else {
|
||||
rl[rlpos].vcn = start_vcn;
|
||||
ntfs_log_debug("Start_vcn: %lld\n",
|
||||
(long long)start_vcn);
|
||||
}
|
||||
|
||||
rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
|
||||
rl[rlpos].length = prev_run_len = 1;
|
||||
rlpos++;
|
||||
}
|
||||
|
||||
ntfs_log_debug("RUN: %-16lld %-16lld %-16lld\n",
|
||||
(long long)rl[rlpos - 1].vcn,
|
||||
(long long)rl[rlpos - 1].lcn,
|
||||
(long long)rl[rlpos - 1].length);
|
||||
/* Done? */
|
||||
if (!--clusters) {
|
||||
if (used_zone_pos)
|
||||
ntfs_cluster_update_zone_pos(vol,
|
||||
search_zone, lcn + bmp_pos + 1 +
|
||||
NTFS_LCNALLOC_SKIP);
|
||||
goto done_ret;
|
||||
}
|
||||
|
||||
lcn++;
|
||||
}
|
||||
|
||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) {
|
||||
err = errno;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
if (!used_zone_pos) {
|
||||
|
||||
used_zone_pos = 1;
|
||||
|
||||
if (search_zone == ZONE_MFT)
|
||||
zone_start = vol->mft_zone_pos;
|
||||
else if (search_zone == ZONE_DATA1)
|
||||
zone_start = vol->data1_zone_pos;
|
||||
else
|
||||
zone_start = vol->data2_zone_pos;
|
||||
|
||||
if (!zone_start || zone_start == vol->mft_zone_start ||
|
||||
zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
bmp_pos = zone_start;
|
||||
} else
|
||||
bmp_pos += buf_size;
|
||||
|
||||
if (bmp_pos < zone_end)
|
||||
continue;
|
||||
|
||||
zone_pass_done:
|
||||
ntfs_log_trace("Finished current zone pass(%i).\n", pass);
|
||||
if (pass == 1) {
|
||||
pass = 2;
|
||||
zone_end = zone_start;
|
||||
|
||||
if (search_zone == ZONE_MFT)
|
||||
zone_start = vol->mft_zone_start;
|
||||
else if (search_zone == ZONE_DATA1)
|
||||
zone_start = vol->mft_zone_end;
|
||||
else
|
||||
zone_start = 0;
|
||||
|
||||
/* Sanity check. */
|
||||
if (zone_end < zone_start)
|
||||
zone_end = zone_start;
|
||||
|
||||
bmp_pos = zone_start;
|
||||
|
||||
continue;
|
||||
}
|
||||
/* pass == 2 */
|
||||
done_zones_check:
|
||||
done_zones |= search_zone;
|
||||
vol->full_zones |= search_zone;
|
||||
if (done_zones < (ZONE_MFT + ZONE_DATA1 + ZONE_DATA2)) {
|
||||
ntfs_log_trace("Switching zone.\n");
|
||||
pass = 1;
|
||||
if (rlpos) {
|
||||
LCN tc = rl[rlpos - 1].lcn +
|
||||
rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP;
|
||||
|
||||
if (used_zone_pos)
|
||||
ntfs_cluster_update_zone_pos(vol,
|
||||
search_zone, tc);
|
||||
}
|
||||
|
||||
switch (search_zone) {
|
||||
case ZONE_MFT:
|
||||
ntfs_log_trace("Zone switch: mft -> data1\n");
|
||||
switch_to_data1_zone: search_zone = ZONE_DATA1;
|
||||
zone_start = vol->data1_zone_pos;
|
||||
zone_end = vol->nr_clusters;
|
||||
if (zone_start == vol->mft_zone_end)
|
||||
pass = 2;
|
||||
break;
|
||||
case ZONE_DATA1:
|
||||
ntfs_log_trace("Zone switch: data1 -> data2\n");
|
||||
search_zone = ZONE_DATA2;
|
||||
zone_start = vol->data2_zone_pos;
|
||||
zone_end = vol->mft_zone_start;
|
||||
if (!zone_start)
|
||||
pass = 2;
|
||||
break;
|
||||
case ZONE_DATA2:
|
||||
if (!(done_zones & ZONE_DATA1)) {
|
||||
ntfs_log_trace("data2 -> data1\n");
|
||||
goto switch_to_data1_zone;
|
||||
}
|
||||
ntfs_log_trace("Zone switch: data2 -> mft\n");
|
||||
search_zone = ZONE_MFT;
|
||||
zone_start = vol->mft_zone_pos;
|
||||
zone_end = vol->mft_zone_end;
|
||||
if (zone_start == vol->mft_zone_start)
|
||||
pass = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
bmp_pos = zone_start;
|
||||
|
||||
if (zone_start == zone_end) {
|
||||
ntfs_log_trace("Empty zone, skipped.\n");
|
||||
goto done_zones_check;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ntfs_log_trace("All zones are finished, no space on device.\n");
|
||||
err = ENOSPC;
|
||||
goto err_ret;
|
||||
}
|
||||
done_ret:
|
||||
ntfs_log_debug("At done_ret.\n");
|
||||
/* Add runlist terminator element. */
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[rlpos].length = 0;
|
||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback)) {
|
||||
err = errno;
|
||||
goto err_ret;
|
||||
}
|
||||
done_err_ret:
|
||||
free(buf);
|
||||
if (err) {
|
||||
errno = err;
|
||||
ntfs_log_perror("Failed to allocate clusters");
|
||||
rl = NULL;
|
||||
}
|
||||
out:
|
||||
ntfs_log_leave("\n");
|
||||
return rl;
|
||||
|
||||
wb_err_ret:
|
||||
ntfs_log_trace("At wb_err_ret.\n");
|
||||
if (bitmap_writeback(vol, last_read_pos, br, buf, &writeback))
|
||||
err = errno;
|
||||
err_ret:
|
||||
ntfs_log_trace("At err_ret.\n");
|
||||
if (rl) {
|
||||
/* Add runlist terminator element. */
|
||||
rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
|
||||
rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
|
||||
rl[rlpos].length = 0;
|
||||
ntfs_debug_runlist_dump(rl);
|
||||
ntfs_cluster_free_from_rl(vol, rl);
|
||||
free(rl);
|
||||
rl = NULL;
|
||||
}
|
||||
goto done_err_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_free_from_rl - free clusters from runlist
|
||||
* @vol: mounted ntfs volume on which to free the clusters
|
||||
* @rl: runlist from which deallocate clusters
|
||||
*
|
||||
* On success return 0 and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
||||
{
|
||||
s64 nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
|
||||
for (; rl->length; rl++) {
|
||||
|
||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
||||
(long long)rl->lcn, (long long)rl->length);
|
||||
|
||||
if (rl->lcn >= 0) {
|
||||
update_full_status(vol,rl->lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
|
||||
rl->length)) {
|
||||
ntfs_log_perror("Cluster deallocation failed "
|
||||
"(%lld, %lld)",
|
||||
(long long)rl->lcn,
|
||||
(long long)rl->length);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += rl->length ;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
vol->free_clusters += nr_freed;
|
||||
if (NVolFreeSpaceKnown(vol)
|
||||
&& (vol->free_clusters > vol->nr_clusters))
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic cluster run free
|
||||
* Returns 0 if successful
|
||||
*/
|
||||
|
||||
int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count)
|
||||
{
|
||||
s64 nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
|
||||
(long long)lcn, (long long)count);
|
||||
|
||||
if (lcn >= 0) {
|
||||
update_full_status(vol,lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn,
|
||||
count)) {
|
||||
ntfs_log_perror("Cluster deallocation failed "
|
||||
"(%lld, %lld)",
|
||||
(long long)lcn,
|
||||
(long long)count);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += count;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
vol->free_clusters += nr_freed;
|
||||
if (vol->free_clusters > vol->nr_clusters)
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_cluster_free - free clusters on an ntfs volume
|
||||
* @vol: mounted ntfs volume on which to free the clusters
|
||||
* @na: attribute whose runlist describes the clusters to free
|
||||
* @start_vcn: vcn in @rl at which to start freeing clusters
|
||||
* @count: number of clusters to free or -1 for all clusters
|
||||
*
|
||||
* Free @count clusters starting at the cluster @start_vcn in the runlist
|
||||
* described by the attribute @na from the mounted ntfs volume @vol.
|
||||
*
|
||||
* If @count is -1, all clusters from @start_vcn to the end of the runlist
|
||||
* are deallocated.
|
||||
*
|
||||
* On success return the number of deallocated clusters (not counting sparse
|
||||
* clusters) and on error return -1 with errno set to the error code.
|
||||
*/
|
||||
int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
|
||||
{
|
||||
runlist *rl;
|
||||
s64 delta, to_free, nr_freed = 0;
|
||||
int ret = -1;
|
||||
|
||||
if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 ||
|
||||
(count < 0 && count != -1)) {
|
||||
ntfs_log_trace("Invalid arguments!\n");
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, "
|
||||
"vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no,
|
||||
le32_to_cpu(na->type), (long long)count, (long long)start_vcn);
|
||||
|
||||
rl = ntfs_attr_find_vcn(na, start_vcn);
|
||||
if (!rl) {
|
||||
if (errno == ENOENT)
|
||||
ret = 0;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Unexpected lcn (%lld)", __FUNCTION__,
|
||||
(long long)rl->lcn);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* Find the starting cluster inside the run that needs freeing. */
|
||||
delta = start_vcn - rl->vcn;
|
||||
|
||||
/* The number of clusters in this run that need freeing. */
|
||||
to_free = rl->length - delta;
|
||||
if (count >= 0 && to_free > count)
|
||||
to_free = count;
|
||||
|
||||
if (rl->lcn != LCN_HOLE) {
|
||||
/* Do the actual freeing of the clusters in this run. */
|
||||
update_full_status(vol,rl->lcn + delta);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta,
|
||||
to_free))
|
||||
goto leave;
|
||||
nr_freed = to_free;
|
||||
}
|
||||
|
||||
/* Go to the next run and adjust the number of clusters left to free. */
|
||||
++rl;
|
||||
if (count >= 0)
|
||||
count -= to_free;
|
||||
|
||||
/*
|
||||
* Loop over the remaining runs, using @count as a capping value, and
|
||||
* free them.
|
||||
*/
|
||||
for (; rl->length && count != 0; ++rl) {
|
||||
// FIXME: Need to try ntfs_attr_map_runlist() for attribute
|
||||
// list support! (AIA)
|
||||
if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Invalid lcn (%lli)",
|
||||
__FUNCTION__, (long long)rl->lcn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The number of clusters in this run that need freeing. */
|
||||
to_free = rl->length;
|
||||
if (count >= 0 && to_free > count)
|
||||
to_free = count;
|
||||
|
||||
if (rl->lcn != LCN_HOLE) {
|
||||
update_full_status(vol,rl->lcn);
|
||||
if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
|
||||
to_free)) {
|
||||
// FIXME: Eeek! We need rollback! (AIA)
|
||||
ntfs_log_perror("%s: Clearing bitmap run failed",
|
||||
__FUNCTION__);
|
||||
goto out;
|
||||
}
|
||||
nr_freed += to_free;
|
||||
}
|
||||
|
||||
if (count >= 0)
|
||||
count -= to_free;
|
||||
}
|
||||
|
||||
if (count != -1 && count != 0) {
|
||||
// FIXME: Eeek! BUG()
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: count still not zero (%lld)", __FUNCTION__,
|
||||
(long long)count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = nr_freed;
|
||||
out:
|
||||
vol->free_clusters += nr_freed ;
|
||||
if (vol->free_clusters > vol->nr_clusters)
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
leave:
|
||||
ntfs_log_leave("\n");
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libntfs-3g
|
||||
Description: NTFS-3G Read/Write Driver Library
|
||||
Version: @PACKAGE_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: @LIBFUSE_LITE_LIBS@ -L${libdir} -lntfs-3g
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
@OUTPUT_FORMAT@
|
||||
GROUP ( @rootlibdir@/libntfs-3g.so )
|
||||
@@ -0,0 +1,765 @@
|
||||
/**
|
||||
* logfile.c - NTFS journal handling. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2009 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 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "attrib.h"
|
||||
#include "debug.h"
|
||||
#include "logfile.h"
|
||||
#include "volume.h"
|
||||
#include "mst.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
/**
|
||||
* ntfs_check_restart_page_header - check the page header for consistency
|
||||
* @rp: restart page header to check
|
||||
* @pos: position in logfile at which the restart page header resides
|
||||
*
|
||||
* Check the restart page header @rp for consistency and return TRUE if it is
|
||||
* consistent and FALSE otherwise.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*/
|
||||
static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
|
||||
{
|
||||
u32 logfile_system_page_size, logfile_log_page_size;
|
||||
u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
|
||||
BOOL have_usa = TRUE;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/*
|
||||
* If the system or log page sizes are smaller than the ntfs block size
|
||||
* or either is not a power of 2 we cannot handle this log file.
|
||||
*/
|
||||
logfile_system_page_size = le32_to_cpu(rp->system_page_size);
|
||||
logfile_log_page_size = le32_to_cpu(rp->log_page_size);
|
||||
if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
|
||||
logfile_log_page_size < NTFS_BLOCK_SIZE ||
|
||||
logfile_system_page_size &
|
||||
(logfile_system_page_size - 1) ||
|
||||
logfile_log_page_size & (logfile_log_page_size - 1)) {
|
||||
ntfs_log_error("$LogFile uses unsupported page size.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* We must be either at !pos (1st restart page) or at pos = system page
|
||||
* size (2nd restart page).
|
||||
*/
|
||||
if (pos && pos != logfile_system_page_size) {
|
||||
ntfs_log_error("Found restart area in incorrect "
|
||||
"position in $LogFile.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* We only know how to handle version 1.1 and 2.0, though
|
||||
* version 2.0 is probably related to cached metadata in
|
||||
* Windows 8, and we will refuse to mount.
|
||||
* Nevertheless, do all the relevant checks before rejecting.
|
||||
*/
|
||||
if (((rp->major_ver != const_cpu_to_sle16(1))
|
||||
|| (rp->minor_ver != const_cpu_to_sle16(1)))
|
||||
&& ((rp->major_ver != const_cpu_to_sle16(2))
|
||||
|| (rp->minor_ver != const_cpu_to_sle16(0)))) {
|
||||
ntfs_log_error("$LogFile version %i.%i is not "
|
||||
"supported.\n (This driver supports version "
|
||||
"1.1 and 2.0 only.)\n",
|
||||
(int)sle16_to_cpu(rp->major_ver),
|
||||
(int)sle16_to_cpu(rp->minor_ver));
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* If chkdsk has been run the restart page may not be protected by an
|
||||
* update sequence array.
|
||||
*/
|
||||
if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
|
||||
have_usa = FALSE;
|
||||
goto skip_usa_checks;
|
||||
}
|
||||
/* Verify the size of the update sequence array. */
|
||||
usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
|
||||
if (usa_count != le16_to_cpu(rp->usa_count)) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent update sequence array count.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Verify the position of the update sequence array. */
|
||||
usa_ofs = le16_to_cpu(rp->usa_ofs);
|
||||
usa_end = usa_ofs + usa_count * sizeof(u16);
|
||||
if (usa_ofs < offsetof(RESTART_PAGE_HEADER, usn) ||
|
||||
usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent update sequence array offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
skip_usa_checks:
|
||||
/*
|
||||
* Verify the position of the restart area. It must be:
|
||||
* - aligned to 8-byte boundary,
|
||||
* - after the update sequence array, and
|
||||
* - within the system page size.
|
||||
*/
|
||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||
if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
|
||||
ra_ofs < offsetof(RESTART_PAGE_HEADER, usn)) ||
|
||||
ra_ofs > logfile_system_page_size) {
|
||||
ntfs_log_error("$LogFile restart page specifies "
|
||||
"inconsistent restart area offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
|
||||
* set.
|
||||
*/
|
||||
if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
|
||||
ntfs_log_error("$LogFile restart page is not modified "
|
||||
"by chkdsk but a chkdsk LSN is specified.\n");
|
||||
return FALSE;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_restart_area - check the restart area for consistency
|
||||
* @rp: restart page whose restart area to check
|
||||
*
|
||||
* Check the restart area of the restart page @rp for consistency and return
|
||||
* TRUE if it is consistent and FALSE otherwise.
|
||||
*
|
||||
* This function assumes that the restart page header has already been
|
||||
* consistency checked.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*/
|
||||
static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
u64 file_size;
|
||||
RESTART_AREA *ra;
|
||||
u16 ra_ofs, ra_len, ca_ofs;
|
||||
u8 fs_bits;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
ra_ofs = le16_to_cpu(rp->restart_area_offset);
|
||||
ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
|
||||
/*
|
||||
* Everything before ra->file_size must be before the first word
|
||||
* protected by an update sequence number. This ensures that it is
|
||||
* safe to access ra->client_array_offset.
|
||||
*/
|
||||
if (ra_ofs + offsetof(RESTART_AREA, file_size) >
|
||||
NTFS_BLOCK_SIZE - sizeof(u16)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent file offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Now that we can access ra->client_array_offset, make sure everything
|
||||
* up to the log client array is before the first word protected by an
|
||||
* update sequence number. This ensures we can access all of the
|
||||
* restart area elements safely. Also, the client array offset must be
|
||||
* aligned to an 8-byte boundary.
|
||||
*/
|
||||
ca_ofs = le16_to_cpu(ra->client_array_offset);
|
||||
if (((ca_ofs + 7) & ~7) != ca_ofs ||
|
||||
ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
|
||||
sizeof(u16))) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent client array offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* The restart area must end within the system page size both when
|
||||
* calculated manually and as specified by ra->restart_area_length.
|
||||
* Also, the calculated length must not exceed the specified length.
|
||||
*/
|
||||
ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
|
||||
sizeof(LOG_CLIENT_RECORD);
|
||||
if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
|
||||
(u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
|
||||
le32_to_cpu(rp->system_page_size) ||
|
||||
ra_len > le16_to_cpu(ra->restart_area_length)) {
|
||||
ntfs_log_error("$LogFile restart area is out of bounds "
|
||||
"of the system page size specified by the "
|
||||
"restart page header and/or the specified "
|
||||
"restart area length is inconsistent.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* The ra->client_free_list and ra->client_in_use_list must be either
|
||||
* LOGFILE_NO_CLIENT or less than ra->log_clients or they are
|
||||
* overflowing the client array.
|
||||
*/
|
||||
if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
|
||||
le16_to_cpu(ra->client_free_list) >=
|
||||
le16_to_cpu(ra->log_clients)) ||
|
||||
(ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||
le16_to_cpu(ra->client_in_use_list) >=
|
||||
le16_to_cpu(ra->log_clients))) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"overflowing client free and/or in use lists.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Check ra->seq_number_bits against ra->file_size for consistency.
|
||||
* We cannot just use ffs() because the file size is not a power of 2.
|
||||
*/
|
||||
file_size = (u64)sle64_to_cpu(ra->file_size);
|
||||
fs_bits = 0;
|
||||
while (file_size) {
|
||||
file_size >>= 1;
|
||||
fs_bits++;
|
||||
}
|
||||
if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent sequence number bits.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* The log record header length must be a multiple of 8. */
|
||||
if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
|
||||
le16_to_cpu(ra->log_record_header_length)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent log record header length.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Ditto for the log page data offset. */
|
||||
if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
|
||||
le16_to_cpu(ra->log_page_data_offset)) {
|
||||
ntfs_log_error("$LogFile restart area specifies "
|
||||
"inconsistent log page data offset.\n");
|
||||
return FALSE;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_log_client_array - check the log client array for consistency
|
||||
* @rp: restart page whose log client array to check
|
||||
*
|
||||
* Check the log client array of the restart page @rp for consistency and
|
||||
* return TRUE if it is consistent and FALSE otherwise.
|
||||
*
|
||||
* This function assumes that the restart page header and the restart area have
|
||||
* already been consistency checked.
|
||||
*
|
||||
* Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
|
||||
* function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
|
||||
* restart page and the page must be multi sector transfer deprotected.
|
||||
*/
|
||||
static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
LOG_CLIENT_RECORD *ca, *cr;
|
||||
u16 nr_clients, idx;
|
||||
BOOL in_free_list, idx_is_first;
|
||||
u32 offset_clients;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* The restart area must be fully within page */
|
||||
if ((le16_to_cpu(rp->restart_area_offset) + sizeof(RESTART_AREA))
|
||||
> le32_to_cpu(rp->system_page_size))
|
||||
goto err_out;
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
offset_clients = le16_to_cpu(rp->restart_area_offset)
|
||||
+ le16_to_cpu(ra->client_array_offset);
|
||||
/* The clients' records must begin within page */
|
||||
if (offset_clients >= le32_to_cpu(rp->system_page_size))
|
||||
goto err_out;
|
||||
ca = (LOG_CLIENT_RECORD*)((u8*)ra +
|
||||
le16_to_cpu(ra->client_array_offset));
|
||||
/*
|
||||
* Check the ra->client_free_list first and then check the
|
||||
* ra->client_in_use_list. Check each of the log client records in
|
||||
* each of the lists and check that the array does not overflow the
|
||||
* ra->log_clients value. Also keep track of the number of records
|
||||
* visited as there cannot be more than ra->log_clients records and
|
||||
* that way we detect eventual loops in within a list.
|
||||
*/
|
||||
nr_clients = le16_to_cpu(ra->log_clients);
|
||||
idx = le16_to_cpu(ra->client_free_list);
|
||||
in_free_list = TRUE;
|
||||
check_list:
|
||||
for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
|
||||
idx = le16_to_cpu(cr->next_client)) {
|
||||
if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
|
||||
goto err_out;
|
||||
/* The client record must be fully within page */
|
||||
if ((offset_clients + (idx + 1)*sizeof(LOG_CLIENT_RECORD))
|
||||
> le32_to_cpu(rp->system_page_size))
|
||||
goto err_out;
|
||||
/* Set @cr to the current log client record. */
|
||||
cr = ca + idx;
|
||||
/* The first log client record must not have a prev_client. */
|
||||
if (idx_is_first) {
|
||||
if (cr->prev_client != LOGFILE_NO_CLIENT)
|
||||
goto err_out;
|
||||
idx_is_first = FALSE;
|
||||
}
|
||||
}
|
||||
/* Switch to and check the in use list if we just did the free list. */
|
||||
if (in_free_list) {
|
||||
in_free_list = FALSE;
|
||||
idx = le16_to_cpu(ra->client_in_use_list);
|
||||
goto check_list;
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
err_out:
|
||||
ntfs_log_error("$LogFile log client array is corrupt.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_and_load_restart_page - check the restart page for consistency
|
||||
* @log_na: opened ntfs attribute for journal $LogFile
|
||||
* @rp: restart page to check
|
||||
* @pos: position in @log_na at which the restart page resides
|
||||
* @wrp: [OUT] copy of the multi sector transfer deprotected restart page
|
||||
* @lsn: [OUT] set to the current logfile lsn on success
|
||||
*
|
||||
* Check the restart page @rp for consistency and return 0 if it is consistent
|
||||
* and errno otherwise. The restart page may have been modified by chkdsk in
|
||||
* which case its magic is CHKD instead of RSTR.
|
||||
*
|
||||
* This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
|
||||
* require the full restart page.
|
||||
*
|
||||
* If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
|
||||
* copy of the complete multi sector transfer deprotected page. On failure,
|
||||
* *@wrp is undefined.
|
||||
*
|
||||
* Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
|
||||
* logfile lsn according to this restart page. On failure, *@lsn is undefined.
|
||||
*
|
||||
* The following error codes are defined:
|
||||
* EINVAL - The restart page is inconsistent.
|
||||
* ENOMEM - Not enough memory to load the restart page.
|
||||
* EIO - Failed to reading from $LogFile.
|
||||
*/
|
||||
static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
|
||||
RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
|
||||
LSN *lsn)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
RESTART_PAGE_HEADER *trp;
|
||||
int err;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* Check the restart page header for consistency. */
|
||||
if (!ntfs_check_restart_page_header(rp, pos)) {
|
||||
/* Error output already done inside the function. */
|
||||
return EINVAL;
|
||||
}
|
||||
/* Check the restart area for consistency. */
|
||||
if (!ntfs_check_restart_area(rp)) {
|
||||
/* Error output already done inside the function. */
|
||||
return EINVAL;
|
||||
}
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
/*
|
||||
* Allocate a buffer to store the whole restart page so we can multi
|
||||
* sector transfer deprotect it.
|
||||
* For safety, make sure this is consistent with the usa_count
|
||||
* and shorter than the full log size
|
||||
*/
|
||||
if ((le32_to_cpu(rp->system_page_size)
|
||||
> (u32)(le16_to_cpu(rp->usa_count) - 1)*NTFS_BLOCK_SIZE)
|
||||
|| (le32_to_cpu(rp->system_page_size)
|
||||
> le64_to_cpu(log_na->data_size)))
|
||||
return (EINVAL);
|
||||
trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
|
||||
if (!trp)
|
||||
return errno;
|
||||
/*
|
||||
* Read the whole of the restart page into the buffer. If it fits
|
||||
* completely inside @rp, just copy it from there. Otherwise read it
|
||||
* from disk.
|
||||
*/
|
||||
if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
|
||||
memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
|
||||
else if (ntfs_attr_pread(log_na, pos,
|
||||
le32_to_cpu(rp->system_page_size), trp) !=
|
||||
le32_to_cpu(rp->system_page_size)) {
|
||||
err = errno;
|
||||
ntfs_log_error("Failed to read whole restart page into the "
|
||||
"buffer.\n");
|
||||
if (err != ENOMEM)
|
||||
err = EIO;
|
||||
goto err_out;
|
||||
}
|
||||
/*
|
||||
* Perform the multi sector transfer deprotection on the buffer if the
|
||||
* restart page is protected.
|
||||
*/
|
||||
if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
|
||||
&& ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
|
||||
le32_to_cpu(rp->system_page_size))) {
|
||||
/*
|
||||
* A multi sector tranfer error was detected. We only need to
|
||||
* abort if the restart page contents exceed the multi sector
|
||||
* transfer fixup of the first sector.
|
||||
*/
|
||||
if (le16_to_cpu(rp->restart_area_offset) +
|
||||
le16_to_cpu(ra->restart_area_length) >
|
||||
NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
|
||||
ntfs_log_error("Multi sector transfer error "
|
||||
"detected in $LogFile restart page.\n");
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the restart page is modified by chkdsk or there are no active
|
||||
* logfile clients, the logfile is consistent. Otherwise, need to
|
||||
* check the log client records for consistency, too.
|
||||
*/
|
||||
err = 0;
|
||||
if (ntfs_is_rstr_record(rp->magic) &&
|
||||
ra->client_in_use_list != LOGFILE_NO_CLIENT) {
|
||||
if (!ntfs_check_log_client_array(trp)) {
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
if (lsn) {
|
||||
if (ntfs_is_rstr_record(rp->magic))
|
||||
*lsn = sle64_to_cpu(ra->current_lsn);
|
||||
else /* if (ntfs_is_chkd_record(rp->magic)) */
|
||||
*lsn = sle64_to_cpu(rp->chkdsk_lsn);
|
||||
}
|
||||
ntfs_log_trace("Done.\n");
|
||||
if (wrp)
|
||||
*wrp = trp;
|
||||
else {
|
||||
err_out:
|
||||
free(trp);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_check_logfile - check in the journal if the volume is consistent
|
||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||
* @rp: [OUT] on success this is a copy of the current restart page
|
||||
*
|
||||
* Check the $LogFile journal for consistency and return TRUE if it is
|
||||
* consistent and FALSE if not. On success, the current restart page is
|
||||
* returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
|
||||
*
|
||||
* At present we only check the two restart pages and ignore the log record
|
||||
* pages.
|
||||
*
|
||||
* Note that the MstProtected flag is not set on the $LogFile inode and hence
|
||||
* when reading pages they are not deprotected. This is because we do not know
|
||||
* if the $LogFile was created on a system with a different page size to ours
|
||||
* yet and mst deprotection would fail if our page size is smaller.
|
||||
*/
|
||||
BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
|
||||
{
|
||||
s64 size, pos;
|
||||
LSN rstr1_lsn, rstr2_lsn;
|
||||
ntfs_volume *vol = log_na->ni->vol;
|
||||
u8 *kaddr = NULL;
|
||||
RESTART_PAGE_HEADER *rstr1_ph = NULL;
|
||||
RESTART_PAGE_HEADER *rstr2_ph = NULL;
|
||||
int log_page_size, err;
|
||||
BOOL logfile_is_empty = TRUE;
|
||||
u8 log_page_bits;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(vol))
|
||||
goto is_empty;
|
||||
size = log_na->data_size;
|
||||
/* Make sure the file doesn't exceed the maximum allowed size. */
|
||||
if (size > (s64)MaxLogFileSize)
|
||||
size = MaxLogFileSize;
|
||||
log_page_size = DefaultLogPageSize;
|
||||
/*
|
||||
* Use generic_ffs() instead of ffs() to enable the compiler to
|
||||
* optimize log_page_size and log_page_bits into constants.
|
||||
*/
|
||||
log_page_bits = ffs(log_page_size) - 1;
|
||||
size &= ~(log_page_size - 1);
|
||||
|
||||
/*
|
||||
* Ensure the log file is big enough to store at least the two restart
|
||||
* pages and the minimum number of log record pages.
|
||||
*/
|
||||
if (size < log_page_size * 2 || (size - log_page_size * 2) >>
|
||||
log_page_bits < MinLogRecordPages) {
|
||||
ntfs_log_error("$LogFile is too small.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* Allocate memory for restart page. */
|
||||
kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
|
||||
if (!kaddr)
|
||||
return FALSE;
|
||||
/*
|
||||
* Read through the file looking for a restart page. Since the restart
|
||||
* page header is at the beginning of a page we only need to search at
|
||||
* what could be the beginning of a page (for each page size) rather
|
||||
* than scanning the whole file byte by byte. If all potential places
|
||||
* contain empty and uninitialized records, the log file can be assumed
|
||||
* to be empty.
|
||||
*/
|
||||
for (pos = 0; pos < size; pos <<= 1) {
|
||||
/*
|
||||
* Read first NTFS_BLOCK_SIZE bytes of potential restart page.
|
||||
*/
|
||||
if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
|
||||
NTFS_BLOCK_SIZE) {
|
||||
ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
|
||||
"bytes of potential restart page.\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-empty block means the logfile is not empty while an
|
||||
* empty block after a non-empty block has been encountered
|
||||
* means we are done.
|
||||
*/
|
||||
if (!ntfs_is_empty_recordp((le32*)kaddr))
|
||||
logfile_is_empty = FALSE;
|
||||
else if (!logfile_is_empty)
|
||||
break;
|
||||
/*
|
||||
* A log record page means there cannot be a restart page after
|
||||
* this so no need to continue searching.
|
||||
*/
|
||||
if (ntfs_is_rcrd_recordp((le32*)kaddr))
|
||||
break;
|
||||
/* If not a (modified by chkdsk) restart page, continue. */
|
||||
if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
|
||||
!ntfs_is_chkd_recordp((le32*)kaddr)) {
|
||||
if (!pos)
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Check the (modified by chkdsk) restart page for consistency
|
||||
* and get a copy of the complete multi sector transfer
|
||||
* deprotected restart page.
|
||||
*/
|
||||
err = ntfs_check_and_load_restart_page(log_na,
|
||||
(RESTART_PAGE_HEADER*)kaddr, pos,
|
||||
!rstr1_ph ? &rstr1_ph : &rstr2_ph,
|
||||
!rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
|
||||
if (!err) {
|
||||
/*
|
||||
* If we have now found the first (modified by chkdsk)
|
||||
* restart page, continue looking for the second one.
|
||||
*/
|
||||
if (!pos) {
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* We have now found the second (modified by chkdsk)
|
||||
* restart page, so we can stop looking.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Error output already done inside the function. Note, we do
|
||||
* not abort if the restart page was invalid as we might still
|
||||
* find a valid one further in the file.
|
||||
*/
|
||||
if (err != EINVAL)
|
||||
goto err_out;
|
||||
/* Continue looking. */
|
||||
if (!pos)
|
||||
pos = NTFS_BLOCK_SIZE >> 1;
|
||||
}
|
||||
if (kaddr) {
|
||||
free(kaddr);
|
||||
kaddr = NULL;
|
||||
}
|
||||
if (logfile_is_empty) {
|
||||
NVolSetLogFileEmpty(vol);
|
||||
is_empty:
|
||||
ntfs_log_trace("Done. ($LogFile is empty.)\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (!rstr1_ph) {
|
||||
if (rstr2_ph)
|
||||
ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
|
||||
ntfs_log_error("Did not find any restart pages in "
|
||||
"$LogFile and it was not empty.\n");
|
||||
return FALSE;
|
||||
}
|
||||
/* If both restart pages were found, use the more recent one. */
|
||||
if (rstr2_ph) {
|
||||
/*
|
||||
* If the second restart area is more recent, switch to it.
|
||||
* Otherwise just throw it away.
|
||||
*/
|
||||
if (rstr2_lsn > rstr1_lsn) {
|
||||
ntfs_log_debug("Using second restart page as it is more "
|
||||
"recent.\n");
|
||||
free(rstr1_ph);
|
||||
rstr1_ph = rstr2_ph;
|
||||
/* rstr1_lsn = rstr2_lsn; */
|
||||
} else {
|
||||
ntfs_log_debug("Using first restart page as it is more "
|
||||
"recent.\n");
|
||||
free(rstr2_ph);
|
||||
}
|
||||
rstr2_ph = NULL;
|
||||
}
|
||||
/* All consistency checks passed. */
|
||||
if (rp)
|
||||
*rp = rstr1_ph;
|
||||
else
|
||||
free(rstr1_ph);
|
||||
ntfs_log_trace("Done.\n");
|
||||
return TRUE;
|
||||
err_out:
|
||||
free(kaddr);
|
||||
free(rstr1_ph);
|
||||
free(rstr2_ph);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_is_logfile_clean - check in the journal if the volume is clean
|
||||
* @log_na: ntfs attribute of loaded journal $LogFile to check
|
||||
* @rp: copy of the current restart page
|
||||
*
|
||||
* Analyze the $LogFile journal and return TRUE if it indicates the volume was
|
||||
* shutdown cleanly and FALSE if not.
|
||||
*
|
||||
* At present we only look at the two restart pages and ignore the log record
|
||||
* pages. This is a little bit crude in that there will be a very small number
|
||||
* of cases where we think that a volume is dirty when in fact it is clean.
|
||||
* This should only affect volumes that have not been shutdown cleanly but did
|
||||
* not have any pending, non-check-pointed i/o, i.e. they were completely idle
|
||||
* at least for the five seconds preceding the unclean shutdown.
|
||||
*
|
||||
* This function assumes that the $LogFile journal has already been consistency
|
||||
* checked by a call to ntfs_check_logfile() and in particular if the $LogFile
|
||||
* is empty this function requires that NVolLogFileEmpty() is true otherwise an
|
||||
* empty volume will be reported as dirty.
|
||||
*/
|
||||
BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
|
||||
{
|
||||
RESTART_AREA *ra;
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
/* An empty $LogFile must have been clean before it got emptied. */
|
||||
if (NVolLogFileEmpty(log_na->ni->vol)) {
|
||||
ntfs_log_trace("$LogFile is empty\n");
|
||||
return TRUE;
|
||||
}
|
||||
if (!rp) {
|
||||
ntfs_log_error("Restart page header is NULL\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (!ntfs_is_rstr_record(rp->magic) &&
|
||||
!ntfs_is_chkd_record(rp->magic)) {
|
||||
ntfs_log_error("Restart page buffer is invalid\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
|
||||
/*
|
||||
* If the $LogFile has active clients, i.e. it is open, and we do not
|
||||
* have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
|
||||
* we assume there was an unclean shutdown.
|
||||
*/
|
||||
if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
|
||||
!(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
|
||||
ntfs_log_error("The disk contains an unclean file system (%d, "
|
||||
"%d).\n", le16_to_cpu(ra->client_in_use_list),
|
||||
le16_to_cpu(ra->flags));
|
||||
return FALSE;
|
||||
}
|
||||
/* $LogFile indicates a clean shutdown. */
|
||||
ntfs_log_trace("$LogFile indicates a clean shutdown\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_empty_logfile - empty the contents of the $LogFile journal
|
||||
* @na: ntfs attribute of journal $LogFile to empty
|
||||
*
|
||||
* Empty the contents of the $LogFile journal @na and return 0 on success and
|
||||
* -1 on error.
|
||||
*
|
||||
* This function assumes that the $LogFile journal has already been consistency
|
||||
* checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
|
||||
* has been used to ensure that the $LogFile is clean.
|
||||
*/
|
||||
int ntfs_empty_logfile(ntfs_attr *na)
|
||||
{
|
||||
s64 pos, count;
|
||||
char buf[NTFS_BUF_SIZE];
|
||||
|
||||
ntfs_log_trace("Entering.\n");
|
||||
|
||||
if (NVolLogFileEmpty(na->ni->vol))
|
||||
return 0;
|
||||
|
||||
if (!NAttrNonResident(na)) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Resident $LogFile $DATA attribute");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buf, -1, NTFS_BUF_SIZE);
|
||||
|
||||
pos = 0;
|
||||
while ((count = na->data_size - pos) > 0) {
|
||||
|
||||
if (count > NTFS_BUF_SIZE)
|
||||
count = NTFS_BUF_SIZE;
|
||||
|
||||
count = ntfs_attr_pwrite(na, pos, count, buf);
|
||||
if (count <= 0) {
|
||||
ntfs_log_perror("Failed to reset $LogFile");
|
||||
if (count != -1)
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
pos += count;
|
||||
}
|
||||
|
||||
NVolSetLogFileEmpty(na->ni->vol);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
/**
|
||||
* logging.c - Centralised logging. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2005 Richard Russon
|
||||
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2010 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef PATH_SEP
|
||||
#define PATH_SEP '/'
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
static int tab;
|
||||
#endif
|
||||
|
||||
/* Some gcc 3.x, 4.[01].X crash with internal compiler error. */
|
||||
#if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1)
|
||||
# define BROKEN_GCC_FORMAT_ATTRIBUTE
|
||||
#else
|
||||
# define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0)))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct ntfs_logging - Control info for the logging system
|
||||
* @levels: Bitfield of logging levels
|
||||
* @flags: Flags which affect the output style
|
||||
* @handler: Function to perform the actual logging
|
||||
*/
|
||||
struct ntfs_logging {
|
||||
u32 levels;
|
||||
u32 flags;
|
||||
ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE;
|
||||
};
|
||||
|
||||
/**
|
||||
* ntfs_log
|
||||
* This struct controls all the logging within the library and tools.
|
||||
*/
|
||||
static struct ntfs_logging ntfs_log = {
|
||||
#ifdef DEBUG
|
||||
NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER |
|
||||
NTFS_LOG_LEVEL_LEAVE |
|
||||
#endif
|
||||
NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING |
|
||||
NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
|
||||
NTFS_LOG_LEVEL_PROGRESS,
|
||||
NTFS_LOG_FLAG_ONLYNAME,
|
||||
#ifdef DEBUG
|
||||
ntfs_log_handler_outerr
|
||||
#else
|
||||
ntfs_log_handler_null
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_levels - Get a list of the current logging levels
|
||||
*
|
||||
* Find out which logging levels are enabled.
|
||||
*
|
||||
* Returns: Log levels in a 32-bit field
|
||||
*/
|
||||
u32 ntfs_log_get_levels(void)
|
||||
{
|
||||
return ntfs_log.levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_set_levels - Enable extra logging levels
|
||||
* @levels: 32-bit field of log levels to set
|
||||
*
|
||||
* Enable one or more logging levels.
|
||||
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Log levels that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_set_levels(u32 levels)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.levels;
|
||||
ntfs_log.levels |= levels;
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_clear_levels - Disable some logging levels
|
||||
* @levels: 32-bit field of log levels to clear
|
||||
*
|
||||
* Disable one or more logging levels.
|
||||
* The logging levels are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Log levels that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_clear_levels(u32 levels)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.levels;
|
||||
ntfs_log.levels &= (~levels);
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_flags - Get a list of logging style flags
|
||||
*
|
||||
* Find out which logging flags are enabled.
|
||||
*
|
||||
* Returns: Logging flags in a 32-bit field
|
||||
*/
|
||||
u32 ntfs_log_get_flags(void)
|
||||
{
|
||||
return ntfs_log.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_set_flags - Enable extra logging style flags
|
||||
* @flags: 32-bit field of logging flags to set
|
||||
*
|
||||
* Enable one or more logging flags.
|
||||
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Logging flags that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_set_flags(u32 flags)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.flags;
|
||||
ntfs_log.flags |= flags;
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_clear_flags - Disable some logging styles
|
||||
* @flags: 32-bit field of logging flags to clear
|
||||
*
|
||||
* Disable one or more logging flags.
|
||||
* The log flags are named: NTFS_LOG_LEVEL_*.
|
||||
*
|
||||
* Returns: Logging flags that were enabled before the call
|
||||
*/
|
||||
u32 ntfs_log_clear_flags(u32 flags)
|
||||
{
|
||||
u32 old;
|
||||
old = ntfs_log.flags;
|
||||
ntfs_log.flags &= (~flags);
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_get_stream - Default output streams for logging levels
|
||||
* @level: Log level
|
||||
*
|
||||
* By default, urgent messages are sent to "stderr".
|
||||
* Other messages are sent to "stdout".
|
||||
*
|
||||
* Returns: "string" Prefix to be used
|
||||
*/
|
||||
static FILE * ntfs_log_get_stream(u32 level)
|
||||
{
|
||||
FILE *stream;
|
||||
|
||||
switch (level) {
|
||||
case NTFS_LOG_LEVEL_INFO:
|
||||
case NTFS_LOG_LEVEL_QUIET:
|
||||
case NTFS_LOG_LEVEL_PROGRESS:
|
||||
case NTFS_LOG_LEVEL_VERBOSE:
|
||||
stream = stdout;
|
||||
break;
|
||||
|
||||
case NTFS_LOG_LEVEL_DEBUG:
|
||||
case NTFS_LOG_LEVEL_TRACE:
|
||||
case NTFS_LOG_LEVEL_ENTER:
|
||||
case NTFS_LOG_LEVEL_LEAVE:
|
||||
case NTFS_LOG_LEVEL_WARNING:
|
||||
case NTFS_LOG_LEVEL_ERROR:
|
||||
case NTFS_LOG_LEVEL_CRITICAL:
|
||||
case NTFS_LOG_LEVEL_PERROR:
|
||||
default:
|
||||
stream = stderr;
|
||||
break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_get_prefix - Default prefixes for logging levels
|
||||
* @level: Log level to be prefixed
|
||||
*
|
||||
* Prefixing the logging output can make it easier to parse.
|
||||
*
|
||||
* Returns: "string" Prefix to be used
|
||||
*/
|
||||
static const char * ntfs_log_get_prefix(u32 level)
|
||||
{
|
||||
const char *prefix;
|
||||
|
||||
switch (level) {
|
||||
case NTFS_LOG_LEVEL_DEBUG:
|
||||
prefix = "DEBUG: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_TRACE:
|
||||
prefix = "TRACE: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_QUIET:
|
||||
prefix = "QUIET: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_INFO:
|
||||
prefix = "INFO: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_VERBOSE:
|
||||
prefix = "VERBOSE: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_PROGRESS:
|
||||
prefix = "PROGRESS: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_WARNING:
|
||||
prefix = "WARNING: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_ERROR:
|
||||
prefix = "ERROR: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_PERROR:
|
||||
prefix = "ERROR: ";
|
||||
break;
|
||||
case NTFS_LOG_LEVEL_CRITICAL:
|
||||
prefix = "CRITICAL: ";
|
||||
break;
|
||||
default:
|
||||
prefix = "";
|
||||
break;
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_set_handler - Provide an alternate logging handler
|
||||
* @handler: function to perform the logging
|
||||
*
|
||||
* This alternate handler will be called for all future logging requests.
|
||||
* If no @handler is specified, logging will revert to the default handler.
|
||||
*/
|
||||
void ntfs_log_set_handler(ntfs_log_handler *handler)
|
||||
{
|
||||
if (handler) {
|
||||
ntfs_log.handler = handler;
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
if (handler == ntfs_log_handler_syslog)
|
||||
openlog("ntfs-3g", LOG_PID, LOG_USER);
|
||||
#endif
|
||||
} else
|
||||
ntfs_log.handler = ntfs_log_handler_null;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_redirect - Pass on the request to the real handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @...: Arguments to be formatted
|
||||
*
|
||||
* This is just a redirector function. The arguments are simply passed to the
|
||||
* main logging handler (as defined in the global logging struct @ntfs_log).
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_redirect(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, ...)
|
||||
{
|
||||
int olderr = errno;
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
if (!(ntfs_log.levels & level)) /* Don't log this message */
|
||||
return 0;
|
||||
|
||||
va_start(args, format);
|
||||
errno = olderr;
|
||||
ret = ntfs_log.handler(function, file, line, level, data, format, args);
|
||||
va_end(args);
|
||||
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_syslog - syslog logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* A simple syslog logging handler. Ignores colors.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
|
||||
#define LOG_LINE_LEN 512
|
||||
|
||||
int ntfs_log_handler_syslog(const char *function __attribute__((unused)),
|
||||
const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)), u32 level,
|
||||
void *data __attribute__((unused)),
|
||||
const char *format, va_list args)
|
||||
{
|
||||
char logbuf[LOG_LINE_LEN];
|
||||
int ret, olderr = errno;
|
||||
|
||||
#ifndef DEBUG
|
||||
if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC)
|
||||
return 1;
|
||||
#endif
|
||||
ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args);
|
||||
if (ret < 0) {
|
||||
vsyslog(LOG_NOTICE, format, args);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) {
|
||||
strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1);
|
||||
strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3));
|
||||
ret = strlen(logbuf);
|
||||
}
|
||||
|
||||
syslog(LOG_NOTICE, "%s", logbuf);
|
||||
out:
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Early logging before the logs are redirected
|
||||
*
|
||||
* (not quite satisfactory : this appears before the ntfs-g banner,
|
||||
* and with a different pid)
|
||||
*/
|
||||
|
||||
void ntfs_log_early_error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
openlog("ntfs-3g", LOG_PID, LOG_USER);
|
||||
ntfs_log_handler_syslog(NULL, NULL, 0,
|
||||
NTFS_LOG_LEVEL_ERROR, NULL,
|
||||
format, args);
|
||||
#else
|
||||
vfprintf(stderr,format,args);
|
||||
#endif
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_fprintf - Basic logging handler
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* A simple logging handler. This is where the log line is finally displayed.
|
||||
* It is more likely that you will want to set the handler to either
|
||||
* ntfs_log_handler_outerr or ntfs_log_handler_stderr.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, nothing will be displayed.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_fprintf(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
int i;
|
||||
#endif
|
||||
int ret = 0;
|
||||
int olderr = errno;
|
||||
FILE *stream;
|
||||
|
||||
if (!data) /* Interpret data as a FILE stream. */
|
||||
return 0; /* If it's NULL, we can't do anything. */
|
||||
stream = (FILE*)data;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (level == NTFS_LOG_LEVEL_LEAVE) {
|
||||
if (tab)
|
||||
tab--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < tab; i++)
|
||||
ret += fprintf(stream, " ");
|
||||
#endif
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
|
||||
(strchr(file, PATH_SEP))) /* Abbreviate the filename */
|
||||
file = strrchr(file, PATH_SEP) + 1;
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
|
||||
ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
|
||||
ret += fprintf(stream, "%s ", file);
|
||||
|
||||
if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
|
||||
ret += fprintf(stream, "(%d) ", line);
|
||||
|
||||
if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
|
||||
(level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER))
|
||||
ret += fprintf(stream, "%s(): ", function);
|
||||
|
||||
ret += vfprintf(stream, format, args);
|
||||
|
||||
if (level & NTFS_LOG_LEVEL_PERROR)
|
||||
ret += fprintf(stream, ": %s\n", strerror(olderr));
|
||||
|
||||
#ifdef DEBUG
|
||||
if (level == NTFS_LOG_LEVEL_ENTER)
|
||||
tab++;
|
||||
#endif
|
||||
fflush(stream);
|
||||
errno = olderr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_null - Null logging handler (no output)
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* This handler produces no output. It provides a way to temporarily disable
|
||||
* logging, without having to change the levels and flags.
|
||||
*
|
||||
* Returns: 0 Message wasn't logged
|
||||
*/
|
||||
int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
|
||||
const char *format __attribute__((unused)), va_list args __attribute__((unused)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_stdout - All logs go to stdout
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message to stdout.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, then stdout will be used.
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_stdout(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = stdout;
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message. The output stream will be determined by the log
|
||||
* level.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, the function ntfs_log_get_stream will be called
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_outerr(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = ntfs_log_get_stream(level);
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_log_handler_stderr - All logs go to stderr
|
||||
* @function: Function in which the log line occurred
|
||||
* @file: File in which the log line occurred
|
||||
* @line: Line number on which the log line occurred
|
||||
* @level: Level at which the line is logged
|
||||
* @data: User specified data, possibly specific to a handler
|
||||
* @format: printf-style formatting string
|
||||
* @args: Arguments to be formatted
|
||||
*
|
||||
* Display a log message to stderr.
|
||||
*
|
||||
* Note: For this handler, @data is a pointer to a FILE output stream.
|
||||
* If @data is NULL, then stdout will be used.
|
||||
*
|
||||
* Note: This function calls ntfs_log_handler_fprintf to do the main work.
|
||||
*
|
||||
* Returns: -1 Error occurred
|
||||
* 0 Message wasn't logged
|
||||
* num Number of output characters
|
||||
*/
|
||||
int ntfs_log_handler_stderr(const char *function, const char *file,
|
||||
int line, u32 level, void *data, const char *format, va_list args)
|
||||
{
|
||||
if (!data)
|
||||
data = stderr;
|
||||
|
||||
return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_log_parse_option - Act upon command line options
|
||||
* @option: Option flag
|
||||
*
|
||||
* Delegate some of the work of parsing the command line. All the options begin
|
||||
* with "--log-". Options cause log levels to be enabled in @ntfs_log (the
|
||||
* global logging structure).
|
||||
*
|
||||
* Note: The "colour" option changes the logging handler.
|
||||
*
|
||||
* Returns: TRUE Option understood
|
||||
* FALSE Invalid log option
|
||||
*/
|
||||
BOOL ntfs_log_parse_option(const char *option)
|
||||
{
|
||||
if (strcmp(option, "--log-debug") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-verbose") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-quiet") == 0) {
|
||||
ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
|
||||
return TRUE;
|
||||
} else if (strcmp(option, "--log-trace") == 0) {
|
||||
ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ntfs_log_debug("Unknown logging option '%s'\n", option);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* misc.c : miscellaneous :
|
||||
* - dealing with errors in memory allocation
|
||||
*
|
||||
* Copyright (c) 2008 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
|
||||
/**
|
||||
* ntfs_calloc
|
||||
*
|
||||
* Return a pointer to the allocated memory or NULL if the request fails.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void *ntfs_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = realloc(ptr, size);
|
||||
if (!p)
|
||||
ntfs_log_perror("Failed to realloc %lld bytes",
|
||||
(long long)size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void ntfs_free(void *p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* mst.c - Multi sector fixup handling code. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2006-2009 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 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_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "mst.h"
|
||||
#include "logging.h"
|
||||
|
||||
/*
|
||||
* Basic validation of a NTFS multi-sector record. The record size must be a
|
||||
* multiple of the logical sector size; and the update sequence array must be
|
||||
* properly aligned, of the expected length, and must end before the last le16
|
||||
* in the first logical sector.
|
||||
*/
|
||||
static BOOL
|
||||
is_valid_record(u32 size, u16 usa_ofs, u16 usa_count)
|
||||
{
|
||||
return size % NTFS_BLOCK_SIZE == 0 &&
|
||||
usa_ofs % 2 == 0 &&
|
||||
usa_count == 1 + (size / NTFS_BLOCK_SIZE) &&
|
||||
usa_ofs + ((u32)usa_count * 2) <= NTFS_BLOCK_SIZE - 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data
|
||||
* @b: pointer to the data to deprotect
|
||||
* @size: size in bytes of @b
|
||||
*
|
||||
* Perform the necessary post read multi sector transfer fixups and detect the
|
||||
* presence of incomplete multi sector transfers. - In that case, overwrite the
|
||||
* magic of the ntfs record header being processed with "BAAD" (in memory only!)
|
||||
* and abort processing.
|
||||
*
|
||||
* Return 0 on success and -1 on error, with errno set to the error code. The
|
||||
* following error codes are defined:
|
||||
* EINVAL Invalid arguments or invalid NTFS record in buffer @b.
|
||||
* EIO Multi sector transfer error was detected. Magic of the NTFS
|
||||
* record in @b will have been set to "BAAD".
|
||||
*/
|
||||
int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size,
|
||||
BOOL warn)
|
||||
{
|
||||
u16 usa_ofs, usa_count, usn;
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Setup the variables. */
|
||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
usa_count = le16_to_cpu(b->usa_count);
|
||||
|
||||
if (!is_valid_record(size, usa_ofs, usa_count)) {
|
||||
errno = EINVAL;
|
||||
if (warn) {
|
||||
ntfs_log_perror("%s: magic: 0x%08lx size: %ld "
|
||||
" usa_ofs: %d usa_count: %u",
|
||||
__FUNCTION__,
|
||||
(long)le32_to_cpu(*(le32 *)b),
|
||||
(long)size, (int)usa_ofs,
|
||||
(unsigned int)usa_count);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||
/*
|
||||
* The update sequence number which has to be equal to each of the
|
||||
* u16 values before they are fixed up. Note no need to care for
|
||||
* endianness since we are comparing and moving data for on disk
|
||||
* structures which means the data is consistent. - If it is
|
||||
* consistency the wrong endianness it doesn't make any difference.
|
||||
*/
|
||||
usn = *usa_pos;
|
||||
/*
|
||||
* Position in protected data of first u16 that needs fixing up.
|
||||
*/
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/*
|
||||
* Check for incomplete multi sector transfer(s).
|
||||
*/
|
||||
while (--usa_count) {
|
||||
if (*data_pos != usn) {
|
||||
/*
|
||||
* Incomplete multi sector transfer detected! )-:
|
||||
* Set the magic to "BAAD" and return failure.
|
||||
* Note that magic_BAAD is already converted to le32.
|
||||
*/
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Incomplete multi-sector transfer: "
|
||||
"magic: 0x%08x size: %d usa_ofs: %d usa_count:"
|
||||
" %d data: %d usn: %d", le32_to_cpu(*(le32 *)b), size,
|
||||
usa_ofs, usa_count, *data_pos, usn);
|
||||
b->magic = magic_BAAD;
|
||||
return -1;
|
||||
}
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
/* Re-setup the variables. */
|
||||
usa_count = le16_to_cpu(b->usa_count);
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
/* Fixup all sectors. */
|
||||
while (--usa_count) {
|
||||
/*
|
||||
* Increment position in usa and restore original data from
|
||||
* the usa into the data buffer.
|
||||
*/
|
||||
*data_pos = *(++usa_pos);
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deprotect multi sector transfer protected data
|
||||
* with a warning if an error is found.
|
||||
*/
|
||||
|
||||
int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
|
||||
{
|
||||
return (ntfs_mst_post_read_fixup_warn(b,size,TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_pre_write_fixup - apply multi sector transfer protection
|
||||
* @b: pointer to the data to protect
|
||||
* @size: size in bytes of @b
|
||||
*
|
||||
* Perform the necessary pre write multi sector transfer fixup on the data
|
||||
* pointer to by @b of @size.
|
||||
*
|
||||
* Return 0 if fixups applied successfully or -1 if no fixups were performed
|
||||
* due to errors. In that case errno i set to the error code (EINVAL).
|
||||
*
|
||||
* NOTE: We consider the absence / invalidity of an update sequence array to
|
||||
* mean error. This means that you have to create a valid update sequence
|
||||
* array header in the ntfs record before calling this function, otherwise it
|
||||
* will fail (the header needs to contain the position of the update sequence
|
||||
* array together with the number of elements in the array). You also need to
|
||||
* initialise the update sequence number before calling this function
|
||||
* otherwise a random word will be used (whatever was in the record at that
|
||||
* position at that time).
|
||||
*/
|
||||
int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size)
|
||||
{
|
||||
u16 usa_ofs, usa_count, usn;
|
||||
le16 le_usn;
|
||||
le16 *usa_pos, *data_pos;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Sanity check + only fixup if it makes sense. */
|
||||
if (!b || ntfs_is_baad_record(b->magic) ||
|
||||
ntfs_is_hole_record(b->magic)) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s: bad argument", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
/* Setup the variables. */
|
||||
usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
usa_count = le16_to_cpu(b->usa_count);
|
||||
|
||||
if (!is_valid_record(size, usa_ofs, usa_count)) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (le16*)((u8*)b + usa_ofs);
|
||||
/*
|
||||
* Cyclically increment the update sequence number
|
||||
* (skipping 0 and -1, i.e. 0xffff).
|
||||
*/
|
||||
usn = le16_to_cpup(usa_pos) + 1;
|
||||
if (usn == 0xffff || !usn)
|
||||
usn = 1;
|
||||
le_usn = cpu_to_le16(usn);
|
||||
*usa_pos = le_usn;
|
||||
/* Position in data of first le16 that needs fixing up. */
|
||||
data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(le16) - 1;
|
||||
/* Fixup all sectors. */
|
||||
while (--usa_count) {
|
||||
/*
|
||||
* Increment the position in the usa and save the
|
||||
* original data from the data buffer into the usa.
|
||||
*/
|
||||
*(++usa_pos) = *data_pos;
|
||||
/* Apply fixup to data. */
|
||||
*data_pos = le_usn;
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(le16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data
|
||||
* @b: pointer to the data to deprotect
|
||||
*
|
||||
* Perform the necessary post write multi sector transfer fixup, not checking
|
||||
* for any errors, because we assume we have just used
|
||||
* ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never
|
||||
* have gotten here.
|
||||
*/
|
||||
void ntfs_mst_post_write_fixup(NTFS_RECORD *b)
|
||||
{
|
||||
u16 *usa_pos, *data_pos;
|
||||
|
||||
u16 usa_ofs = le16_to_cpu(b->usa_ofs);
|
||||
u16 usa_count = le16_to_cpu(b->usa_count);
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
/* Position of usn in update sequence array. */
|
||||
usa_pos = (u16*)b + usa_ofs/sizeof(u16);
|
||||
|
||||
/* Position in protected data of first u16 that needs fixing up. */
|
||||
data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
|
||||
|
||||
/* Fixup all sectors. */
|
||||
while (--usa_count) {
|
||||
/*
|
||||
* Increment position in usa and restore original data from
|
||||
* the usa into the data buffer.
|
||||
*/
|
||||
*data_pos = *(++usa_pos);
|
||||
|
||||
/* Increment position in data as well. */
|
||||
data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,636 @@
|
||||
/**
|
||||
* object_id.c - Processing of object ids
|
||||
*
|
||||
* This module is part of ntfs-3g library
|
||||
*
|
||||
* Copyright (c) 2009-2019 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SYSMACROS_H
|
||||
#include <sys/sysmacros.h>
|
||||
#endif
|
||||
|
||||
#include "compat.h"
|
||||
#include "types.h"
|
||||
#include "debug.h"
|
||||
#include "attrib.h"
|
||||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
#include "volume.h"
|
||||
#include "mft.h"
|
||||
#include "index.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "object_id.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
/*
|
||||
* Endianness considerations
|
||||
*
|
||||
* According to RFC 4122, GUIDs should be printed with the most
|
||||
* significant byte first, and the six fields be compared individually
|
||||
* for ordering. RFC 4122 does not define the internal representation.
|
||||
*
|
||||
* Windows apparently stores the first three fields in little endian
|
||||
* order, and the last two fields in big endian order.
|
||||
*
|
||||
* Here we always copy disk images with no endianness change,
|
||||
* and, for indexing, GUIDs are compared as if they were a sequence
|
||||
* of four little-endian unsigned 32 bit integers (as Windows
|
||||
* does it that way.)
|
||||
*
|
||||
* --------------------- begin from RFC 4122 ----------------------
|
||||
* Consider each field of the UUID to be an unsigned integer as shown
|
||||
* in the table in section Section 4.1.2. Then, to compare a pair of
|
||||
* UUIDs, arithmetically compare the corresponding fields from each
|
||||
* UUID in order of significance and according to their data type.
|
||||
* Two UUIDs are equal if and only if all the corresponding fields
|
||||
* are equal.
|
||||
*
|
||||
* UUIDs, as defined in this document, can also be ordered
|
||||
* lexicographically. For a pair of UUIDs, the first one follows the
|
||||
* second if the most significant field in which the UUIDs differ is
|
||||
* greater for the first UUID. The second precedes the first if the
|
||||
* most significant field in which the UUIDs differ is greater for
|
||||
* the second UUID.
|
||||
*
|
||||
* The fields are encoded as 16 octets, with the sizes and order of the
|
||||
* fields defined above, and with each field encoded with the Most
|
||||
* Significant Byte first (known as network byte order). Note that the
|
||||
* field names, particularly for multiplexed fields, follow historical
|
||||
* practice.
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | time_low |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | time_mid | time_hi_and_version |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |clk_seq_hi_res | clk_seq_low | node (0-1) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | node (2-5) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* ---------------------- end from RFC 4122 -----------------------
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
/* alignment may be needed to evaluate collations */
|
||||
u32 alignment;
|
||||
GUID guid;
|
||||
} object_id;
|
||||
} OBJECT_ID_INDEX_KEY;
|
||||
|
||||
typedef struct {
|
||||
le64 file_id;
|
||||
GUID birth_volume_id;
|
||||
GUID birth_object_id;
|
||||
GUID domain_id;
|
||||
} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA
|
||||
|
||||
struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */
|
||||
INDEX_ENTRY_HEADER header;
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
OBJECT_ID_INDEX_DATA data;
|
||||
} ;
|
||||
|
||||
static ntfschar objid_index_name[] = { const_cpu_to_le16('$'),
|
||||
const_cpu_to_le16('O') };
|
||||
|
||||
/*
|
||||
* Set the index for a new object id
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo,
|
||||
const OBJECT_ID_ATTR *object_id)
|
||||
{
|
||||
struct OBJECT_ID_INDEX indx;
|
||||
u64 file_id_cpu;
|
||||
le64 file_id;
|
||||
le16 seqn;
|
||||
|
||||
seqn = ni->mrec->sequence_number;
|
||||
file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn));
|
||||
file_id = cpu_to_le64(file_id_cpu);
|
||||
indx.header.data_offset = const_cpu_to_le16(
|
||||
sizeof(INDEX_ENTRY_HEADER)
|
||||
+ sizeof(OBJECT_ID_INDEX_KEY));
|
||||
indx.header.data_length = const_cpu_to_le16(
|
||||
sizeof(OBJECT_ID_INDEX_DATA));
|
||||
indx.header.reservedV = const_cpu_to_le32(0);
|
||||
indx.header.length = const_cpu_to_le16(
|
||||
sizeof(struct OBJECT_ID_INDEX));
|
||||
indx.header.key_length = const_cpu_to_le16(
|
||||
sizeof(OBJECT_ID_INDEX_KEY));
|
||||
indx.header.flags = const_cpu_to_le16(0);
|
||||
indx.header.reserved = const_cpu_to_le16(0);
|
||||
|
||||
memcpy(&indx.key.object_id,object_id,sizeof(GUID));
|
||||
|
||||
indx.data.file_id = file_id;
|
||||
memcpy(&indx.data.birth_volume_id,
|
||||
&object_id->birth_volume_id,sizeof(GUID));
|
||||
memcpy(&indx.data.birth_object_id,
|
||||
&object_id->birth_object_id,sizeof(GUID));
|
||||
memcpy(&indx.data.domain_id,
|
||||
&object_id->domain_id,sizeof(GUID));
|
||||
ntfs_index_ctx_reinit(xo);
|
||||
return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx));
|
||||
}
|
||||
|
||||
/*
|
||||
* Open the $Extend/$ObjId file and its index
|
||||
*
|
||||
* Return the index context if opened
|
||||
* or NULL if an error occurred (errno tells why)
|
||||
*
|
||||
* The index has to be freed and inode closed when not needed any more.
|
||||
*/
|
||||
|
||||
static ntfs_index_context *open_object_id_index(ntfs_volume *vol)
|
||||
{
|
||||
u64 inum;
|
||||
ntfs_inode *ni;
|
||||
ntfs_inode *dir_ni;
|
||||
ntfs_index_context *xo;
|
||||
|
||||
/* do not use path_name_to inode - could reopen root */
|
||||
dir_ni = ntfs_inode_open(vol, FILE_Extend);
|
||||
ni = (ntfs_inode*)NULL;
|
||||
if (dir_ni) {
|
||||
inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId");
|
||||
if (inum != (u64)-1)
|
||||
ni = ntfs_inode_open(vol, inum);
|
||||
ntfs_inode_close(dir_ni);
|
||||
}
|
||||
if (ni) {
|
||||
xo = ntfs_index_ctx_get(ni, objid_index_name, 2);
|
||||
if (!xo) {
|
||||
ntfs_inode_close(ni);
|
||||
}
|
||||
} else
|
||||
xo = (ntfs_index_context*)NULL;
|
||||
return (xo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Merge object_id data stored in the index into
|
||||
* a full object_id struct.
|
||||
*
|
||||
* returns 0 if merging successful
|
||||
* -1 if no data could be merged. This is generally not an error
|
||||
*/
|
||||
|
||||
static int merge_index_data(ntfs_inode *ni,
|
||||
const OBJECT_ID_ATTR *objectid_attr,
|
||||
OBJECT_ID_ATTR *full_objectid)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
struct OBJECT_ID_INDEX *entry;
|
||||
ntfs_index_context *xo;
|
||||
ntfs_inode *xoni;
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
memcpy(&key.object_id,objectid_attr,sizeof(GUID));
|
||||
if (!ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
||||
/* make sure inode numbers match */
|
||||
if (entry
|
||||
&& (MREF(le64_to_cpu(entry->data.file_id))
|
||||
== ni->mft_no)) {
|
||||
memcpy(&full_objectid->birth_volume_id,
|
||||
&entry->data.birth_volume_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&full_objectid->birth_object_id,
|
||||
&entry->data.birth_object_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&full_objectid->domain_id,
|
||||
&entry->data.domain_id,
|
||||
sizeof(GUID));
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
xoni = xo->ni;
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove an object id index entry if attribute present
|
||||
*
|
||||
* Returns the size of existing object id
|
||||
* (the existing object_d is returned)
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
|
||||
OBJECT_ID_ATTR *old_attr)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
struct OBJECT_ID_INDEX *entry;
|
||||
s64 size;
|
||||
int ret;
|
||||
|
||||
ret = na->data_size;
|
||||
if (ret) {
|
||||
/* read the existing object id attribute */
|
||||
size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr);
|
||||
if (size >= (s64)sizeof(GUID)) {
|
||||
memcpy(&key.object_id,
|
||||
&old_attr->object_id,sizeof(GUID));
|
||||
if (!ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
|
||||
entry = (struct OBJECT_ID_INDEX*)xo->entry;
|
||||
memcpy(&old_attr->birth_volume_id,
|
||||
&entry->data.birth_volume_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&old_attr->birth_object_id,
|
||||
&entry->data.birth_object_id,
|
||||
sizeof(GUID));
|
||||
memcpy(&old_attr->domain_id,
|
||||
&entry->data.domain_id,
|
||||
sizeof(GUID));
|
||||
if (ntfs_index_rm(xo))
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
ret = -1;
|
||||
errno = ENODATA;
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Update the object id and index
|
||||
*
|
||||
* The object_id attribute should have been created and the
|
||||
* non-duplication of the GUID should have been checked before.
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
* If could not remove the existing index, nothing is done,
|
||||
* If could not write the new data, no index entry is inserted
|
||||
* If failed to insert the index, data is removed
|
||||
*/
|
||||
|
||||
static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo,
|
||||
const OBJECT_ID_ATTR *value, size_t size)
|
||||
{
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
ntfs_attr *na;
|
||||
int oldsize;
|
||||
int written;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
memset(&old_attr, 0, sizeof(OBJECT_ID_ATTR));
|
||||
/* remove the existing index entry */
|
||||
oldsize = remove_object_id_index(na,xo,&old_attr);
|
||||
if (oldsize < 0)
|
||||
res = -1;
|
||||
else {
|
||||
/* resize attribute */
|
||||
res = ntfs_attr_truncate(na, (s64)sizeof(GUID));
|
||||
/* write the object_id in attribute */
|
||||
if (!res && value) {
|
||||
written = (int)ntfs_attr_pwrite(na,
|
||||
(s64)0, (s64)sizeof(GUID),
|
||||
&value->object_id);
|
||||
if (written != (s64)sizeof(GUID)) {
|
||||
ntfs_log_error("Failed to update "
|
||||
"object id\n");
|
||||
errno = EIO;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
/* overwrite index data with new value */
|
||||
memcpy(&old_attr, value,
|
||||
(size < sizeof(OBJECT_ID_ATTR)
|
||||
? size : sizeof(OBJECT_ID_ATTR)));
|
||||
if (!res
|
||||
&& set_object_id_index(ni,xo,&old_attr)) {
|
||||
/*
|
||||
* If cannot index, try to remove the object
|
||||
* id and log the error. There will be an
|
||||
* inconsistency if removal fails.
|
||||
*/
|
||||
ntfs_attr_rm(na);
|
||||
ntfs_log_error("Failed to index object id."
|
||||
" Possible corruption.\n");
|
||||
}
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
NInoSetDirty(ni);
|
||||
} else
|
||||
res = -1;
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a (dummy) object id to an inode if it does not exist
|
||||
*
|
||||
* returns 0 if attribute was inserted (or already present)
|
||||
* -1 if adding failed (explained by errno)
|
||||
*/
|
||||
|
||||
static int add_object_id(ntfs_inode *ni, int flags)
|
||||
{
|
||||
int res;
|
||||
u8 dummy;
|
||||
|
||||
res = -1; /* default return */
|
||||
if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) {
|
||||
if (!(flags & XATTR_REPLACE)) {
|
||||
/*
|
||||
* no object id attribute : add one,
|
||||
* apparently, this does not feed the new value in
|
||||
* Note : NTFS version must be >= 3
|
||||
*/
|
||||
if (ni->vol->major_ver >= 3) {
|
||||
res = ntfs_attr_add(ni, AT_OBJECT_ID,
|
||||
AT_UNNAMED, 0, &dummy, (s64)0);
|
||||
NInoSetDirty(ni);
|
||||
} else
|
||||
errno = EOPNOTSUPP;
|
||||
} else
|
||||
errno = ENODATA;
|
||||
} else {
|
||||
if (flags & XATTR_CREATE)
|
||||
errno = EEXIST;
|
||||
else
|
||||
res = 0;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delete an object_id index entry
|
||||
*
|
||||
* Returns 0 if success
|
||||
* -1 if failure, explained by errno
|
||||
*/
|
||||
|
||||
int ntfs_delete_object_id_index(ntfs_inode *ni)
|
||||
{
|
||||
ntfs_index_context *xo;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_attr *na;
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0);
|
||||
if (na) {
|
||||
/*
|
||||
* read the existing object id
|
||||
* and un-index it
|
||||
*/
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
if (remove_object_id_index(na,xo,&old_attr) < 0)
|
||||
res = -1;
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the ntfs object id into an extended attribute
|
||||
*
|
||||
* If present, the object_id from the attribute and the GUIDs
|
||||
* from the index are returned (formatted as OBJECT_ID_ATTR)
|
||||
*
|
||||
* Returns the global size (can be 0, 16 or 64)
|
||||
* and the buffer is updated if it is long enough
|
||||
*/
|
||||
|
||||
int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size)
|
||||
{
|
||||
OBJECT_ID_ATTR full_objectid;
|
||||
OBJECT_ID_ATTR *objectid_attr;
|
||||
s64 attr_size;
|
||||
int full_size;
|
||||
|
||||
full_size = 0; /* default to no data and some error to be defined */
|
||||
if (ni) {
|
||||
objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni,
|
||||
AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size);
|
||||
if (objectid_attr) {
|
||||
/* restrict to only GUID present in attr */
|
||||
if (attr_size == sizeof(GUID)) {
|
||||
memcpy(&full_objectid.object_id,
|
||||
objectid_attr,sizeof(GUID));
|
||||
full_size = sizeof(GUID);
|
||||
/* get data from index, if any */
|
||||
if (!merge_index_data(ni, objectid_attr,
|
||||
&full_objectid)) {
|
||||
full_size = sizeof(OBJECT_ID_ATTR);
|
||||
}
|
||||
if (full_size <= (s64)size) {
|
||||
if (value)
|
||||
memcpy(value,&full_objectid,
|
||||
full_size);
|
||||
else
|
||||
errno = EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* unexpected size, better return unsupported */
|
||||
errno = EOPNOTSUPP;
|
||||
full_size = 0;
|
||||
}
|
||||
free(objectid_attr);
|
||||
} else
|
||||
errno = ENODATA;
|
||||
}
|
||||
return (full_size ? (int)full_size : -errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the object id from an extended attribute
|
||||
*
|
||||
* The first 16 bytes are the new object id, they can be followed
|
||||
* by the birth volume id, the birth object id and the domain id.
|
||||
* If they are not present, their previous value is kept.
|
||||
* Only the object id is stored into the attribute, all the fields
|
||||
* are stored into the index.
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_set_ntfs_object_id(ntfs_inode *ni,
|
||||
const char *value, size_t size, int flags)
|
||||
{
|
||||
OBJECT_ID_INDEX_KEY key;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_index_context *xo;
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
if (ni && value && (size >= sizeof(GUID))) {
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
/* make sure the GUID was not used elsewhere */
|
||||
memcpy(&key.object_id, value, sizeof(GUID));
|
||||
if ((ntfs_index_lookup(&key,
|
||||
sizeof(OBJECT_ID_INDEX_KEY), xo))
|
||||
|| (MREF_LE(((struct OBJECT_ID_INDEX*)xo->entry)
|
||||
->data.file_id) == ni->mft_no)) {
|
||||
ntfs_index_ctx_reinit(xo);
|
||||
res = add_object_id(ni, flags);
|
||||
if (!res) {
|
||||
/* update value and index */
|
||||
res = update_object_id(ni,xo,
|
||||
(const OBJECT_ID_ATTR*)value,
|
||||
size);
|
||||
}
|
||||
} else {
|
||||
/* GUID is present elsewhere */
|
||||
res = -1;
|
||||
errno = EEXIST;
|
||||
}
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the object id
|
||||
*
|
||||
* Returns 0, or -1 if there is a problem
|
||||
*/
|
||||
|
||||
int ntfs_remove_ntfs_object_id(ntfs_inode *ni)
|
||||
{
|
||||
int res;
|
||||
int olderrno;
|
||||
ntfs_attr *na;
|
||||
ntfs_inode *xoni;
|
||||
ntfs_index_context *xo;
|
||||
int oldsize;
|
||||
OBJECT_ID_ATTR old_attr;
|
||||
|
||||
res = 0;
|
||||
if (ni) {
|
||||
/*
|
||||
* open and delete the object id
|
||||
*/
|
||||
na = ntfs_attr_open(ni, AT_OBJECT_ID,
|
||||
AT_UNNAMED,0);
|
||||
if (na) {
|
||||
/* first remove index (old object id needed) */
|
||||
xo = open_object_id_index(ni->vol);
|
||||
if (xo) {
|
||||
oldsize = remove_object_id_index(na,xo,
|
||||
&old_attr);
|
||||
if (oldsize < 0) {
|
||||
res = -1;
|
||||
} else {
|
||||
/* now remove attribute */
|
||||
res = ntfs_attr_rm(na);
|
||||
if (res
|
||||
&& (oldsize > (int)sizeof(GUID))) {
|
||||
/*
|
||||
* If we could not remove the
|
||||
* attribute, try to restore the
|
||||
* index and log the error. There
|
||||
* will be an inconsistency if
|
||||
* the reindexing fails.
|
||||
*/
|
||||
set_object_id_index(ni, xo,
|
||||
&old_attr);
|
||||
ntfs_log_error(
|
||||
"Failed to remove object id."
|
||||
" Possible corruption.\n");
|
||||
}
|
||||
}
|
||||
|
||||
xoni = xo->ni;
|
||||
ntfs_index_entry_mark_dirty(xo);
|
||||
NInoSetDirty(xoni);
|
||||
ntfs_index_ctx_put(xo);
|
||||
ntfs_inode_close(xoni);
|
||||
}
|
||||
olderrno = errno;
|
||||
ntfs_attr_close(na);
|
||||
/* avoid errno pollution */
|
||||
if (errno == ENOENT)
|
||||
errno = olderrno;
|
||||
} else {
|
||||
errno = ENODATA;
|
||||
res = -1;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
}
|
||||
return (res ? -1 : 0);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* realpath.c - realpath() aware of device mapper
|
||||
* Originated from the util-linux project.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifdef HAVE_CTYPE_H
|
||||
#include <ctype.h>
|
||||
#endif
|
||||
|
||||
#include "param.h"
|
||||
#include "realpath.h"
|
||||
|
||||
/* If there is no realpath() on the system, provide a dummy one. */
|
||||
#ifndef HAVE_REALPATH
|
||||
char *ntfs_realpath(const char *path, char *resolved_path)
|
||||
{
|
||||
strncpy(resolved_path, path, PATH_MAX);
|
||||
resolved_path[PATH_MAX] = '\0';
|
||||
return resolved_path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef linux
|
||||
|
||||
/*
|
||||
* Converts private "dm-N" names to "/dev/mapper/<name>"
|
||||
*
|
||||
* Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
|
||||
* provides the real DM device names in /sys/block/<ptname>/dm/name
|
||||
*/
|
||||
static char *
|
||||
canonicalize_dm_name(const char *ptname, char *canonical)
|
||||
{
|
||||
FILE *f;
|
||||
size_t sz;
|
||||
char name[MAPPERNAMELTH + 16];
|
||||
char path[sizeof(name) + 16];
|
||||
char *res = NULL;
|
||||
|
||||
snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
|
||||
if (!(f = fopen(path, "r")))
|
||||
return NULL;
|
||||
|
||||
/* read "<name>\n" from sysfs */
|
||||
if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
|
||||
name[sz - 1] = '\0';
|
||||
snprintf(path, sizeof(path), "/dev/mapper/%s", name);
|
||||
res = strcpy(canonical, path);
|
||||
}
|
||||
fclose(f);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Canonicalize a device path
|
||||
*
|
||||
* Workaround from "basinilya" for fixing device mapper paths.
|
||||
*
|
||||
* Background (Phillip Susi, 2011-04-09)
|
||||
* - ntfs-3g canonicalizes the device name so that if you mount with
|
||||
* /dev/mapper/foo, the device name listed in mtab is /dev/dm-n,
|
||||
* so you can not umount /dev/mapper/foo
|
||||
* - umount won't even recognize and translate /dev/dm-n to the mount
|
||||
* point, apparently because of the '-' involved. Editing mtab and
|
||||
* removing the '-' allows you to umount /dev/dmn successfully.
|
||||
*
|
||||
* This code restores the devmapper name after canonicalization,
|
||||
* until a proper fix is implemented.
|
||||
*/
|
||||
|
||||
char *ntfs_realpath_canonicalize(const char *path, char *canonical)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (path == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!ntfs_realpath(path, canonical))
|
||||
return NULL;
|
||||
|
||||
p = strrchr(canonical, '/');
|
||||
if (p && strncmp(p, "/dm-", 4) == 0 && isdigit(*(p + 4))) {
|
||||
p = canonicalize_dm_name(p+1, canonical);
|
||||
if (p)
|
||||
return p;
|
||||
}
|
||||
|
||||
return canonical;
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
* unix_io.c - Unix style disk io functions. Originated from the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2006 Anton Altaparmakov
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FD_H
|
||||
#include <linux/fd.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "mst.h"
|
||||
#include "debug.h"
|
||||
#include "device.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define DEV_FD(dev) (*(int *)dev->d_private)
|
||||
|
||||
/* Define to nothing if not present on this system. */
|
||||
#ifndef O_EXCL
|
||||
# define O_EXCL 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fsync replacement which makes every effort to try to get the data down to
|
||||
* disk, using different means for different operating systems. Specifically,
|
||||
* it issues the proper fcntl for Mac OS X or does fsync where it is available
|
||||
* or as a last resort calls the fsync function. Information on this problem
|
||||
* was retrieved from:
|
||||
* http://mirror.linux.org.au/pub/linux.conf.au/2007/video/talks/278.pdf
|
||||
*/
|
||||
static int ntfs_fsync(int fildes)
|
||||
{
|
||||
int ret = -1;
|
||||
#if defined(__APPLE__) || defined(__DARWIN__)
|
||||
# ifndef F_FULLFSYNC
|
||||
# error "Mac OS X: F_FULLFSYNC is not defined. Either you didn't include fcntl.h or you're using an older, unsupported version of Mac OS X (pre-10.3)."
|
||||
# endif
|
||||
/*
|
||||
* Apple has disabled fsync() for internal disk drives in OS X.
|
||||
* To force a synchronization of disk contents, we use a Mac OS X
|
||||
* specific fcntl, F_FULLFSYNC.
|
||||
*/
|
||||
ret = fcntl(fildes, F_FULLFSYNC, NULL);
|
||||
if (ret) {
|
||||
/*
|
||||
* If we are not on a file system that supports this,
|
||||
* then fall back to a plain fsync.
|
||||
*/
|
||||
ret = fsync(fildes);
|
||||
}
|
||||
#else
|
||||
ret = fsync(fildes);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_open - Open a device and lock it exclusively
|
||||
* @dev:
|
||||
* @flags:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags)
|
||||
{
|
||||
struct flock flk;
|
||||
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))
|
||||
NDevSetBlock(dev);
|
||||
|
||||
dev->d_private = ntfs_malloc(sizeof(int));
|
||||
if (!dev->d_private)
|
||||
return -1;
|
||||
/*
|
||||
* Open file for exclusive access if mounting r/w.
|
||||
* Fuseblk takes care about block devices.
|
||||
*/
|
||||
if (!NDevBlock(dev) && (flags & O_RDWR) == O_RDWR)
|
||||
flags |= O_EXCL;
|
||||
*(int*)dev->d_private = open(dev->d_name, flags);
|
||||
if (*(int*)dev->d_private == -1) {
|
||||
err = errno;
|
||||
/* if permission error and rw, retry read-only */
|
||||
if ((err == EACCES) && ((flags & O_RDWR) == O_RDWR))
|
||||
err = EROFS;
|
||||
goto err_out;
|
||||
}
|
||||
#ifdef HAVE_LINUX_FS_H
|
||||
/* Check whether the device was forced read-only */
|
||||
if (NDevBlock(dev) && ((flags & O_RDWR) == O_RDWR)) {
|
||||
int r;
|
||||
int state;
|
||||
|
||||
r = ioctl(DEV_FD(dev), BLKROGET, &state);
|
||||
if (!r && state) {
|
||||
err = EROFS;
|
||||
if (close(DEV_FD(dev)))
|
||||
err = errno;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((flags & O_RDWR) != O_RDWR)
|
||||
NDevSetReadOnly(dev);
|
||||
|
||||
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), 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)))
|
||||
ntfs_log_perror("Failed to close '%s'", dev->d_name);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
NDevSetOpen(dev);
|
||||
return 0;
|
||||
err_out:
|
||||
free(dev->d_private);
|
||||
dev->d_private = NULL;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_close - Close the device, releasing the lock
|
||||
* @dev:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_close(struct ntfs_device *dev)
|
||||
{
|
||||
struct flock flk;
|
||||
|
||||
if (!NDevOpen(dev)) {
|
||||
errno = EBADF;
|
||||
ntfs_log_perror("Device %s is not open", dev->d_name);
|
||||
return -1;
|
||||
}
|
||||
if (NDevDirty(dev))
|
||||
if (ntfs_fsync(DEV_FD(dev))) {
|
||||
ntfs_log_perror("Failed to fsync device %s", dev->d_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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), F_SETLK, &flk))
|
||||
ntfs_log_perror("Could not unlock %s", dev->d_name);
|
||||
if (close(DEV_FD(dev))) {
|
||||
ntfs_log_perror("Failed to close device %s", dev->d_name);
|
||||
return -1;
|
||||
}
|
||||
NDevClearOpen(dev);
|
||||
free(dev->d_private);
|
||||
dev->d_private = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_seek - Seek to a place on the device
|
||||
* @dev:
|
||||
* @offset:
|
||||
* @whence:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset,
|
||||
int whence)
|
||||
{
|
||||
return lseek(DEV_FD(dev), offset, whence);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_read - Read from the device, from the current location
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf,
|
||||
s64 count)
|
||||
{
|
||||
return read(DEV_FD(dev), buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_write - Write to the device, at the current location
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf,
|
||||
s64 count)
|
||||
{
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
NDevSetDirty(dev);
|
||||
return write(DEV_FD(dev), buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_pread - Perform a positioned read from the device
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
* @offset:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf,
|
||||
s64 count, s64 offset)
|
||||
{
|
||||
return pread(DEV_FD(dev), buf, count, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_pwrite - Perform a positioned write to the device
|
||||
* @dev:
|
||||
* @buf:
|
||||
* @count:
|
||||
* @offset:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf,
|
||||
s64 count, s64 offset)
|
||||
{
|
||||
if (NDevReadOnly(dev)) {
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
NDevSetDirty(dev);
|
||||
return pwrite(DEV_FD(dev), buf, count, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_sync - Flush any buffered changes to the device
|
||||
* @dev:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_sync(struct ntfs_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (!NDevReadOnly(dev)) {
|
||||
res = ntfs_fsync(DEV_FD(dev));
|
||||
if (res)
|
||||
ntfs_log_perror("Failed to sync device %s", dev->d_name);
|
||||
else
|
||||
NDevClearDirty(dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_stat - Get information about the device
|
||||
* @dev:
|
||||
* @buf:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf)
|
||||
{
|
||||
return fstat(DEV_FD(dev), buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_device_unix_io_ioctl - Perform an ioctl on the device
|
||||
* @dev:
|
||||
* @request:
|
||||
* @argp:
|
||||
*
|
||||
* Description...
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev,
|
||||
unsigned long request, void *argp)
|
||||
{
|
||||
return ioctl(DEV_FD(dev), request, argp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Device operations for working with unix style devices and files.
|
||||
*/
|
||||
struct ntfs_device_operations ntfs_device_unix_io_ops = {
|
||||
.open = ntfs_device_unix_io_open,
|
||||
.close = ntfs_device_unix_io_close,
|
||||
.seek = ntfs_device_unix_io_seek,
|
||||
.read = ntfs_device_unix_io_read,
|
||||
.write = ntfs_device_unix_io_write,
|
||||
.pread = ntfs_device_unix_io_pread,
|
||||
.pwrite = ntfs_device_unix_io_pwrite,
|
||||
.sync = ntfs_device_unix_io_sync,
|
||||
.stat = ntfs_device_unix_io_stat,
|
||||
.ioctl = ntfs_device_unix_io_ioctl,
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,808 @@
|
||||
/**
|
||||
* xattrs.c : common functions to deal with system extended attributes
|
||||
*
|
||||
* Copyright (c) 2010-2014 Jean-Pierre Andre
|
||||
*
|
||||
* 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_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "layout.h"
|
||||
#include "attrib.h"
|
||||
#include "index.h"
|
||||
#include "dir.h"
|
||||
#include "security.h"
|
||||
#include "acls.h"
|
||||
#include "efs.h"
|
||||
#include "reparse.h"
|
||||
#include "object_id.h"
|
||||
#include "ea.h"
|
||||
#include "misc.h"
|
||||
#include "logging.h"
|
||||
#include "xattrs.h"
|
||||
|
||||
#if POSIXACLS
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
/*
|
||||
* Posix ACL structures
|
||||
*/
|
||||
|
||||
struct LE_POSIX_ACE {
|
||||
le16 tag;
|
||||
le16 perms;
|
||||
le32 id;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct LE_POSIX_ACL {
|
||||
u8 version;
|
||||
u8 flags;
|
||||
le16 filler;
|
||||
struct LE_POSIX_ACE ace[1];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const char nf_ns_xattr_ntfs_acl[] = "system.ntfs_acl";
|
||||
static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib";
|
||||
static const char nf_ns_xattr_attrib_be[] = "system.ntfs_attrib_be";
|
||||
static const char nf_ns_xattr_efsinfo[] = "system.ntfs_efsinfo";
|
||||
static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data";
|
||||
static const char nf_ns_xattr_object_id[] = "system.ntfs_object_id";
|
||||
static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name";
|
||||
static const char nf_ns_xattr_times[] = "system.ntfs_times";
|
||||
static const char nf_ns_xattr_times_be[] = "system.ntfs_times_be";
|
||||
static const char nf_ns_xattr_crtime[] = "system.ntfs_crtime";
|
||||
static const char nf_ns_xattr_crtime_be[] = "system.ntfs_crtime_be";
|
||||
static const char nf_ns_xattr_ea[] = "system.ntfs_ea";
|
||||
static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access";
|
||||
static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default";
|
||||
|
||||
static const char nf_ns_alt_xattr_efsinfo[] = "user.ntfs.efsinfo";
|
||||
|
||||
struct XATTRNAME {
|
||||
enum SYSTEMXATTRS xattr;
|
||||
const char *name;
|
||||
} ;
|
||||
|
||||
static struct XATTRNAME nf_ns_xattr_names[] = {
|
||||
{ XATTR_NTFS_ACL, nf_ns_xattr_ntfs_acl },
|
||||
{ XATTR_NTFS_ATTRIB, nf_ns_xattr_attrib },
|
||||
{ XATTR_NTFS_ATTRIB_BE, nf_ns_xattr_attrib_be },
|
||||
{ XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo },
|
||||
{ XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse },
|
||||
{ XATTR_NTFS_OBJECT_ID, nf_ns_xattr_object_id },
|
||||
{ XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name },
|
||||
{ XATTR_NTFS_TIMES, nf_ns_xattr_times },
|
||||
{ XATTR_NTFS_TIMES_BE, nf_ns_xattr_times_be },
|
||||
{ XATTR_NTFS_CRTIME, nf_ns_xattr_crtime },
|
||||
{ XATTR_NTFS_CRTIME_BE, nf_ns_xattr_crtime_be },
|
||||
{ XATTR_NTFS_EA, nf_ns_xattr_ea },
|
||||
{ XATTR_POSIX_ACC, nf_ns_xattr_posix_access },
|
||||
{ XATTR_POSIX_DEF, nf_ns_xattr_posix_default },
|
||||
{ XATTR_UNMAPPED, (char*)NULL } /* terminator */
|
||||
};
|
||||
|
||||
/*
|
||||
* Make an integer big-endian
|
||||
*
|
||||
* Swap bytes on a small-endian computer and does nothing on a
|
||||
* big-endian computer.
|
||||
*/
|
||||
|
||||
static void fix_big_endian(char *p, int size)
|
||||
{
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
int i,j;
|
||||
int c;
|
||||
|
||||
i = 0;
|
||||
j = size - 1;
|
||||
while (i < j) {
|
||||
c = p[i];
|
||||
p[i++] = p[j];
|
||||
p[j--] = c;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if POSIXACLS
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
/*
|
||||
* Make a Posix ACL CPU endian
|
||||
*/
|
||||
|
||||
static int le_acl_to_cpu(const struct LE_POSIX_ACL *le_acl, size_t size,
|
||||
struct POSIX_ACL *acl)
|
||||
{
|
||||
int i;
|
||||
int cnt;
|
||||
|
||||
acl->version = le_acl->version;
|
||||
acl->flags = le_acl->flags;
|
||||
acl->filler = 0;
|
||||
cnt = (size - sizeof(struct LE_POSIX_ACL)) / sizeof(struct LE_POSIX_ACE);
|
||||
for (i=0; i<cnt; i++) {
|
||||
acl->ace[i].tag = le16_to_cpu(le_acl->ace[i].tag);
|
||||
acl->ace[i].perms = le16_to_cpu(le_acl->ace[i].perms);
|
||||
acl->ace[i].id = le32_to_cpu(le_acl->ace[i].id);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a Posix ACL little endian
|
||||
*/
|
||||
|
||||
int cpu_to_le_acl(const struct POSIX_ACL *acl, size_t size,
|
||||
struct LE_POSIX_ACL *le_acl)
|
||||
{
|
||||
int i;
|
||||
int cnt;
|
||||
|
||||
le_acl->version = acl->version;
|
||||
le_acl->flags = acl->flags;
|
||||
le_acl->filler = const_cpu_to_le16(0);
|
||||
cnt = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
|
||||
for (i=0; i<cnt; i++) {
|
||||
le_acl->ace[i].tag = cpu_to_le16(acl->ace[i].tag);
|
||||
le_acl->ace[i].perms = cpu_to_le16(acl->ace[i].perms);
|
||||
le_acl->ace[i].id = cpu_to_le32(acl->ace[i].id);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Determine whether an extended attribute is mapped to
|
||||
* internal data (original name in system namespace, or renamed)
|
||||
*/
|
||||
|
||||
enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name,
|
||||
ntfs_volume *vol)
|
||||
{
|
||||
struct XATTRNAME *p;
|
||||
enum SYSTEMXATTRS ret;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
const struct XATTRMAPPING *q;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
|
||||
p = nf_ns_xattr_names;
|
||||
while (p->name && strcmp(p->name,name))
|
||||
p++;
|
||||
ret = p->xattr;
|
||||
#ifdef XATTR_MAPPINGS
|
||||
if (!p->name && vol && vol->xattr_mapping) {
|
||||
q = vol->xattr_mapping;
|
||||
while (q && strcmp(q->name,name))
|
||||
q = q->next;
|
||||
if (q)
|
||||
ret = q->xattr;
|
||||
}
|
||||
#else /* XATTR_MAPPINGS */
|
||||
if (!p->name
|
||||
&& vol
|
||||
&& vol->efs_raw
|
||||
&& !strcmp(nf_ns_alt_xattr_efsinfo,name))
|
||||
ret = XATTR_NTFS_EFSINFO;
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
return (ret);
|
||||
}
|
||||
|
||||
#ifdef XATTR_MAPPINGS
|
||||
|
||||
/*
|
||||
* Basic read from a user mapping file on another volume
|
||||
*/
|
||||
|
||||
static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
|
||||
{
|
||||
return (read(*(int*)fileid, buf, size));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read from a user mapping file on current NTFS partition
|
||||
*/
|
||||
|
||||
static int localread(void *fileid, char *buf, size_t size, off_t offs)
|
||||
{
|
||||
return (ntfs_attr_data_read((ntfs_inode*)fileid,
|
||||
AT_UNNAMED, 0, buf, size, offs));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a single mapping item from buffer
|
||||
*
|
||||
* Always reads a full line, truncating long lines
|
||||
* Refills buffer when exhausted
|
||||
* Returns pointer to item, or NULL when there is no more
|
||||
* Note : errors are logged, but not returned
|
||||
// TODO partially share with acls.c
|
||||
*/
|
||||
|
||||
static struct XATTRMAPPING *getmappingitem(FILEREADER reader, void *fileid,
|
||||
off_t *poffs, char *buf, int *psrc, s64 *psize)
|
||||
{
|
||||
int src;
|
||||
int dst;
|
||||
char *pe;
|
||||
char *ps;
|
||||
char *pu;
|
||||
enum SYSTEMXATTRS xattr;
|
||||
int gotend;
|
||||
char maptext[LINESZ];
|
||||
struct XATTRMAPPING *item;
|
||||
|
||||
src = *psrc;
|
||||
dst = 0;
|
||||
do {
|
||||
gotend = 0;
|
||||
while ((src < *psize)
|
||||
&& (buf[src] != '\n')) {
|
||||
/* ignore spaces */
|
||||
if ((dst < LINESZ)
|
||||
&& (buf[src] != '\r')
|
||||
&& (buf[src] != '\t')
|
||||
&& (buf[src] != ' '))
|
||||
maptext[dst++] = buf[src];
|
||||
src++;
|
||||
}
|
||||
if (src >= *psize) {
|
||||
*poffs += *psize;
|
||||
*psize = reader(fileid, buf, (size_t)BUFSZ, *poffs);
|
||||
src = 0;
|
||||
} else {
|
||||
gotend = 1;
|
||||
src++;
|
||||
maptext[dst] = '\0';
|
||||
dst = 0;
|
||||
}
|
||||
} while (*psize && ((maptext[0] == '#') || !gotend));
|
||||
item = (struct XATTRMAPPING*)NULL;
|
||||
if (gotend) {
|
||||
/* decompose into system name and user name */
|
||||
ps = maptext;
|
||||
pu = strchr(maptext,':');
|
||||
if (pu) {
|
||||
*pu++ = 0;
|
||||
pe = strchr(pu,':');
|
||||
if (pe)
|
||||
*pe = 0;
|
||||
/* check name validity */
|
||||
if ((strlen(pu) < 6) || strncmp(pu,"user.",5))
|
||||
pu = (char*)NULL;
|
||||
xattr = ntfs_xattr_system_type(ps,
|
||||
(ntfs_volume*)NULL);
|
||||
if (xattr == XATTR_UNMAPPED)
|
||||
pu = (char*)NULL;
|
||||
}
|
||||
if (pu) {
|
||||
item = (struct XATTRMAPPING*)ntfs_malloc(
|
||||
sizeof(struct XATTRMAPPING)
|
||||
+ strlen(pu));
|
||||
if (item) {
|
||||
item->xattr = xattr;
|
||||
strcpy(item->name,pu);
|
||||
item->next = (struct XATTRMAPPING*)NULL;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_early_error("Bad xattr mapping item, aborting\n");
|
||||
}
|
||||
}
|
||||
*psrc = src;
|
||||
return (item);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read xattr mapping file and split into their attribute.
|
||||
* Parameters are kept in a chained list.
|
||||
* Returns the head of list, if any
|
||||
* Errors are logged, but not returned
|
||||
*
|
||||
* If an absolute path is provided, the mapping file is assumed
|
||||
* to be located in another mounted file system, and plain read()
|
||||
* are used to get its contents.
|
||||
* If a relative path is provided, the mapping file is assumed
|
||||
* to be located on the current file system, and internal IO
|
||||
* have to be used since we are still mounting and we have not
|
||||
* entered the fuse loop yet.
|
||||
*/
|
||||
|
||||
static struct XATTRMAPPING *ntfs_read_xattr_mapping(FILEREADER reader,
|
||||
void *fileid)
|
||||
{
|
||||
char buf[BUFSZ];
|
||||
struct XATTRMAPPING *item;
|
||||
struct XATTRMAPPING *current;
|
||||
struct XATTRMAPPING *firstitem;
|
||||
struct XATTRMAPPING *lastitem;
|
||||
BOOL duplicated;
|
||||
int src;
|
||||
off_t offs;
|
||||
s64 size;
|
||||
|
||||
firstitem = (struct XATTRMAPPING*)NULL;
|
||||
lastitem = (struct XATTRMAPPING*)NULL;
|
||||
offs = 0;
|
||||
size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0);
|
||||
if (size > 0) {
|
||||
src = 0;
|
||||
do {
|
||||
item = getmappingitem(reader, fileid, &offs,
|
||||
buf, &src, &size);
|
||||
if (item) {
|
||||
/* check no double mapping */
|
||||
duplicated = FALSE;
|
||||
for (current=firstitem; current; current=current->next)
|
||||
if ((current->xattr == item->xattr)
|
||||
|| !strcmp(current->name,item->name))
|
||||
duplicated = TRUE;
|
||||
if (duplicated) {
|
||||
free(item);
|
||||
ntfs_log_early_error("Conflicting xattr mapping ignored\n");
|
||||
} else {
|
||||
item->next = (struct XATTRMAPPING*)NULL;
|
||||
if (lastitem)
|
||||
lastitem->next = item;
|
||||
else
|
||||
firstitem = item;
|
||||
lastitem = item;
|
||||
}
|
||||
}
|
||||
} while (item);
|
||||
}
|
||||
return (firstitem);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build the extended attribute mappings to user namespace
|
||||
*
|
||||
* Note : no error is returned. If we refused mounting when there
|
||||
* is an error it would be too difficult to fix the offending file
|
||||
*/
|
||||
|
||||
struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol,
|
||||
const char *xattrmap_path)
|
||||
{
|
||||
struct XATTRMAPPING *firstmapping;
|
||||
struct XATTRMAPPING *mapping;
|
||||
BOOL user_efs;
|
||||
BOOL notfound;
|
||||
ntfs_inode *ni;
|
||||
int fd;
|
||||
|
||||
firstmapping = (struct XATTRMAPPING*)NULL;
|
||||
notfound = FALSE;
|
||||
if (!xattrmap_path)
|
||||
xattrmap_path = XATTRMAPPINGFILE;
|
||||
if (xattrmap_path[0] == '/') {
|
||||
fd = open(xattrmap_path,O_RDONLY);
|
||||
if (fd > 0) {
|
||||
firstmapping = ntfs_read_xattr_mapping(basicread, (void*)&fd);
|
||||
close(fd);
|
||||
} else
|
||||
notfound = TRUE;
|
||||
} else {
|
||||
ni = ntfs_pathname_to_inode(vol, NULL, xattrmap_path);
|
||||
if (ni) {
|
||||
firstmapping = ntfs_read_xattr_mapping(localread, ni);
|
||||
ntfs_inode_close(ni);
|
||||
} else
|
||||
notfound = TRUE;
|
||||
}
|
||||
if (notfound && strcmp(xattrmap_path, XATTRMAPPINGFILE)) {
|
||||
ntfs_log_early_error("Could not open \"%s\"\n",xattrmap_path);
|
||||
}
|
||||
if (vol->efs_raw) {
|
||||
user_efs = TRUE;
|
||||
for (mapping=firstmapping; mapping; mapping=mapping->next)
|
||||
if (mapping->xattr == XATTR_NTFS_EFSINFO)
|
||||
user_efs = FALSE;
|
||||
} else
|
||||
user_efs = FALSE;
|
||||
if (user_efs) {
|
||||
mapping = (struct XATTRMAPPING*)ntfs_malloc(
|
||||
sizeof(struct XATTRMAPPING)
|
||||
+ strlen(nf_ns_alt_xattr_efsinfo));
|
||||
if (mapping) {
|
||||
mapping->next = firstmapping;
|
||||
mapping->xattr = XATTR_NTFS_EFSINFO;
|
||||
strcpy(mapping->name,nf_ns_alt_xattr_efsinfo);
|
||||
firstmapping = mapping;
|
||||
}
|
||||
}
|
||||
return (firstmapping);
|
||||
}
|
||||
|
||||
void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping)
|
||||
{
|
||||
struct XATTRMAPPING *p, *q;
|
||||
|
||||
p = mapping;
|
||||
while (p) {
|
||||
q = p->next;
|
||||
free(p);
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* XATTR_MAPPINGS */
|
||||
|
||||
/*
|
||||
* Get an NTFS attribute into an extended attribute
|
||||
*
|
||||
* Returns the non-negative size of attribute if successful,
|
||||
* or negative, with errno set, when fails
|
||||
* Note : the size is returned even if no buffer is provided
|
||||
* for returning the attribute, or if it is zero-sized.
|
||||
*/
|
||||
|
||||
int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx,
|
||||
enum SYSTEMXATTRS attr,
|
||||
ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
char *value, size_t size)
|
||||
{
|
||||
int res;
|
||||
int i;
|
||||
#if POSIXACLS
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
struct POSIX_ACL *acl;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
switch (attr) {
|
||||
case XATTR_NTFS_ACL :
|
||||
res = ntfs_get_ntfs_acl(scx, ni, value, size);
|
||||
break;
|
||||
#if POSIXACLS
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
case XATTR_POSIX_ACC :
|
||||
acl = (struct POSIX_ACL*)ntfs_malloc(size);
|
||||
if (acl) {
|
||||
res = ntfs_get_posix_acl(scx, ni,
|
||||
nf_ns_xattr_posix_access, (char*)acl, size);
|
||||
if (res > 0) {
|
||||
if (cpu_to_le_acl(acl,res,
|
||||
(struct LE_POSIX_ACL*)value))
|
||||
res = -errno;
|
||||
}
|
||||
free(acl);
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
case XATTR_POSIX_DEF :
|
||||
acl = (struct POSIX_ACL*)ntfs_malloc(size);
|
||||
if (acl) {
|
||||
res = ntfs_get_posix_acl(scx, ni,
|
||||
nf_ns_xattr_posix_default, (char*)acl, size);
|
||||
if (res > 0) {
|
||||
if (cpu_to_le_acl(acl,res,
|
||||
(struct LE_POSIX_ACL*)value))
|
||||
res = -errno;
|
||||
}
|
||||
free(acl);
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
#else
|
||||
case XATTR_POSIX_ACC :
|
||||
res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_access,
|
||||
value, size);
|
||||
break;
|
||||
case XATTR_POSIX_DEF :
|
||||
res = ntfs_get_posix_acl(scx, ni, nf_ns_xattr_posix_default,
|
||||
value, size);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
case XATTR_NTFS_ATTRIB :
|
||||
res = ntfs_get_ntfs_attrib(ni, value, size);
|
||||
break;
|
||||
case XATTR_NTFS_ATTRIB_BE :
|
||||
res = ntfs_get_ntfs_attrib(ni, value, size);
|
||||
if ((res == 4) && value) {
|
||||
if (size >= 4)
|
||||
fix_big_endian(value,4);
|
||||
else
|
||||
res = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case XATTR_NTFS_EFSINFO :
|
||||
if (ni->vol->efs_raw)
|
||||
res = ntfs_get_efs_info(ni, value, size);
|
||||
else
|
||||
res = -EPERM;
|
||||
break;
|
||||
case XATTR_NTFS_REPARSE_DATA :
|
||||
res = ntfs_get_ntfs_reparse_data(ni, value, size);
|
||||
break;
|
||||
case XATTR_NTFS_OBJECT_ID :
|
||||
res = ntfs_get_ntfs_object_id(ni, value, size);
|
||||
break;
|
||||
case XATTR_NTFS_DOS_NAME:
|
||||
if (dir_ni)
|
||||
res = ntfs_get_ntfs_dos_name(ni, dir_ni, value, size);
|
||||
else
|
||||
res = -errno;
|
||||
break;
|
||||
case XATTR_NTFS_TIMES:
|
||||
res = ntfs_inode_get_times(ni, value, size);
|
||||
break;
|
||||
case XATTR_NTFS_TIMES_BE:
|
||||
res = ntfs_inode_get_times(ni, value, size);
|
||||
if ((res > 0) && value) {
|
||||
for (i=0; (i+1)*sizeof(u64)<=(unsigned int)res; i++)
|
||||
fix_big_endian(&value[i*sizeof(u64)],
|
||||
sizeof(u64));
|
||||
}
|
||||
break;
|
||||
case XATTR_NTFS_CRTIME:
|
||||
res = ntfs_inode_get_times(ni, value,
|
||||
(size >= sizeof(u64) ? sizeof(u64) : size));
|
||||
break;
|
||||
case XATTR_NTFS_CRTIME_BE:
|
||||
res = ntfs_inode_get_times(ni, value,
|
||||
(size >= sizeof(u64) ? sizeof(u64) : size));
|
||||
if ((res >= (int)sizeof(u64)) && value)
|
||||
fix_big_endian(value,sizeof(u64));
|
||||
break;
|
||||
case XATTR_NTFS_EA :
|
||||
res = ntfs_get_ntfs_ea(ni, value, size);
|
||||
break;
|
||||
default :
|
||||
errno = EOPNOTSUPP;
|
||||
res = -errno;
|
||||
break;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an NTFS attribute from an extended attribute
|
||||
*
|
||||
* Returns 0 if successful,
|
||||
* non-zero, with errno set, when fails
|
||||
*/
|
||||
|
||||
int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx,
|
||||
enum SYSTEMXATTRS attr,
|
||||
ntfs_inode *ni, ntfs_inode *dir_ni,
|
||||
const char *value, size_t size, int flags)
|
||||
{
|
||||
int res;
|
||||
int i;
|
||||
char buf[4*sizeof(u64)];
|
||||
#if POSIXACLS
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
struct POSIX_ACL *acl;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
switch (attr) {
|
||||
case XATTR_NTFS_ACL :
|
||||
res = ntfs_set_ntfs_acl(scx, ni, value, size, flags);
|
||||
break;
|
||||
#if POSIXACLS
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
case XATTR_POSIX_ACC :
|
||||
acl = (struct POSIX_ACL*)ntfs_malloc(size);
|
||||
if (acl) {
|
||||
if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value,
|
||||
size, acl)) {
|
||||
res = ntfs_set_posix_acl(scx ,ni ,
|
||||
nf_ns_xattr_posix_access,
|
||||
(char*)acl, size, flags);
|
||||
} else
|
||||
res = -errno;
|
||||
free(acl);
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
case XATTR_POSIX_DEF :
|
||||
acl = (struct POSIX_ACL*)ntfs_malloc(size);
|
||||
if (acl) {
|
||||
if (!le_acl_to_cpu((const struct LE_POSIX_ACL*)value,
|
||||
size, acl)) {
|
||||
res = ntfs_set_posix_acl(scx ,ni ,
|
||||
nf_ns_xattr_posix_default,
|
||||
(char*)acl, size, flags);
|
||||
} else
|
||||
res = -errno;
|
||||
free(acl);
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
#else
|
||||
case XATTR_POSIX_ACC :
|
||||
res = ntfs_set_posix_acl(scx ,ni , nf_ns_xattr_posix_access,
|
||||
value, size, flags);
|
||||
break;
|
||||
case XATTR_POSIX_DEF :
|
||||
res = ntfs_set_posix_acl(scx, ni, nf_ns_xattr_posix_default,
|
||||
value, size, flags);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
case XATTR_NTFS_ATTRIB :
|
||||
res = ntfs_set_ntfs_attrib(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_ATTRIB_BE :
|
||||
if (value && (size >= 4)) {
|
||||
memcpy(buf,value,4);
|
||||
fix_big_endian(buf,4);
|
||||
res = ntfs_set_ntfs_attrib(ni, buf, 4, flags);
|
||||
} else
|
||||
res = ntfs_set_ntfs_attrib(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_EFSINFO :
|
||||
if (ni->vol->efs_raw)
|
||||
res = ntfs_set_efs_info(ni, value, size, flags);
|
||||
else {
|
||||
errno = EPERM;
|
||||
res = -EPERM;
|
||||
}
|
||||
break;
|
||||
case XATTR_NTFS_REPARSE_DATA :
|
||||
res = ntfs_set_ntfs_reparse_data(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_OBJECT_ID :
|
||||
res = ntfs_set_ntfs_object_id(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_DOS_NAME:
|
||||
if (dir_ni)
|
||||
/* warning : this closes both inodes */
|
||||
res = ntfs_set_ntfs_dos_name(ni, dir_ni, value,
|
||||
size, flags);
|
||||
else {
|
||||
errno = EINVAL;
|
||||
res = -errno;
|
||||
}
|
||||
break;
|
||||
case XATTR_NTFS_TIMES:
|
||||
res = ntfs_inode_set_times(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_TIMES_BE:
|
||||
if (value && (size > 0) && (size <= 4*sizeof(u64))) {
|
||||
memcpy(buf,value,size);
|
||||
for (i=0; (i+1)*sizeof(u64)<=size; i++)
|
||||
fix_big_endian(&buf[i*sizeof(u64)],
|
||||
sizeof(u64));
|
||||
res = ntfs_inode_set_times(ni, buf, size, flags);
|
||||
} else
|
||||
res = ntfs_inode_set_times(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_CRTIME:
|
||||
res = ntfs_inode_set_times(ni, value,
|
||||
(size >= sizeof(u64) ? sizeof(u64) : size), flags);
|
||||
break;
|
||||
case XATTR_NTFS_CRTIME_BE:
|
||||
if (value && (size >= sizeof(u64))) {
|
||||
memcpy(buf,value,sizeof(u64));
|
||||
fix_big_endian(buf,sizeof(u64));
|
||||
res = ntfs_inode_set_times(ni, buf, sizeof(u64), flags);
|
||||
} else
|
||||
res = ntfs_inode_set_times(ni, value, size, flags);
|
||||
break;
|
||||
case XATTR_NTFS_EA :
|
||||
res = ntfs_set_ntfs_ea(ni, value, size, flags);
|
||||
break;
|
||||
default :
|
||||
errno = EOPNOTSUPP;
|
||||
res = -errno;
|
||||
break;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
int ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx,
|
||||
enum SYSTEMXATTRS attr,
|
||||
ntfs_inode *ni, ntfs_inode *dir_ni)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = 0;
|
||||
switch (attr) {
|
||||
/*
|
||||
* Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES
|
||||
* is never allowed
|
||||
*/
|
||||
case XATTR_NTFS_ACL :
|
||||
case XATTR_NTFS_ATTRIB :
|
||||
case XATTR_NTFS_ATTRIB_BE :
|
||||
case XATTR_NTFS_EFSINFO :
|
||||
case XATTR_NTFS_TIMES :
|
||||
case XATTR_NTFS_TIMES_BE :
|
||||
case XATTR_NTFS_CRTIME :
|
||||
case XATTR_NTFS_CRTIME_BE :
|
||||
res = -EPERM;
|
||||
break;
|
||||
#if POSIXACLS
|
||||
case XATTR_POSIX_ACC :
|
||||
case XATTR_POSIX_DEF :
|
||||
if (ni) {
|
||||
if (!ntfs_allowed_as_owner(scx, ni)
|
||||
|| ntfs_remove_posix_acl(scx, ni,
|
||||
(attr == XATTR_POSIX_ACC ?
|
||||
nf_ns_xattr_posix_access :
|
||||
nf_ns_xattr_posix_default)))
|
||||
res = -errno;
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
#endif
|
||||
case XATTR_NTFS_REPARSE_DATA :
|
||||
if (ni) {
|
||||
if (!ntfs_allowed_as_owner(scx, ni)
|
||||
|| ntfs_remove_ntfs_reparse_data(ni))
|
||||
res = -errno;
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
case XATTR_NTFS_OBJECT_ID :
|
||||
if (ni) {
|
||||
if (!ntfs_allowed_as_owner(scx, ni)
|
||||
|| ntfs_remove_ntfs_object_id(ni))
|
||||
res = -errno;
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
case XATTR_NTFS_DOS_NAME:
|
||||
if (ni && dir_ni) {
|
||||
if (ntfs_remove_ntfs_dos_name(ni,dir_ni))
|
||||
res = -errno;
|
||||
/* ni and dir_ni have been closed */
|
||||
} else
|
||||
res = -errno;
|
||||
break;
|
||||
case XATTR_NTFS_EA :
|
||||
res = ntfs_remove_ntfs_ea(ni);
|
||||
break;
|
||||
default :
|
||||
errno = EOPNOTSUPP;
|
||||
res = -errno;
|
||||
break;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
Reference in new issue
Block a user