NTFS: update libntfs-3g to latest stable version

This commit is contained in:
threedeyes
2012-10-17 23:09:27 +00:00
parent cb8f90c0c8
commit a814d85025
40 changed files with 2489 additions and 744 deletions
@@ -161,7 +161,7 @@ fs_identify_partition(int fd, partition_data *partition, void **_cookie)
}
// try mount
ntVolume = utils_mount_volume(devpath, MS_RDONLY, true);
ntVolume = utils_mount_volume(devpath, MS_RDONLY | MS_RECOVER);
if (!ntVolume) {
ERROR("fs_identify_partition: mount failed\n");
return -1;
@@ -257,7 +257,7 @@ fs_mount(fs_volume *_vol, const char *device, ulong flags, const char *args,
ns->flags |= B_FS_IS_READONLY;
}
ns->ntvol = utils_mount_volume(device, mountFlags, true);
ns->ntvol = utils_mount_volume(device, mountFlags | MS_RECOVER);
if (ns->ntvol != NULL)
result = B_NO_ERROR;
else
@@ -530,7 +530,7 @@ fs_read_vnode(fs_volume *_vol, ino_t vnid, fs_vnode *_node, int *_type,
goto exit;
newNode->vnid = vnid;
newNode->parent_vnid = ntfs_get_parent_ref(ni);
newNode->parent_vnid = ntfs_mft_get_parent_ref(ni);
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
set_mime(newNode, ".***");
@@ -31,4 +31,6 @@ KernelStaticLibrary libntfs.a :
unistr.c
unix_io.c
volume.c
realpath.c
xattrs.c
;
@@ -4,7 +4,7 @@
* This module is part of ntfs-3g library, but may also be
* integrated in tools running over Linux or Windows
*
* Copyright (c) 2007-2009 Jean-Pierre Andre
* Copyright (c) 2007-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
@@ -528,9 +528,7 @@ gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid)
{
gid_t gid;
const struct MAPPING *p;
int gsidsz;
gsidsz = ntfs_sid_size(gsid);
p = groupmapping;
while (p && p->xid && !ntfs_same_sid(gsid, p->sid))
p = p->next;
@@ -1907,7 +1905,6 @@ static int buildacls_posix(struct MAPPING* const mapping[],
const SID *sid;
int acecnt;
int usidsz;
int gsidsz;
int wsidsz;
int asidsz;
int ssidsz;
@@ -1915,7 +1912,6 @@ static int buildacls_posix(struct MAPPING* const mapping[],
le32 grants;
usidsz = ntfs_sid_size(usid);
gsidsz = ntfs_sid_size(gsid);
wsidsz = ntfs_sid_size(worldsid);
asidsz = ntfs_sid_size(adminsid);
ssidsz = ntfs_sid_size(systemsid);
@@ -3138,7 +3134,6 @@ static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc,
u16 tag;
u16 tagsset;
struct POSIX_ACE *pxace;
int acccnt;
mode_t denywrld;
mode_t allow;
mode_t deny;
@@ -3147,7 +3142,6 @@ static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc,
mode = 0;
pxace = posix_desc->acl.ace;
acccnt = posix_desc->acccnt;
tagsset = 0;
denywrld = 0;
/*
@@ -3887,12 +3881,10 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix(
int ntfs_build_permissions(const char *securattr,
const SID *usid, const SID *gsid, BOOL isdir)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
int perm;
BOOL adminowns;
BOOL groupowns;
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
adminowns = ntfs_same_sid(usid,adminsid)
|| ntfs_same_sid(gsid,adminsid);
groupowns = !adminowns && ntfs_same_sid(gsid,usid);
@@ -3922,6 +3914,7 @@ static unsigned long atoul(const char *p)
}
#endif
/*
* Build an internal representation of a SID
* Returns a copy in allocated memory if it succeeds
@@ -3964,35 +3957,6 @@ static SID *encodesid(const char *sidstr)
return (sid);
}
/*
* Early logging before the logs are redirected
*
* (not quite satisfactory : this appears before the ntfs-g banner,
* and with a different pid)
*/
static void log_early_error(const char *format, ...)
__attribute__((format(printf, 1, 2)));
static void log_early_error(const char *format, ...)
{
#ifndef __HAIKU__
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);
#endif
}
/*
* Get a single mapping item from buffer
*
@@ -4006,7 +3970,6 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid,
{
int src;
int dst;
char *p;
char *q;
char *pu;
char *pg;
@@ -4040,7 +4003,6 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid,
if (gotend) {
pu = pg = (char*)NULL;
/* decompose into uid, gid and sid */
p = item->maptext;
item->uidstr = item->maptext;
item->gidstr = strchr(item->uidstr, ':');
if (item->gidstr) {
@@ -4055,7 +4017,7 @@ static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid,
if (pu && pg)
*pu = *pg = '\0';
else {
log_early_error("Bad mapping item \"%s\"\n",
ntfs_log_early_error("Bad mapping item \"%s\"\n",
item->maptext);
free(item);
item = (struct MAPLIST*)NULL;
@@ -4184,7 +4146,7 @@ struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem)
if (pwd)
uid = pwd->pw_uid;
else
log_early_error("Invalid user \"%s\"\n",
ntfs_log_early_error("Invalid user \"%s\"\n",
item->uidstr);
}
}
@@ -4264,7 +4226,7 @@ struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem)
if (grp)
gid = grp->gr_gid;
else
log_early_error("Invalid group \"%s\"\n",
ntfs_log_early_error("Invalid group \"%s\"\n",
item->gidstr);
}
}
@@ -5,7 +5,7 @@
* Copyright (c) 2002-2005 Richard Russon
* Copyright (c) 2002-2008 Szabolcs Szakacsits
* Copyright (c) 2004-2007 Yura Pakhuchiy
* Copyright (c) 2007-2010 Jean-Pierre Andre
* Copyright (c) 2007-2011 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@@ -402,7 +402,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
ntfs_attr *na = NULL;
ntfschar *newname = NULL;
ATTR_RECORD *a;
BOOL cs;
le16 cs;
ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n",
(unsigned long long)ni->mft_no, type);
@@ -475,9 +475,10 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
}
cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
/* a file may be sparse though its unnamed data is not (cf $UsnJrnl) */
if (na->type == AT_DATA && na->name == AT_UNNAMED &&
((!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) ||
(((a->flags & ATTR_IS_SPARSE) && !NAttrSparse(na)) ||
(!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) {
errno = EIO;
ntfs_log_perror("Inode %lld has corrupt attribute flags "
@@ -588,6 +589,82 @@ int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn)
return -1;
}
#if PARTIAL_RUNLIST_UPDATING
/*
* Map the runlist of an attribute from some point to the end
*
* Returns 0 if success,
* -1 if it failed (errno telling why)
*/
static int ntfs_attr_map_partial_runlist(ntfs_attr *na, VCN vcn)
{
LCN lcn;
VCN last_vcn;
VCN highest_vcn;
VCN needed;
VCN existing_vcn;
runlist_element *rl;
ATTR_RECORD *a;
BOOL startseen;
ntfs_attr_search_ctx *ctx;
lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT)
return 0;
existing_vcn = (na->rl ? na->rl->vcn : -1);
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
if (!ctx)
return -1;
/* Get the last vcn in the attribute. */
last_vcn = na->allocated_size >> na->ni->vol->cluster_size_bits;
needed = vcn;
highest_vcn = 0;
startseen = FALSE;
do {
/* Find the attribute in the mft record. */
if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
needed, NULL, 0, ctx)) {
a = ctx->attr;
/* Decode and merge the runlist. */
rl = ntfs_mapping_pairs_decompress(na->ni->vol, a,
na->rl);
if (rl) {
na->rl = rl;
highest_vcn = le64_to_cpu(a->highest_vcn);
/* corruption detection */
if (((highest_vcn + 1) < last_vcn)
&& ((highest_vcn + 1) <= needed)) {
ntfs_log_error("Corrupt attribute list\n");
rl = (runlist_element*)NULL;
}
needed = highest_vcn + 1;
if (!a->lowest_vcn)
startseen = TRUE;
/* reaching a previously allocated part ? */
if ((existing_vcn >= 0)
&& (needed >= existing_vcn)) {
needed = last_vcn;
}
}
} else
rl = (runlist_element*)NULL;
} while (rl && (needed < last_vcn));
ntfs_attr_put_search_ctx(ctx);
/* mark fully mapped if we did so */
if (rl && startseen)
NAttrSetFullyMapped(na);
return (rl ? 0 : -1);
}
#endif
/**
* ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute
* @na: ntfs attribute for which to map the runlist
@@ -1114,12 +1191,27 @@ static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count)
rli++;
}
size = min(end - pos, NTFS_BUF_SIZE);
written = ntfs_rl_pwrite(vol, rli, ofsi, pos, size, buf);
if (written <= 0) {
ntfs_log_perror("Failed to zero space");
goto err_free;
/*
* If the zeroed block is fully within a hole,
* we need not write anything, so advance as far
* as possible within the hole.
*/
if ((rli->lcn == (LCN)LCN_HOLE)
&& (ofsi <= pos)
&& (ofsi + (rli->length << vol->cluster_size_bits)
>= (pos + size))) {
size = min(end - pos, ofsi - pos
+ (rli->length << vol->cluster_size_bits));
pos += size;
} else {
written = ntfs_rl_pwrite(vol, rli, ofsi, pos,
size, buf);
if (written <= 0) {
ntfs_log_perror("Failed to zero space");
goto err_free;
}
pos += written;
}
pos += written;
}
ret = 0;
@@ -1149,9 +1241,27 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
"%lld\n", (long long)count, (long long)cur_vcn,
(long long)from_vcn, (long long)to_write, (long long)*ofs);
/* Map whole runlist to be able update mapping pairs later. */
/* Map the runlist to be able to update mapping pairs later. */
#if PARTIAL_RUNLIST_UPDATING
if ((!na->rl
|| !NAttrDataAppending(na))) {
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
} else {
/* make sure the previous non-hole is mapped */
rlc = *rl;
rlc--;
if (((*rl)->lcn == LCN_HOLE)
&& cur_vcn
&& (rlc->vcn < 0)) {
if (ntfs_attr_map_partial_runlist(na, cur_vcn - 1))
goto err_out;
}
}
#else
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
#endif
/* Restore @*rl, it probably get lost during runlist mapping. */
*rl = ntfs_attr_find_vcn(na, cur_vcn);
@@ -1510,14 +1620,27 @@ static int borrow_from_hole(ntfs_attr *na, runlist_element **prl,
if (undecided || nothole) {
runlist_element *orl = na->rl;
s64 olcn = (*prl)->lcn;
#if PARTIAL_RUNLIST_UPDATING
VCN prevblock;
#endif
/*
* Map the full runlist (needed to compute the
* compressed size), unless the runlist has not
* yet been created (data just made non-resident)
* Map the runlist, unless it has not been created.
* If appending data, a partial mapping from the
* end of previous block will do.
*/
irl = *prl - na->rl;
#if PARTIAL_RUNLIST_UPDATING
prevblock = pos >> cluster_size_bits;
if (prevblock)
prevblock--;
if (!NAttrBeingNonResident(na)
&& (NAttrDataAppending(na)
? ntfs_attr_map_partial_runlist(na,prevblock)
: ntfs_attr_map_whole_runlist(na))) {
#else
if (!NAttrBeingNonResident(na)
&& ntfs_attr_map_whole_runlist(na)) {
#endif
rl = (runlist_element*)NULL;
} else {
/*
@@ -1612,6 +1735,9 @@ static int borrow_from_hole(ntfs_attr *na, runlist_element **prl,
return (compressed_part);
}
static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize,
hole_type holes);
/**
* ntfs_attr_pwrite - positioned write to an ntfs attribute
* @na: ntfs attribute to write to
@@ -1703,11 +1829,32 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
fullcount = count;
/* If the write reaches beyond the end, extend the attribute. */
old_data_size = na->data_size;
/* identify whether this is appending to a non resident data attribute */
if ((na->type == AT_DATA) && (pos >= old_data_size)
&& NAttrNonResident(na))
NAttrSetDataAppending(na);
if (pos + count > na->data_size) {
if (ntfs_attr_truncate(na, pos + count)) {
#if PARTIAL_RUNLIST_UPDATING
/*
* When appending data, the attribute is first extended
* before being filled with data. This may cause the
* attribute to be made temporarily sparse, which
* implies reformating the inode and reorganizing the
* full runlist. To avoid unnecessary reorganization,
* we avoid sparse testing until the data is filled in.
*/
if (ntfs_attr_truncate_i(na, pos + count,
(NAttrDataAppending(na) ?
HOLES_DELAY : HOLES_OK))) {
ntfs_log_perror("Failed to enlarge attribute");
goto errno_set;
}
#else
if (ntfs_attr_truncate_i(na, pos + count, HOLES_OK)) {
ntfs_log_perror("Failed to enlarge attribute");
goto errno_set;
}
#endif
/* resizing may change the compression mode */
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
@@ -1768,8 +1915,29 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
/* Handle writes beyond initialized_size. */
if (pos + count > na->initialized_size) {
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
#if PARTIAL_RUNLIST_UPDATING
/*
* When appending, we only need to map the end of the runlist,
* starting at the last previously allocated run, so that
* we are able a new one to it.
* However, for compressed file, we need the full compression
* block, which may be split in several extents.
*/
if (NAttrDataAppending(na)) {
VCN block_begin = pos >> vol->cluster_size_bits;
if (compressed)
block_begin &= -na->compression_block_clusters;
if (block_begin)
block_begin--;
if (ntfs_attr_map_partial_runlist(na, block_begin))
goto err_out;
if ((update_from == -1) || (block_begin < update_from))
update_from = block_begin;
} else
#endif
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
/*
* For a compressed attribute, we must be sure there is an
* available entry, and, when reopening a compressed file,
@@ -2046,9 +2214,13 @@ done:
* of the mapping list. This makes a difference only if
* inode extents were needed.
*/
#if PARTIAL_RUNLIST_UPDATING
updatemap = NAttrFullyMapped(na) || NAttrDataAppending(na);
#else
updatemap = (compressed
? NAttrFullyMapped(na) != 0 : update_from != -1);
if (updatemap)
#endif
if (updatemap) {
if (ntfs_attr_update_mapping_pairs(na,
(update_from < 0 ? 0 : update_from))) {
/*
@@ -2058,6 +2230,10 @@ done:
total = -1;
goto out;
}
if (!wasnonresident)
NAttrClearBeingNonResident(na);
NAttrClearDataAppending(na);
}
out:
ntfs_log_leave("\n");
return total;
@@ -2119,7 +2295,8 @@ err_out:
if (updatemap)
ntfs_attr_update_mapping_pairs(na, 0);
/* Restore original data_size if needed. */
if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size))
if (need_to.undo_data_size
&& ntfs_attr_truncate_i(na, old_data_size, HOLES_OK))
ntfs_log_perror("Failed to restore data_size");
errno = eo;
errno_set:
@@ -2137,7 +2314,6 @@ int ntfs_attr_pclose(ntfs_attr *na)
ntfs_attr_search_ctx *ctx = NULL;
runlist_element *rl;
int eo;
s64 hole;
int compressed_part;
BOOL compressed;
@@ -2249,7 +2425,6 @@ int ntfs_attr_pclose(ntfs_attr *na)
goto rl_err_out;
}
if (rl->lcn < (LCN)0) {
hole = rl->vcn + rl->length;
if (rl->lcn != (LCN)LCN_HOLE) {
errno = EIO;
ntfs_log_perror("%s: Unexpected LCN (%lld)",
@@ -2302,6 +2477,7 @@ retry:
goto out;
}
out:
NAttrClearComprClosing(na);
ntfs_log_leave("\n");
return (!ok);
rl_err_out:
@@ -2358,6 +2534,7 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt,
{
s64 br;
u8 *end;
BOOL warn;
ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n",
(unsigned long long)na->ni->mft_no, na->type,
@@ -2371,9 +2548,11 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt,
if (br <= 0)
return br;
br /= bk_size;
/* log errors unless silenced */
warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol);
for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst +
bk_size)
ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size);
ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn);
/* Finally, return the number of blocks read. */
return br;
}
@@ -3371,6 +3550,14 @@ int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type,
min_size = sle64_to_cpu(ad->min_size);
max_size = sle64_to_cpu(ad->max_size);
/* The $AttrDef generated by Windows specifies 2 as min_size for the
* volume name attribute, but in reality Windows sets it to 0 when
* clearing the volume name. If we want to be able to clear the volume
* name we must also accept 0 as min_size, despite the $AttrDef
* definition. */
if(type == AT_VOLUME_NAME)
min_size = 0;
if ((min_size && (size < min_size)) ||
((max_size > 0) && (size > max_size))) {
@@ -4123,7 +4310,7 @@ add_non_resident:
goto rm_attr_err_out;
}
/* Resize and set attribute value. */
if (ntfs_attr_truncate(na, size) ||
if (ntfs_attr_truncate_i(na, size, HOLES_OK) ||
(val && (ntfs_attr_pwrite(na, 0, size, val) != size))) {
err = errno;
ntfs_log_perror("Failed to initialize just added attribute");
@@ -4704,6 +4891,7 @@ cluster_free_err_out:
ntfs_log_trace("Eeek! Failed to release allocated clusters in error "
"code path. Leaving inconsistent metadata...\n");
NAttrClearNonResident(na);
NAttrClearFullyMapped(na);
na->allocated_size = na->data_size;
na->rl = NULL;
free(rl);
@@ -4827,7 +5015,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize,
return (ret);
}
/* Resize non-resident attribute */
return ntfs_attr_truncate(na, newsize);
return ntfs_attr_truncate_i(na, newsize, HOLES_OK);
} else if (errno != ENOSPC && errno != EPERM) {
err = errno;
ntfs_log_perror("Failed to make attribute non-resident");
@@ -4864,6 +5052,15 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize,
ntfs_attr_close(tna);
continue;
}
if ((tna->type == AT_DATA) && !tna->name_len) {
/*
* If we had to make the unnamed data attribute
* non-resident, propagate its new allocated size
* to all name attributes and directory indexes
*/
tna->ni->allocated_size = tna->allocated_size;
NInoFileNameSetDirty(tna->ni);
}
if (((tna->data_flags & ATTR_COMPRESSION_MASK)
== ATTR_IS_COMPRESSED)
&& ntfs_attr_pclose(tna)) {
@@ -5166,7 +5363,6 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
* record is in a transiently corrupted state at this moment in time.
*/
if (ntfs_cluster_free(vol, na, 0, -1) < 0) {
err = errno;
ntfs_log_perror("Eeek! Failed to release allocated clusters");
ntfs_log_trace("Ignoring error and leaving behind wasted "
"clusters.\n");
@@ -5178,6 +5374,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
/* Update in-memory struct ntfs_attr. */
NAttrClearNonResident(na);
NAttrClearFullyMapped(na);
NAttrClearSparse(na);
NAttrClearEncrypted(na);
na->initialized_size = na->data_size;
@@ -5192,7 +5389,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
* update allocated and compressed size.
*/
static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
ntfs_attr_search_ctx *ctx)
hole_type holes, ntfs_attr_search_ctx *ctx)
{
int sparse, ret = 0;
@@ -5204,15 +5401,21 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
a->allocated_size = cpu_to_sle64(na->allocated_size);
/* Update sparse bit. */
sparse = ntfs_rl_sparse(na->rl);
if (sparse == -1) {
errno = EIO;
goto error;
/* Update sparse bit, unless this is an intermediate state */
if (holes == HOLES_DELAY)
sparse = (a->flags & ATTR_IS_SPARSE) != const_cpu_to_le16(0);
else {
sparse = ntfs_rl_sparse(na->rl);
if (sparse == -1) {
errno = EIO;
goto error;
}
}
/* Attribute become sparse. */
if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) {
/* Check whether attribute becomes sparse, unless check is delayed. */
if ((holes != HOLES_DELAY)
&& sparse
&& !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) {
/*
* Move attribute to another mft record, if attribute is too
* small to add compressed_size field to it and we have no
@@ -5245,6 +5448,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
NAttrSetSparse(na);
a->flags |= ATTR_IS_SPARSE;
na->data_flags = a->flags;
a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows
set it so, even if attribute is not actually compressed. */
@@ -5264,6 +5468,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
NAttrClearSparse(na);
a->flags &= ~ATTR_IS_SPARSE;
na->data_flags = a->flags;
a->compression_unit = 0;
memmove((u8*)a + le16_to_cpu(a->name_offset) - 8,
@@ -5278,7 +5483,8 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
}
/* Update compressed size if required. */
if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) {
if (NAttrFullyMapped(na)
&& (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))) {
s64 new_compr_size;
new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl);
@@ -5310,7 +5516,8 @@ error: ret = -3; goto out;
/**
* ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs
*/
static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn)
static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn,
hole_type holes)
{
ntfs_attr_search_ctx *ctx;
ntfs_inode *ni, *base_ni;
@@ -5338,6 +5545,59 @@ retry:
return -1;
}
#if PARTIAL_RUNLIST_UPDATING
/*
* For a file just been made sparse, we will have
* to reformat the first extent, so be sure the
* runlist is fully mapped and fully processed.
* Same if the file was sparse and is not any more.
* Note : not needed if the full runlist is to be processed
*/
if ((holes != HOLES_DELAY)
&& (!NAttrFullyMapped(na) || from_vcn)
&& !(na->data_flags & ATTR_IS_COMPRESSED)) {
BOOL changed;
if (!(na->data_flags & ATTR_IS_SPARSE)) {
int sparse;
runlist_element *xrl;
/*
* If attribute was not sparse, we only
* have to check whether there is a hole
* in the updated region.
*/
xrl = na->rl;
if (xrl->lcn == LCN_RL_NOT_MAPPED)
xrl++;
sparse = ntfs_rl_sparse(xrl);
if (sparse < 0) {
ntfs_log_error("Could not check whether sparse\n");
errno = EIO;
return (-1);
}
changed = sparse > 0;
} else {
/*
* If attribute was sparse, the compressed
* size has been maintained, and it gives
* and easy way to check whether the
* attribute is still sparse.
*/
changed = (((na->data_size - 1)
| (na->ni->vol->cluster_size - 1)) + 1)
== na->compressed_size;
}
if (changed) {
if (ntfs_attr_map_whole_runlist(na)) {
ntfs_log_error("Could not map whole for sparse change\n");
errno = EIO;
return (-1);
}
from_vcn = 0;
}
}
#endif
if (na->ni->nr_extents == -1)
base_ni = na->ni->base_ni;
else
@@ -5399,7 +5659,7 @@ retry:
continue;
}
switch (ntfs_attr_update_meta(a, na, m, ctx)) {
switch (ntfs_attr_update_meta(a, na, m, holes, ctx)) {
case -1: return -1;
case -2: goto retry;
case -3: goto put_err_out;
@@ -5667,7 +5927,7 @@ int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn)
int ret;
ntfs_log_enter("Entering\n");
ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn);
ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn, HOLES_OK);
ntfs_log_leave("\n");
return ret;
}
@@ -5837,7 +6097,8 @@ put_err_out:
* ERANGE - @newsize is not valid for the attribute type of @na.
* ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST.
*/
static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize)
static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize,
hole_type holes)
{
LCN lcn_seek_from;
VCN first_free_vcn;
@@ -5864,6 +6125,8 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize)
return -1;
}
if (na->type == AT_DATA)
NAttrSetDataAppending(na);
/* Save for future use. */
org_alloc_size = na->allocated_size;
/* The first cluster outside the new allocation. */
@@ -5874,16 +6137,33 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize)
* clusters if there is a change.
*/
if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) {
#if PARTIAL_RUNLIST_UPDATING
s64 start_update;
/*
* Update from the last previously allocated run,
* as we may have to expand an existing hole.
*/
start_update = na->allocated_size >> vol->cluster_size_bits;
if (start_update)
start_update--;
if (ntfs_attr_map_partial_runlist(na, start_update)) {
ntfs_log_perror("failed to map partial runlist");
return -1;
}
#else
if (ntfs_attr_map_whole_runlist(na)) {
ntfs_log_perror("ntfs_attr_map_whole_runlist failed");
return -1;
}
#endif
/*
* If we extend $DATA attribute on NTFS 3+ volume, we can add
* sparse runs instead of real allocation of clusters.
*/
if (na->type == AT_DATA && vol->major_ver >= 3) {
if ((na->type == AT_DATA) && (vol->major_ver >= 3)
&& (holes != HOLES_NO)) {
rl = ntfs_malloc(0x1000);
if (!rl)
return -1;
@@ -5953,8 +6233,11 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize)
/* Prepare to mapping pairs update. */
na->allocated_size = first_free_vcn << vol->cluster_size_bits;
/* Write mapping pairs for new runlist. */
if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >>
vol->cluster_size_bits*/)) {
#if PARTIAL_RUNLIST_UPDATING
if (ntfs_attr_update_mapping_pairs_i(na, start_update, holes)) {
#else
if (ntfs_attr_update_mapping_pairs(na, 0)) {
#endif
err = errno;
ntfs_log_perror("Mapping pairs update failed");
goto rollback;
@@ -6040,12 +6323,13 @@ put_err_out:
}
static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize)
static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize,
hole_type holes)
{
int ret;
ntfs_log_enter("Entering\n");
ret = ntfs_non_resident_attr_expand_i(na, newsize);
ret = ntfs_non_resident_attr_expand_i(na, newsize, holes);
ntfs_log_leave("\n");
return ret;
}
@@ -6054,6 +6338,7 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize)
* ntfs_attr_truncate - resize an ntfs attribute
* @na: open ntfs attribute to resize
* @newsize: new size (in bytes) to which to resize the attribute
* @holes: how to create a hole if expanding
*
* Change the size of an open ntfs attribute @na to @newsize bytes. If the
* attribute is made bigger and the attribute is resident the newly
@@ -6070,7 +6355,8 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize)
* EOPNOTSUPP - The desired resize is not implemented yet.
* EACCES - Encrypted attribute.
*/
int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize,
hole_type holes)
{
int ret = STATUS_ERROR;
s64 fullsize;
@@ -6135,7 +6421,8 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
else
fullsize = newsize;
if (fullsize > na->data_size)
ret = ntfs_non_resident_attr_expand(na, fullsize);
ret = ntfs_non_resident_attr_expand(na, fullsize,
holes);
else
ret = ntfs_non_resident_attr_shrink(na, fullsize);
} else
@@ -6144,7 +6431,30 @@ out:
ntfs_log_leave("Return status %d\n", ret);
return ret;
}
/*
* Resize an attribute, creating a hole if relevant
*/
int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
{
int r;
r = ntfs_attr_truncate_i(na, newsize, HOLES_OK);
NAttrClearDataAppending(na);
NAttrClearBeingNonResident(na);
return (r);
}
/*
* Resize an attribute, avoiding hole creation
*/
int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize)
{
return (ntfs_attr_truncate_i(na, newsize, HOLES_NO));
}
/*
* Stuff a hole in a compressed file
*
@@ -6205,7 +6515,7 @@ static int stuff_hole(ntfs_attr *na, const s64 pos)
if (!ret
&& ((na->initialized_size + end_size) < pos)
&& ntfs_non_resident_attr_expand(na,
pos - end_size))
pos - end_size, HOLES_OK))
ret = -1;
else
na->initialized_size
@@ -6285,6 +6595,89 @@ err_exit:
return ret;
}
/*
* Read some data from a data attribute
*
* Returns the amount of data read, negative if there was an error
*/
int ntfs_attr_data_read(ntfs_inode *ni,
ntfschar *stream_name, int stream_name_len,
char *buf, size_t size, off_t offset)
{
ntfs_attr *na = NULL;
int res, total = 0;
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
if ((size_t)offset < (size_t)na->data_size) {
if (offset + size > (size_t)na->data_size)
size = na->data_size - offset;
while (size) {
res = ntfs_attr_pread(na, offset, size, buf + total);
if ((off_t)res < (off_t)size)
ntfs_log_perror("ntfs_attr_pread partial read "
"(%lld : %lld <> %d)",
(long long)offset,
(long long)size, res);
if (res <= 0) {
res = -errno;
goto exit;
}
size -= res;
offset += res;
total += res;
}
}
res = total;
exit:
if (na)
ntfs_attr_close(na);
return res;
}
/*
* Write some data into a data attribute
*
* Returns the amount of data written, negative if there was an error
*/
int ntfs_attr_data_write(ntfs_inode *ni,
ntfschar *stream_name, int stream_name_len,
char *buf, size_t size, off_t offset)
{
ntfs_attr *na = NULL;
int res, total = 0;
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
while (size) {
res = ntfs_attr_pwrite(na, offset, size, buf + total);
if (res < (s64)size)
ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: "
"%lld <> %d)", (long long)offset,
(long long)size, res);
if (res <= 0) {
res = -errno;
goto exit;
}
size -= res;
offset += res;
total += res;
}
res = total;
exit:
if (na)
ntfs_attr_close(na);
return res;
}
int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name,
@@ -4,6 +4,7 @@
* Copyright (c) 2000-2004 Anton Altaparmakov
* Copyright (c) 2004-2005 Yura Pakhuchiy
* Copyright (c) 2006-2007 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
@@ -57,6 +58,12 @@ typedef enum {
LCN_EIO = -5,
} ntfs_lcn_special_values;
typedef enum { /* ways of processing holes when expanding */
HOLES_NO,
HOLES_OK,
HOLES_DELAY
} hole_type;
/**
* struct ntfs_attr_search_ctx - search context used in attribute search functions
* @mrec: buffer containing mft record to search
@@ -201,6 +208,7 @@ typedef enum {
NA_NonResident, /* 1: Attribute is not resident. */
NA_BeingNonResident, /* 1: Attribute is being made not resident. */
NA_FullyMapped, /* 1: Attribute has been fully mapped */
NA_DataAppending, /* 1: Attribute is being appended to */
NA_ComprClosing, /* 1: Compressed attribute is being closed */
} ntfs_attr_state_bits;
@@ -224,6 +232,10 @@ typedef enum {
#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped)
#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped)
#define NAttrDataAppending(na) test_nattr_flag(na, DataAppending)
#define NAttrSetDataAppending(na) set_nattr_flag(na, DataAppending)
#define NAttrClearDataAppending(na) clear_nattr_flag(na, DataAppending)
#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing)
#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing)
#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing)
@@ -332,6 +344,7 @@ extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn);
extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize);
extern int ntfs_attr_truncate_solid(ntfs_attr *na, const s64 newsize);
/**
* get_attribute_value_length - return the length of the value of an attribute
@@ -370,6 +383,12 @@ extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type,
extern int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type,
ntfschar *name, u32 name_len);
extern s64 ntfs_attr_get_free_bits(ntfs_attr *na);
extern int ntfs_attr_data_read(ntfs_inode *ni,
ntfschar *stream_name, int stream_name_len,
char *buf, size_t size, off_t offset);
extern int ntfs_attr_data_write(ntfs_inode *ni,
ntfschar *stream_name, int stream_name_len,
char *buf, size_t size, off_t offset);
#endif /* defined _NTFS_ATTRIB_H */
@@ -1,7 +1,7 @@
/**
* cache.c : deal with LRU caches
*
* Copyright (c) 2008-2009 Jean-Pierre Andre
* 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
@@ -298,7 +298,7 @@ struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache,
if (cache->most_recent_entry)
cache->most_recent_entry->previous = current;
cache->most_recent_entry = current;
memcpy(current->fixed, item->fixed, cache->fixed_size);
memcpy(current->payload, item->payload, cache->fixed_size);
if (item->varsize) {
if (current->variable) {
memcpy(current->variable,
@@ -376,7 +376,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
int flags)
{
struct CACHED_GENERIC *current;
struct CACHED_GENERIC *previous;
struct CACHED_GENERIC *next;
struct HASH_ENTRY *link;
int count;
@@ -412,7 +411,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
* Search sequentially in LRU list
*/
current = cache->most_recent_entry;
previous = (struct CACHED_GENERIC*)NULL;
while (current) {
if (!compare(current, item)) {
next = current->next;
@@ -423,7 +421,6 @@ int ntfs_invalidate_cache(struct CACHE_HEADER *cache,
current = next;
count++;
} else {
previous = current;
current = current->next;
}
}
@@ -1,7 +1,7 @@
/*
* cache.h : deal with indexed LRU caches
*
* Copyright (c) 2008-2009 Jean-Pierre Andre
* 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
@@ -29,11 +29,7 @@ struct CACHED_GENERIC {
struct CACHED_GENERIC *previous;
void *variable;
size_t varsize;
union {
/* force alignment for pointers and u64 */
u64 u64align;
void *ptralign;
} fixed[0];
union ALIGNMENT payload[0];
} ;
struct CACHED_INODE {
@@ -41,6 +37,7 @@ struct CACHED_INODE {
struct CACHED_INODE *previous;
const char *pathname;
size_t varsize;
union ALIGNMENT payload[0];
/* above fields must match "struct CACHED_GENERIC" */
u64 inum;
} ;
@@ -50,6 +47,7 @@ struct CACHED_NIDATA {
struct CACHED_NIDATA *previous;
const char *pathname; /* not used */
size_t varsize; /* not used */
union ALIGNMENT payload[0];
/* above fields must match "struct CACHED_GENERIC" */
u64 inum;
ntfs_inode *ni;
@@ -60,6 +58,7 @@ struct CACHED_LOOKUP {
struct CACHED_LOOKUP *previous;
const char *name;
size_t namesize;
union ALIGNMENT payload[0];
/* above fields must match "struct CACHED_GENERIC" */
u64 parent;
u64 inum;
@@ -5,7 +5,7 @@
* Copyright (c) 2004-2005 Anton Altaparmakov
* Copyright (c) 2004-2006 Szabolcs Szakacsits
* Copyright (c) 2005 Yura Pakhuchiy
* Copyright (c) 2009-2010 Jean-Pierre Andre
* Copyright (c) 2009-2011 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
@@ -62,6 +62,10 @@
#include "logging.h"
#include "misc.h"
#undef le16_to_cpup
/* the standard le16_to_cpup() crashes for unaligned data on some processors */
#define le16_to_cpup(p) (*(u8*)(p) + (((u8*)(p))[1] << 8))
/**
* enum ntfs_compression_constants - constants used in the compression code
*/
@@ -77,259 +81,215 @@ typedef enum {
NTFS_SB_IS_COMPRESSED = 0x8000,
} ntfs_compression_constants;
#define THRESHOLD 3 /* minimal match length for compression */
#define NIL NTFS_SB_SIZE /* End of tree's node */
struct COMPRESS_CONTEXT {
const unsigned char *inbuf;
unsigned int len;
unsigned int nbt;
int match_position;
unsigned int match_length;
u16 lson[NTFS_SB_SIZE + 1];
u16 rson[NTFS_SB_SIZE + 257];
u16 dad[NTFS_SB_SIZE + 1];
int bufsize;
int size;
int rel;
int mxsz;
s16 head[256];
s16 lson[NTFS_SB_SIZE];
s16 rson[NTFS_SB_SIZE];
} ;
/*
* Initialize the match tree
*/
static void ntfs_init_compress_tree(struct COMPRESS_CONTEXT *pctx)
{
int i;
for (i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++)
pctx->rson[i] = NIL; /* root */
for (i = 0; i < NTFS_SB_SIZE; i++)
pctx->dad[i] = NIL; /* node */
}
/*
* Insert a new node into match tree for quickly locating
* further similar strings
*/
static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx,
unsigned int r)
{
unsigned int pp;
BOOL less;
BOOL done;
const unsigned char *key;
int c;
unsigned long mxi;
unsigned int mxl;
mxl = (1 << (16 - pctx->nbt)) + 2;
less = FALSE;
done = FALSE;
key = &pctx->inbuf[r];
pp = NTFS_SB_SIZE + 1 + key[0];
pctx->rson[r] = pctx->lson[r] = NIL;
pctx->match_length = 0;
do {
if (!less) {
if (pctx->rson[pp] != NIL)
pp = pctx->rson[pp];
else {
pctx->rson[pp] = r;
pctx->dad[r] = pp;
done = TRUE;
}
} else {
if (pctx->lson[pp] != NIL)
pp = pctx->lson[pp];
else {
pctx->lson[pp] = r;
pctx->dad[r] = pp;
done = TRUE;
}
}
if (!done) {
register unsigned long i;
register const unsigned char *p1,*p2;
i = 1;
mxi = NTFS_SB_SIZE - r;
if (mxi < 2)
less = FALSE;
else {
p1 = key;
p2 = &pctx->inbuf[pp];
/* this loop has a significant impact on performances */
do {
} while ((p1[i] == p2[i]) && (++i < mxi));
less = (i < mxi) && (p1[i] < p2[i]);
}
if (i >= THRESHOLD) {
if (i > pctx->match_length) {
pctx->match_position =
r - pp + 2*NTFS_SB_SIZE - 1;
if ((pctx->match_length = i) > mxl) {
i = pctx->rson[pp];
pctx->rson[r] = i;
pctx->dad[i] = r;
i = pctx->lson[pp];
pctx->lson[r] = i;
pctx->dad[i] = r;
i = pctx->dad[pp];
pctx->dad[r] = i;
if (pctx->rson[i] == pp)
pctx->rson[i] = r;
else
pctx->lson[i] = r;
/* remove pp */
pctx->dad[pp] = NIL;
done = TRUE;
pctx->match_length = mxl;
}
} else
if ((i == pctx->match_length)
&& ((c = (r - pp + 2*NTFS_SB_SIZE - 1))
< pctx->match_position))
pctx->match_position = c;
}
}
} while (!done);
}
/*
* Search for the longest previous string matching the
* current one
* Search for the longest sequence matching current position
*
* Returns the end of the longest current string which matched
* or zero if there was a bug
* A binary tree is maintained to locate all previously met sequences,
* and this function has to be called for all of them.
*
* This function is heavily used, it has to be optimized carefully
*
* Returns the size of the longest match,
* zero if no match is found.
*/
static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx,
unsigned int rr, int dd)
static int ntfs_best_match(struct COMPRESS_CONTEXT *pctx, int i)
{
unsigned int bestlen = 0;
s16 *prev;
int node;
register long j;
long maxpos;
long startj;
long bestj;
int bufsize;
int bestnode;
register const unsigned char *p1,*p2;
do {
rr++;
if (pctx->match_length > 0)
pctx->match_length--;
if (!pctx->len) {
ntfs_log_error("compress bug : void run\n");
goto bug;
}
if (--pctx->len) {
if (rr >= NTFS_SB_SIZE) {
ntfs_log_error("compress bug : buffer overflow\n");
goto bug;
}
if (((rr + bestlen) < NTFS_SB_SIZE)) {
while ((unsigned int)(1 << pctx->nbt)
<= (rr - 1))
pctx->nbt++;
ntfs_new_node(pctx,rr);
if (pctx->match_length > bestlen)
bestlen = pctx->match_length;
} else
if (dd > 0) {
rr += dd;
if ((int)pctx->match_length > dd)
pctx->match_length -= dd;
else
pctx->match_length = 0;
if ((int)pctx->len < dd) {
ntfs_log_error("compress bug : run overflows\n");
goto bug;
p1 = pctx->inbuf;
node = pctx->head[p1[i] & 255];
if (node >= 0) {
/* search the best match at current position */
bestnode = node;
bufsize = pctx->bufsize;
/* restrict matches to the longest allowed sequence */
maxpos = bufsize;
if ((i + pctx->mxsz) < maxpos)
maxpos = i + pctx->mxsz;
startj = i + 1 - maxpos;
bestj = startj;
/* make indexes relative to end of allowed position */
p1 = &p1[maxpos];
if (startj < 0) {
do {
/* indexes are negative */
p2 = &p1[node - i];
/* no need to compare the first byte */
j = startj;
/* the second byte cannot lead to useful compression */
if (p1[j] == p2[j]) {
j++;
if (j < 0) {
do {
} while ((p1[j] == p2[j])
&& (++j < 0));
}
/* remember the match, if better */
if (j > bestj) {
bestj = j;
bestnode = node;
}
pctx->len -= dd;
dd = 0;
}
/* walk in the tree in the right direction */
if ((j < 0) && (p1[j] < p2[j]))
prev = &pctx->lson[node];
else
prev = &pctx->rson[node];
node = *prev;
/* stop if reaching a leaf or maximum length */
} while ((node >= 0) && (j < 0));
/* put the node into the tree if we reached a leaf */
if (node < 0)
*prev = i;
}
} while (dd-- > 0);
return (rr);
bug :
return (0);
/* done, return the best match */
pctx->size = bestj + maxpos - i;
pctx->rel = bestnode - i;
} else {
pctx->head[p1[i] & 255] = i;
pctx->size = 0;
pctx->rel = 0;
}
return (pctx->size);
}
/*
* Compress an input block
* Compress a 4096-byte block
*
* Returns the size of the compressed block (including header)
* or zero if there was an error
* Returns a header of two bytes followed by the compressed data.
* If compression is not effective, the header and an uncompressed
* block is returned.
*
* Note : two bytes may be output before output buffer overflow
* is detected, so a 4100-bytes output buffer must be reserved.
*
* Returns the size of the compressed block, including the
* header (minimal size is 2, maximum size is 4098)
* 0 if an error has been met.
*/
static unsigned int ntfs_compress_block(const char *inbuf,
unsigned int size, char *outbuf)
static unsigned int ntfs_compress_block(const char *inbuf, int bufsize,
char *outbuf)
{
struct COMPRESS_CONTEXT *pctx;
char *ptag;
int dd;
unsigned int rr;
unsigned int last_match_length;
unsigned int q;
int i; /* current position */
int j; /* end of best match from current position */
int k; /* end of best match from next position */
int offs; /* offset to best match */
int n;
int bp; /* bits to store offset */
int mxoff; /* max match offset : 1 << bp */
int mxsz2;
unsigned int xout;
unsigned int ntag;
unsigned int q; /* aggregated offset and size */
int done;
char *ptag; /* location reserved for a tag */
int tag; /* current value of tag */
int ntag; /* count of bits still undefined in tag */
pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT));
pctx = (struct COMPRESS_CONTEXT*)ntfs_malloc(sizeof(struct COMPRESS_CONTEXT));
if (pctx) {
for (n=0; n<NTFS_SB_SIZE; n++)
pctx->lson[n] = pctx->rson[n] = -1;
for (n=0; n<256; n++)
pctx->head[n] = -1;
pctx->inbuf = (const unsigned char*)inbuf;
ntfs_init_compress_tree(pctx);
pctx->bufsize = bufsize;
xout = 2;
ntag = 0;
n = 0;
i = 0;
bp = 4;
mxoff = 1 << bp;
pctx->mxsz = (1 << (16 - bp)) + 2;
tag = 0;
done = -1;
ntag = 8;
ptag = &outbuf[xout++];
*ptag = 0;
rr = 0;
pctx->nbt = 4;
pctx->len = size;
pctx->match_length = 0;
ntfs_new_node(pctx,0);
do {
if (pctx->match_length > pctx->len)
pctx->match_length = pctx->len;
if (pctx->match_length < THRESHOLD) {
pctx->match_length = 1;
if (ntag >= 8) {
ntag = 0;
ptag = &outbuf[xout++];
*ptag = 0;
while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) {
/* adjust the longest match we can output */
while (mxoff < i) {
bp++;
mxoff <<= 1;
pctx->mxsz = (pctx->mxsz + 2) >> 1;
}
/* search the best match at current position */
if (done < i)
do {
ntfs_best_match(pctx,++done);
} while (done < i);
j = i + pctx->size;
if ((j - i) > pctx->mxsz)
j = i + pctx->mxsz;
if ((j - i) > 2) {
offs = pctx->rel;
/* check whether there is a better run at i+1 */
ntfs_best_match(pctx,i+1);
done = i+1;
k = i + 1 + pctx->size;
mxsz2 = pctx->mxsz;
if (mxoff <= i)
mxsz2 = (pctx->mxsz + 2) >> 1;
if ((k - i) > mxsz2)
k = i + mxsz2;
if (k > (j + 1)) {
/* issue a single byte */
outbuf[xout++] = inbuf[i];
i++;
} else {
q = (~offs << (16 - bp))
+ (j - i - 3);
outbuf[xout++] = q & 255;
outbuf[xout++] = (q >> 8) & 255;
tag |= (1 << (8 - ntag));
i = j;
}
outbuf[xout++] = inbuf[rr];
ntag++;
} else {
while ((unsigned int)(1 << pctx->nbt)
<= (rr - 1))
pctx->nbt++;
q = (pctx->match_position << (16 - pctx->nbt))
+ pctx->match_length - THRESHOLD;
if (ntag >= 8) {
ntag = 0;
ptag = &outbuf[xout++];
*ptag = 0;
}
*ptag |= 1 << ntag++;
outbuf[xout++] = q & 255;
outbuf[xout++] = (q >> 8) & 255;
outbuf[xout++] = inbuf[i];
i++;
}
last_match_length = pctx->match_length;
dd = last_match_length;
if (dd-- > 0) {
rr = ntfs_nextmatch(pctx,rr,dd);
if (!rr)
goto bug;
/* store the tag if fully used */
if (!--ntag) {
*ptag = tag;
ntag = 8;
ptag = &outbuf[xout++];
tag = 0;
}
/*
* stop if input is exhausted or output has exceeded
* the maximum size. Two extra bytes have to be
* reserved in output buffer, as 3 bytes may be
* output in a loop.
*/
} while ((pctx->len > 0)
&& (rr < size) && (xout < (NTFS_SB_SIZE + 2)));
/* uncompressed must be full size, so accept if better */
if (xout < (NTFS_SB_SIZE + 2)) {
}
/* store the last tag, if partially used */
if (ntag == 8)
xout--;
else
*ptag = tag;
/* uncompressed must be full size, accept if better */
if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) {
outbuf[0] = (xout - 3) & 255;
outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15);
} else {
memcpy(&outbuf[2],inbuf,size);
if (size < NTFS_SB_SIZE)
memset(&outbuf[size+2],0,NTFS_SB_SIZE - size);
memcpy(&outbuf[2],inbuf,bufsize);
if (bufsize < NTFS_SB_SIZE)
memset(&outbuf[bufsize+2], 0,
NTFS_SB_SIZE - bufsize);
outbuf[0] = 0xff;
outbuf[1] = 0x3f;
xout = NTFS_SB_SIZE + 2;
@@ -339,9 +299,7 @@ static unsigned int ntfs_compress_block(const char *inbuf,
xout = 0;
errno = ENOMEM;
}
return (xout); /* 0 for an error, > size if cannot compress */
bug :
return (0);
return (xout);
}
/**
@@ -1247,6 +1205,7 @@ static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl,
case 1 :
/* there is a single hole, may have to merge */
freerl->vcn = freevcn;
freerl->length = freecnt;
if (freerl[1].lcn == LCN_HOLE) {
freerl->length += freerl[1].length;
erl = freerl;
@@ -1507,12 +1466,12 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
* or -1 if there were an irrecoverable error (errno set)
*/
static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
const char *outbuf, s32 count, BOOL compress,
BOOL appending, VCN *update_from)
{
int rounded;
int written;
s32 rounded;
s32 written;
int clsz;
if (compress) {
@@ -1650,7 +1609,7 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
* (we are reopening an existing file to append to it)
* Decompress the data and append
*/
compsz = compressed_part << vol->cluster_size_bits;
compsz = (s32)compressed_part << vol->cluster_size_bits;
outbuf = (char*)ntfs_malloc(na->compression_block_size);
if (outbuf) {
if (appending) {
@@ -38,10 +38,10 @@ static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl
#define NTFS_BUG(msg) \
{ \
int ___i; \
int ___i = 1; \
ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \
ntfs_log_debug("Forcing segmentation fault!"); \
___i = ((int*)NULL)[1]; \
___i = ((int*)NULL)[___i]; \
}
#endif /* defined _NTFS_DEBUG_H */
@@ -3,6 +3,7 @@
*
* Copyright (c) 2004-2006 Anton Altaparmakov
* Copyright (c) 2004-2006 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
@@ -57,6 +58,9 @@
#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
@@ -154,6 +158,25 @@ int ntfs_device_free(struct ntfs_device *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
@@ -260,6 +283,9 @@ s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
total = written;
break;
}
if (NDevSync(dev) && total && dops->sync(dev)) {
total--; /* on sync error, return partially written */
}
ret = total;
out:
return ret;
@@ -533,6 +559,36 @@ s64 ntfs_device_size_get(struct ntfs_device *dev, int block_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,
@@ -682,6 +738,28 @@ int ntfs_device_sector_size_get(struct ntfs_device *dev)
return sect_size;
}
}
#elif defined(DIOCGSECTORSIZE)
{
/* FreeBSD */
size_t sect_size = 0;
if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, &sect_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, &sect_size)) {
ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n",
(int) sect_size);
return sect_size;
}
}
#else
errno = EOPNOTSUPP;
#endif
@@ -41,6 +41,7 @@ typedef enum {
ND_ReadOnly, /* 1: Device is read-only. */
ND_Dirty, /* 1: Device is dirty, needs sync. */
ND_Block, /* 1: Device is a block device. */
ND_Sync, /* 1: Device is mounted with "-o sync" */
} ntfs_device_state_bits;
#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state)
@@ -63,6 +64,10 @@ typedef enum {
#define NDevSetBlock(nd) set_ndev_flag(nd, Block)
#define NDevClearBlock(nd) clear_ndev_flag(nd, Block)
#define NDevSync(nd) test_ndev_flag(nd, Sync)
#define NDevSetSync(nd) set_ndev_flag(nd, Sync)
#define NDevClearSync(nd) clear_ndev_flag(nd, Sync)
/**
* struct ntfs_device -
*
@@ -102,6 +107,7 @@ struct ntfs_device_operations {
extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
struct ntfs_device_operations *dops, void *priv_data);
extern int ntfs_device_free(struct ntfs_device *dev);
extern int ntfs_device_sync(struct ntfs_device *dev);
extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count,
void *b);
@@ -1,38 +0,0 @@
/*
* device_io.c - Default device io operations. Originated from the Linux-NTFS project.
*
* Copyright (c) 2003 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
*/
#include "config.h"
#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
#ifndef __CYGWIN32__
/* Not on Cygwin; use standard Unix style low level device operations. */
#include "unix_io.c"
#else /* __CYGWIN32__ */
/* On Cygwin; use Win32 low level device operations. */
#include "win32_io.c"
#endif /* __CYGWIN32__ */
#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
@@ -257,7 +257,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
u8 *index_end;
ntfs_attr *ia_na;
int eo, rc;
u32 index_block_size, index_vcn_size;
u32 index_block_size;
u8 index_vcn_size_bits;
ntfs_log_trace("Entering\n");
@@ -356,7 +356,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
ntfs_attr_put_search_ctx(ctx);
if (mref)
return mref;
ntfs_log_debug("Entry not found.\n");
ntfs_log_debug("Entry not found - between root entries.\n");
errno = ENOENT;
return -1;
} /* Child node present, descend into it. */
@@ -378,11 +378,9 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
/* Determine the size of a vcn in the directory index. */
if (vol->cluster_size <= index_block_size) {
index_vcn_size = vol->cluster_size;
index_vcn_size_bits = vol->cluster_size_bits;
} else {
index_vcn_size = vol->sector_size;
index_vcn_size_bits = vol->sector_size_bits;
index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
}
/* Get the starting vcn of the index_block holding the child node. */
@@ -1039,7 +1037,7 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
INDEX_ENTRY *ie;
INDEX_ALLOCATION *ia = NULL;
int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo;
u32 index_block_size, index_vcn_size;
u32 index_block_size;
u8 index_block_size_bits, index_vcn_size_bits;
ntfs_log_trace("Entering.\n");
@@ -1131,11 +1129,9 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
}
index_block_size_bits = ffs(index_block_size) - 1;
if (vol->cluster_size <= index_block_size) {
index_vcn_size = vol->cluster_size;
index_vcn_size_bits = vol->cluster_size_bits;
} else {
index_vcn_size = vol->sector_size;
index_vcn_size_bits = vol->sector_size_bits;
index_vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
}
/* Are we jumping straight into the index allocation attribute? */
@@ -1517,7 +1513,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
else
ir->clusters_per_index_block =
ni->vol->indx_record_size >>
ni->vol->sector_size_bits;
NTFS_BLOCK_SIZE_BITS;
ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER));
ir->index.index_length = cpu_to_le32(index_len);
ir->index.allocated_size = cpu_to_le32(index_len);
@@ -1812,7 +1808,7 @@ search:
while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
0, NULL, 0, actx)) {
char *s;
BOOL case_sensitive = IGNORE_CASE;
IGNORE_CASE_BOOL case_sensitive = IGNORE_CASE;
errno = 0;
fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
@@ -1844,8 +1840,9 @@ search:
(long long unsigned)MREF_LE(fn->parent_directory));
continue;
}
if (fn->file_name_type == FILE_NAME_POSIX || case_sensitive_match)
if (case_sensitive_match
|| ((fn->file_name_type == FILE_NAME_POSIX)
&& NVolCaseSensitive(ni->vol)))
case_sensitive = CASE_SENSITIVE;
if (ntfs_names_are_equal(fn->file_name, fn->file_name_length,
@@ -2507,24 +2504,24 @@ int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni,
int res = 0;
int longlen = 0;
int shortlen = 0;
char newname[MAX_DOS_NAME_LENGTH + 1];
char newname[3*MAX_DOS_NAME_LENGTH + 1];
ntfschar oldname[MAX_DOS_NAME_LENGTH];
int oldlen;
ntfs_volume *vol;
u64 fnum;
u64 dnum;
BOOL closed = FALSE;
ntfschar *shortname = NULL;
ntfschar longname[NTFS_MAX_NAME_LEN];
vol = ni->vol;
fnum = ni->mft_no;
/* convert the string to the NTFS wide chars */
if (size > MAX_DOS_NAME_LENGTH)
size = MAX_DOS_NAME_LENGTH;
/* copy the string to insert a null char, and truncate */
if (size > 3*MAX_DOS_NAME_LENGTH)
size = 3*MAX_DOS_NAME_LENGTH;
strncpy(newname, value, size);
/* a long name may be truncated badly and be untranslatable */
newname[size] = 0;
/* convert the string to the NTFS wide chars, and truncate */
shortlen = ntfs_mbstoucs(newname, &shortname);
if (shortlen > MAX_DOS_NAME_LENGTH)
shortlen = MAX_DOS_NAME_LENGTH;
/* make sure the short name has valid chars */
if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) {
ntfs_inode_close_in_dir(ni,dir_ni);
@@ -139,7 +139,6 @@ static int fixup_loop(ntfs_inode *ni)
ntfs_attr *na;
ATTR_RECORD *a;
BOOL restart;
BOOL first;
int cnt;
int maxcnt;
int res = 0;
@@ -200,7 +199,6 @@ static int fixup_loop(ntfs_inode *ni)
if (na)
ntfs_attr_close(na);
}
first = FALSE;
} while (restart && !res);
if (ctx)
ntfs_attr_put_search_ctx(ctx);
@@ -701,7 +701,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic
if (ni->vol->cluster_size <= icx->block_size)
icx->vcn_size_bits = ni->vol->cluster_size_bits;
else
icx->vcn_size_bits = ni->vol->sector_size_bits;
icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
/* get the appropriate collation function */
icx->collate = ntfs_get_collate_function(ir->collation_rule);
if (!icx->collate) {
@@ -1121,6 +1121,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx)
INDEX_ENTRY *ie;
INDEX_BLOCK *ib = NULL;
VCN new_ib_vcn;
int ix_root_size;
int ret = STATUS_ERROR;
ntfs_log_trace("Entering\n");
@@ -1150,6 +1151,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx)
if (ntfs_ib_write(icx, ib))
goto clear_bmp;
retry :
ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx);
if (!ir)
goto clear_bmp;
@@ -1164,12 +1166,32 @@ static int ntfs_ir_reparent(ntfs_index_context *icx)
ir->index.index_length = cpu_to_le32(le32_to_cpu(ir->index.entries_offset)
+ le16_to_cpu(ie->length));
ir->index.allocated_size = ir->index.index_length;
ix_root_size = sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER)
+ le32_to_cpu(ir->index.allocated_size);
if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) +
le32_to_cpu(ir->index.allocated_size)))
ix_root_size)) {
/*
* When there is no space to build a non-resident
* index, we may have to move the root to an extent
*/
if ((errno == ENOSPC)
&& !ctx->al_entry
&& !ntfs_inode_add_attrlist(icx->ni)) {
ntfs_attr_put_search_ctx(ctx);
ctx = (ntfs_attr_search_ctx*)NULL;
ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len,
&ctx);
if (ir
&& !ntfs_attr_record_move_away(ctx, ix_root_size
- le32_to_cpu(ctx->attr->value_length))) {
ntfs_attr_put_search_ctx(ctx);
ctx = (ntfs_attr_search_ctx*)NULL;
goto retry;
}
}
/* FIXME: revert index root */
goto clear_bmp;
}
/*
* FIXME: do it earlier if we have enough space in IR (should always),
* so in error case we wouldn't lose the IB.
@@ -437,13 +437,12 @@ static int idata_cache_compare(const struct CACHED_GENERIC *cached,
void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref)
{
struct CACHED_NIDATA item;
int count;
item.inum = MREF(mref);
item.ni = (ntfs_inode*)NULL;
item.pathname = (const char*)NULL;
item.varsize = 0;
count = ntfs_invalidate_cache(vol->nidata_cache,
ntfs_invalidate_cache(vol->nidata_cache,
GENERIC(&item),idata_cache_compare,CACHE_FREE);
}
@@ -481,6 +480,9 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
} else {
ni = ntfs_inode_real_open(vol, mref);
}
if (!ni) {
debug_double_inode(item.inum, 0);
}
#else
ni = ntfs_inode_real_open(vol, mref);
#endif
@@ -571,6 +573,9 @@ int ntfs_inode_close(ntfs_inode *ni)
ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref)
{
u64 mft_no = MREF_LE(mref);
VCN extent_vcn;
runlist_element *rl;
ntfs_volume *vol;
ntfs_inode *ni = NULL;
ntfs_inode **extent_nis;
int i;
@@ -585,6 +590,37 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref)
(unsigned long long)mft_no,
(unsigned long long)base_ni->mft_no);
if (!base_ni->mft_no) {
/*
* When getting extents of MFT, we must be sure
* they are in the MFT part which has already
* been mapped, otherwise we fall into an endless
* recursion.
* Situations have been met where extents locations
* are described in themselves.
* This is a severe error which chkdsk cannot fix.
*/
vol = base_ni->vol;
extent_vcn = mft_no << vol->mft_record_size_bits
>> vol->cluster_size_bits;
rl = vol->mft_na->rl;
if (rl) {
while (rl->length
&& ((rl->vcn + rl->length) <= extent_vcn))
rl++;
}
if (!rl || (rl->lcn < 0)) {
ntfs_log_error("MFT is corrupt, cannot read"
" its unmapped extent record %lld\n",
(long long)mft_no);
ntfs_log_error("Note : chkdsk cannot fix this,"
" try ntfsfix\n");
errno = EIO;
ni = (ntfs_inode*)NULL;
goto out;
}
}
/* Is the extent inode already open and attached to the base inode? */
if (base_ni->nr_extents > 0) {
extent_nis = base_ni->extent_nis;
@@ -842,6 +878,12 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni)
else {
fnx->allocated_size = cpu_to_sle64(ni->allocated_size);
fnx->data_size = cpu_to_sle64(ni->data_size);
/*
* The file name record has also to be fixed if some
* attribute update implied the unnamed data to be
* made non-resident
*/
fn->allocated_size = fnx->allocated_size;
}
/* update or clear the reparse tag in the index */
fnx->reparse_point_tag = reparse_tag;
@@ -311,6 +311,7 @@ typedef enum {
#define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU)
typedef u64 MFT_REF;
typedef le64 leMFT_REF; /* a little-endian MFT_MREF */
#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \
((MFT_REF)(m) & MFT_REF_MASK_CPU)))
@@ -2222,11 +2223,11 @@ typedef struct {
/* The below field is NOT present for the quota defaults entry. */
SID sid; /* The SID of the user/object associated with
this quota entry. If this field is missing
then the INDEX_ENTRY is padded with zeros
to multiply of 8 which are not counted in
then the INDEX_ENTRY is padded to a multiple
of 8 with zeros which are not counted in
the data_length field. If the sid is present
then this structure is padded with zeros to
multiply of 8 and the padding is counted in
a multiple of 8 and the padding is counted in
the INDEX_ENTRY's data_length. */
} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY;
@@ -468,7 +468,7 @@ BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
u8 *kaddr = NULL;
RESTART_PAGE_HEADER *rstr1_ph = NULL;
RESTART_PAGE_HEADER *rstr2_ph = NULL;
int log_page_size, log_page_mask, err;
int log_page_size, err;
BOOL logfile_is_empty = TRUE;
u8 log_page_bits;
@@ -481,7 +481,6 @@ BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
if (size > (s64)MaxLogFileSize)
size = MaxLogFileSize;
log_page_size = DefaultLogPageSize;
log_page_mask = log_page_size - 1;
/*
* Use generic_ffs() instead of ffs() to enable the compiler to
* optimize log_page_size and log_page_bits into constants.
@@ -3,6 +3,7 @@
*
* 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
@@ -297,7 +298,6 @@ void ntfs_log_set_handler(ntfs_log_handler *handler)
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
@@ -390,6 +390,31 @@ out:
}
#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, ...)
{
#ifndef __HAIKU__
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);
#endif
}
#ifndef __HAIKU__
/**
* ntfs_log_handler_fprintf - Basic logging handler
@@ -586,7 +611,6 @@ int ntfs_log_handler_stderr(const char *function, const char *file,
#endif // __HAIKU__
/**
* ntfs_log_parse_option - Act upon command line options
* @option: Option flag
@@ -116,5 +116,8 @@ int ntfs_log_redirect(const char *function, const char *file, int line,
#define ntfs_log_leave(FORMAT, ARGS...)do {} while (0)
#endif /* DEBUG */
void ntfs_log_early_error(const char *format, ...)
__attribute__((format(printf, 1, 2)));
#endif /* _LOGGING_H_ */
@@ -216,8 +216,10 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
int ret = -1;
if (!ntfs_is_file_record(m->magic)) {
ntfs_log_error("Record %llu has no FILE magic (0x%x)\n",
(unsigned long long)MREF(mref), *(le32 *)m);
if (!NVolNoFixupWarn(vol))
ntfs_log_error("Record %llu has no FILE magic (0x%x)\n",
(unsigned long long)MREF(mref),
(int)le32_to_cpu(*(le32*)m));
goto err_out;
}
@@ -1190,7 +1192,7 @@ undo_alloc:
static int ntfs_mft_record_init(ntfs_volume *vol, s64 size)
{
int ret = -1;
ntfs_attr *mft_na, *mftbmp_na;
ntfs_attr *mft_na;
s64 old_data_initialized, old_data_size;
ntfs_attr_search_ctx *ctx;
@@ -1199,7 +1201,6 @@ static int ntfs_mft_record_init(ntfs_volume *vol, s64 size)
/* NOTE: Caller must sanity check vol, vol->mft_na and vol->mftbmp_na */
mft_na = vol->mft_na;
mftbmp_na = vol->mftbmp_na;
/*
* The mft record is outside the initialized data. Extend the mft data
@@ -1295,14 +1296,13 @@ undo_data_init:
static int ntfs_mft_rec_init(ntfs_volume *vol, s64 size)
{
int ret = -1;
ntfs_attr *mft_na, *mftbmp_na;
ntfs_attr *mft_na;
s64 old_data_initialized, old_data_size;
ntfs_attr_search_ctx *ctx;
ntfs_log_enter("Entering\n");
mft_na = vol->mft_na;
mftbmp_na = vol->mftbmp_na;
if (size > mft_na->allocated_size || size > mft_na->initialized_size) {
errno = EIO;
@@ -47,7 +47,8 @@
* 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(NTFS_RECORD *b, const u32 size)
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;
@@ -63,9 +64,14 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
(u32)(usa_ofs + (usa_count * 2)) > size ||
(size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
errno = EINVAL;
ntfs_log_perror("%s: magic: 0x%08x size: %d usa_ofs: %d "
"usa_count: %d", __FUNCTION__, *(le32 *)b,
size, usa_ofs, usa_count);
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. */
@@ -118,6 +124,16 @@ int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
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
@@ -25,8 +25,11 @@
#include "types.h"
#include "layout.h"
#include "volume.h"
extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size);
extern int ntfs_mst_post_read_fixup_warn(NTFS_RECORD *b, const u32 size,
BOOL warn);
extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size);
extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b);
@@ -107,7 +107,11 @@
*/
typedef struct {
GUID object_id;
union {
/* alignment may be needed to evaluate collations */
u32 alignment;
GUID guid;
} object_id;
} OBJECT_ID_INDEX_KEY;
typedef struct {
@@ -283,7 +287,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
if (size >= (s64)sizeof(GUID)) {
memcpy(&key.object_id,
&old_attr->object_id,sizeof(GUID));
size = sizeof(GUID);
if (!ntfs_index_lookup(&key,
sizeof(OBJECT_ID_INDEX_KEY), xo)) {
entry = (struct OBJECT_ID_INDEX*)xo->entry;
@@ -296,7 +299,6 @@ static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo,
memcpy(&old_attr->domain_id,
&entry->data.domain_id,
sizeof(GUID));
size = sizeof(OBJECT_ID_ATTR);
if (ntfs_index_rm(xo))
ret = -1;
}
@@ -1,7 +1,7 @@
/*
* param.h - Parameter values for ntfs-3g
*
* Copyright (c) 2009 Jean-Pierre Andre
* 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
@@ -44,12 +44,44 @@ enum {
*/
/* default option for compression */
#define DEFAULT_COMPRESSION FALSE
#define DEFAULT_COMPRESSION 1
/* (log2 of) number of clusters in a compression block for new files */
#define STANDARD_COMPRESSION_UNIT 4
/* maximum cluster size for allowing compression for new files */
#define MAX_COMPRESSION_CLUSTER_SIZE 4096
/*
* Use of big write buffers
*
* With small volumes, the cluster allocator may fail to allocate
* enough clusters when the volume is nearly full. At most a run
* can be allocated per bitmap chunk. So, there is a danger when the
* number of chunks (capacity/(32768*clsiz)) is less than the number
* of clusters in the biggest write buffer (131072/clsiz). Hence
* a safe minimal capacity is 4GB
*/
#define SAFE_CAPACITY_FOR_BIG_WRITES 0x100000000LL
/*
* Parameters for runlists
*/
/* only update the final extent of a runlist when appending data */
#define PARTIAL_RUNLIST_UPDATING 1
/*
* Parameters for user and xattr mappings
*/
#define XATTRMAPPINGFILE ".NTFS-3G/XattrMapping" /* default mapping file */
/*
* Parameters for path canonicalization
*/
#define MAPPERNAMELTH 256
/*
* Permission checking modes for high level and low level
*
@@ -58,7 +90,8 @@ enum {
*
* Stick to the recommended values unless you understand the consequences
* on protection and performances. Use of cacheing is good for
* performances, but bad on security.
* performances, but bad on security with internal fuse or external
* fuse older than 2.8
*
* Possible values for high level :
* 1 : no cache, kernel control (recommended)
@@ -67,7 +100,7 @@ enum {
*
* Possible values for low level :
* 2 : no cache, kernel control
* 3 : use kernel/fuse cache, kernel control
* 3 : use kernel/fuse cache, kernel control (external fuse >= 2.8)
* 5 : no cache, file system control (recommended)
* 8 : no cache, kernel control for ACLs
*
@@ -77,6 +110,10 @@ enum {
*/
#define HPERMSCONFIG 1
#if defined(FUSE_INTERNAL) || !defined(FUSE_VERSION) || (FUSE_VERSION < 28)
#define LPERMSCONFIG 5
#else
#define LPERMSCONFIG 3
#endif
#endif /* defined _NTFS_PARAM_H */
@@ -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 path[MAPPERNAMELTH + 24];
char name[MAPPERNAMELTH + 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
@@ -0,0 +1,24 @@
/*
* realpath.h - realpath() aware of device mapper
*/
#ifndef REALPATH_H
#define REALPATH_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_REALPATH
#define ntfs_realpath realpath
#else
extern char *ntfs_realpath(const char *path, char *resolved_path);
#endif
#ifdef linux
extern char *ntfs_realpath_canonicalize(const char *path, char *resolved_path);
#else
#define ntfs_realpath_canonicalize ntfs_realpath
#endif
#endif /* REALPATH_H */
@@ -200,8 +200,13 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname,
*/
lemref = entry->indexed_file;
mref = le64_to_cpu(lemref);
for (i=0; i<found->file_name_length; i++)
uname[i] = found->file_name[i];
if (NVolCaseSensitive(vol) || !vol->locase) {
for (i=0; i<found->file_name_length; i++)
uname[i] = found->file_name[i];
} else {
for (i=0; i<found->file_name_length; i++)
uname[i] = vol->locase[found->file_name[i]];
}
}
}
ntfs_index_ctx_put(icx);
@@ -5,7 +5,7 @@
* Copyright (c) 2002-2005 Richard Russon
* Copyright (c) 2002-2008 Szabolcs Szakacsits
* Copyright (c) 2004 Yura Pakhuchiy
* Copyright (c) 2007-2009 Jean-Pierre Andre
* Copyright (c) 2007-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
@@ -136,9 +136,10 @@ runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl,
if (!newrl) {
errno = ENOMEM;
rl = (runlist_element*)NULL;
} else
} else {
na->rl = newrl;
rl = &newrl[irl];
}
} else {
ntfs_log_error("Cannot extend unmapped runlist");
errno = EIO;
@@ -1418,28 +1419,18 @@ int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n)
{
s64 l = n;
int i;
s8 j;
i = 0;
do {
if (dst > dst_max)
goto err_out;
*dst++ = l;
i++;
while ((l > 0x7f) || (l < -0x80)) {
if (dst > dst_max)
goto err_out;
*dst++ = l & 0xffLL;
l >>= 8;
*dst++ = l;
i++;
} while (l != 0LL && l != -1LL);
j = (n >> 8 * (i - 1)) & 0xff;
/* If the sign bit is wrong, we need an extra byte. */
if (n < 0LL && j >= 0) {
if (dst > dst_max)
goto err_out;
i++;
*dst = (u8)-1;
} else if (n > 0LL && j < 0) {
if (dst > dst_max)
goto err_out;
i++;
*dst = 0;
}
return i;
err_out:
@@ -1633,7 +1624,7 @@ errno_set:
int ntfs_rl_truncate(runlist **arl, const VCN start_vcn)
{
runlist *rl;
BOOL is_end = FALSE;
/* BOOL is_end = FALSE; */
if (!arl || !*arl) {
errno = EINVAL;
@@ -1676,8 +1667,10 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn)
*/
if (rl->length) {
++rl;
/*
if (!rl->length)
is_end = TRUE;
*/
rl->vcn = start_vcn;
rl->length = 0;
}
@@ -1685,7 +1678,7 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn)
/**
* Reallocate memory if necessary.
* FIXME: Below code is broken, because runlist allocations must be
* a multiply of 4096. The code caused crashes and corruptions.
* a multiple of 4096. The code caused crashes and corruptions.
*/
/*
if (!is_end) {
@@ -4,7 +4,7 @@
* Copyright (c) 2004 Anton Altaparmakov
* Copyright (c) 2005-2006 Szabolcs Szakacsits
* Copyright (c) 2006 Yura Pakhuchiy
* Copyright (c) 2007-2009 Jean-Pierre Andre
* Copyright (c) 2007-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
@@ -409,91 +409,6 @@ le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
return cpu_to_le32(hash);
}
/*
* Internal read
* copied and pasted from ntfs_fuse_read() and made independent
* of fuse context
*/
static int ntfs_local_read(ntfs_inode *ni,
ntfschar *stream_name, int stream_name_len,
char *buf, size_t size, off_t offset)
{
ntfs_attr *na = NULL;
int res, total = 0;
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
if ((size_t)offset < (size_t)na->data_size) {
if (offset + size > (size_t)na->data_size)
size = na->data_size - offset;
while (size) {
res = ntfs_attr_pread(na, offset, size, buf);
if ((off_t)res < (off_t)size)
ntfs_log_perror("ntfs_attr_pread partial read "
"(%lld : %lld <> %d)",
(long long)offset,
(long long)size, res);
if (res <= 0) {
res = -errno;
goto exit;
}
size -= res;
offset += res;
total += res;
}
}
res = total;
exit:
if (na)
ntfs_attr_close(na);
return res;
}
/*
* Internal write
* copied and pasted from ntfs_fuse_write() and made independent
* of fuse context
*/
static int ntfs_local_write(ntfs_inode *ni,
ntfschar *stream_name, int stream_name_len,
char *buf, size_t size, off_t offset)
{
ntfs_attr *na = NULL;
int res, total = 0;
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
while (size) {
res = ntfs_attr_pwrite(na, offset, size, buf);
if (res < (s64)size)
ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: "
"%lld <> %d)", (long long)offset,
(long long)size, res);
if (res <= 0) {
res = -errno;
goto exit;
}
size -= res;
offset += res;
total += res;
}
res = total;
exit:
if (na)
ntfs_attr_close(na);
return res;
}
/*
* Get the first entry of current index block
* cut and pasted form ntfs_ie_get_first() in index.c
@@ -536,7 +451,7 @@ static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
if (stuff) {
memset(stuff, 0, STUFFSZ);
do {
written = ntfs_local_write(vol->secure_ni,
written = ntfs_attr_data_write(vol->secure_ni,
STREAM_SDS, 4, stuff, STUFFSZ, offs);
if (written == STUFFSZ) {
total += STUFFSZ;
@@ -594,10 +509,10 @@ static int entersecurity_data(ntfs_volume *vol,
phsds->security_id = keyid;
phsds->offset = cpu_to_le64(offs);
phsds->length = cpu_to_le32(fullsz - gap);
written1 = ntfs_local_write(vol->secure_ni,
written1 = ntfs_attr_data_write(vol->secure_ni,
STREAM_SDS, 4, fullattr, fullsz,
offs - gap);
written2 = ntfs_local_write(vol->secure_ni,
written2 = ntfs_attr_data_write(vol->secure_ni,
STREAM_SDS, 4, fullattr, fullsz,
offs - gap + ALIGN_SDS_BLOCK);
if ((written1 == fullsz)
@@ -955,7 +870,7 @@ static le32 setsecurityattr(ntfs_volume *vol,
+ sizeof(SECURITY_DESCRIPTOR_HEADER);
oldattr = (char*)ntfs_malloc(size);
if (oldattr) {
rdsize = ntfs_local_read(
rdsize = ntfs_attr_data_read(
vol->secure_ni,
STREAM_SDS, 4,
oldattr, size, offs);
@@ -1154,7 +1069,6 @@ static int upgrade_secur_desc(ntfs_volume *vol,
na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
AT_UNNAMED, 0);
if (na) {
res = 0;
/* expand standard information attribute to v3.x */
res = ntfs_attr_truncate(na,
(s64)sizeof(STANDARD_INFORMATION));
@@ -1240,6 +1154,7 @@ static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid
* /proc/$TID/task/$TID/status which apparently exists and
* contains the same data.
*/
#ifdef __HAIKU__
static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
{
@@ -1783,7 +1698,7 @@ static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
securattr = (char*)ntfs_malloc(size);
if (securattr) {
rdsize = ntfs_local_read(
rdsize = ntfs_attr_data_read(
ni, STREAM_SDS, 4,
securattr, size, offs);
if ((rdsize != size)
@@ -2109,7 +2024,6 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
const SID *gsid; /* group of file/directory */
uid_t uid;
gid_t gid;
int perm;
BOOL isdir;
size_t outsize;
@@ -2144,7 +2058,6 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
* fetch owner and group for cacheing
*/
if (pxdesc) {
perm = pxdesc->mode & 07777;
/*
* Create a security id if there were none
* and upgrade option is selected
@@ -2158,11 +2071,10 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
#if OWNERFROMACL
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#else
if (!perm && ntfs_same_sid(usid, adminsid)) {
if (!(pxdesc->mode & 07777)
&& ntfs_same_sid(usid, adminsid)) {
uid = find_tenant(scx,
securattr);
if (uid)
perm = 0700;
} else
uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
#endif
@@ -2989,7 +2901,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
uid_t uid;
uid_t gid;
int res;
mode_t mode;
BOOL isdir;
BOOL deflt;
BOOL exist;
@@ -3015,7 +2926,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
gid = cached->gid;
oldpxdesc = cached->pxdesc;
if (oldpxdesc) {
mode = oldpxdesc->mode;
newpxdesc = ntfs_replace_acl(oldpxdesc,
(const struct POSIX_ACL*)value,count,deflt);
}
@@ -3042,7 +2952,6 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
|| (!exist && (flags & XATTR_REPLACE))) {
errno = (exist ? EEXIST : ENODATA);
} else {
mode = oldpxdesc->mode;
newpxdesc = ntfs_replace_acl(oldpxdesc,
(const struct POSIX_ACL*)value,count,deflt);
}
@@ -3609,16 +3518,16 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
uid_t uid, gid_t gid, const mode_t mode)
{
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const struct CACHED_PERMISSIONS *cached;
char *oldattr;
const SID *usid;
const SID *gsid;
uid_t fileuid;
uid_t filegid;
BOOL isdir;
int res;
#if POSIXACLS
const SECURITY_DESCRIPTOR_RELATIVE *phead;
const SID *usid;
const SID *gsid;
BOOL isdir;
const struct POSIX_SECURITY *oldpxdesc;
struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
int pxsize;
@@ -3651,6 +3560,7 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
filegid = 0;
oldattr = getsecurityattr(scx->vol, ni);
if (oldattr) {
#if POSIXACLS
isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
!= const_cpu_to_le16(0);
phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
@@ -3663,7 +3573,6 @@ int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
usid = (const SID*)
&oldattr[le32_to_cpu(phead->owner)];
#endif
#if POSIXACLS
newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
usid, gsid, isdir);
if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
@@ -4108,7 +4017,7 @@ static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribut
static int localread(void *fileid, char *buf, size_t size, off_t offs)
{
return (ntfs_local_read((ntfs_inode*)fileid,
return (ntfs_attr_data_read((ntfs_inode*)fileid,
AT_UNNAMED, 0, buf, size, offs));
}
@@ -4880,6 +4789,7 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoSetDirty(ni);
NInoFileNameSetDirty(ni);
}
if (!ntfs_inode_close(ni))
res = -1;
@@ -4930,7 +4840,7 @@ int ntfs_read_sds(struct SECURITY_API *scapi,
got = -1; /* default return */
if (scapi && (scapi->magic == MAGIC_API)) {
if (scapi->security.vol->secure_ni)
got = ntfs_local_read(scapi->security.vol->secure_ni,
got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
STREAM_SDS, 4, buf, size, offset);
else
errno = EOPNOTSUPP;
@@ -5126,7 +5036,7 @@ int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
*/
struct SECURITY_API *ntfs_initialize_file_security(const char *device,
int flags)
unsigned long flags)
{
ntfs_volume *vol;
unsigned long mntflag;
@@ -4,7 +4,7 @@
*
* Copyright (c) 2004 Anton Altaparmakov
* Copyright (c) 2005-2006 Szabolcs Szakacsits
* Copyright (c) 2007-2008 Jean-Pierre Andre
* Copyright (c) 2007-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
@@ -85,6 +85,7 @@ struct CACHED_PERMISSIONS_LEGACY {
struct CACHED_PERMISSIONS_LEGACY *previous;
void *variable;
size_t varsize;
union ALIGNMENT payload[0];
/* above fields must match "struct CACHED_GENERIC" */
u64 mft_no;
struct CACHED_PERMISSIONS perm;
@@ -99,6 +100,7 @@ struct CACHED_SECURID {
struct CACHED_SECURID *previous;
void *variable;
size_t varsize;
union ALIGNMENT payload[0];
/* above fields must match "struct CACHED_GENERIC" */
uid_t uid;
gid_t gid;
@@ -135,6 +137,7 @@ struct PERMISSIONS_CACHE {
enum {
SECURITY_DEFAULT, /* rely on fuse for permissions checking */
SECURITY_RAW, /* force same ownership/permissions on files */
SECURITY_ACL, /* enable Posix ACLs (when compiled in) */
SECURITY_ADDSECURIDS, /* upgrade old security descriptors */
SECURITY_STATICGRPS, /* use static groups for access control */
SECURITY_WANTED /* a security related option was present */
@@ -181,6 +184,7 @@ struct POSIX_SECURITY {
int defcnt;
int firstdef;
u16 tagsset;
s32 alignment[0];
struct POSIX_ACL acl;
} ;
@@ -346,7 +350,7 @@ INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
INDEX_ENTRY *entry);
struct SECURITY_API *ntfs_initialize_file_security(const char *device,
int flags);
unsigned long flags);
BOOL ntfs_leave_file_security(struct SECURITY_API *scx);
int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf);
@@ -120,5 +120,13 @@ typedef enum {
#define STATUS_KEEP_SEARCHING (-3)
#define STATUS_NOT_FOUND (-4)
/*
* Force alignment in a struct if required by processor
*/
union ALIGNMENT {
u64 u64align;
void *ptralign;
} ;
#endif /* defined _NTFS_TYPES_H */
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2004 Anton Altaparmakov
* Copyright (c) 2002-2009 Szabolcs Szakacsits
* Copyright (c) 2008-2009 Jean-Pierre Andre
* Copyright (c) 2008-2011 Jean-Pierre Andre
* Copyright (c) 2008 Bernhard Kaindl
*
* This program/include file is free software; you can redistribute it and/or
@@ -175,24 +175,22 @@ int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len,
cnt = min(name1_len, name2_len);
if (cnt > 0) {
if (ic == CASE_SENSITIVE) {
do {
c1 = le16_to_cpu(*name1);
while (--cnt && (*name1 == *name2)) {
name1++;
c2 = le16_to_cpu(*name2);
name2++;
} while (--cnt && (c1 == c2));
u1 = c1;
u2 = c2;
}
u1 = c1 = le16_to_cpu(*name1);
u2 = c2 = le16_to_cpu(*name2);
if (u1 < upcase_len)
u1 = le16_to_cpu(upcase[u1]);
if (u2 < upcase_len)
u2 = le16_to_cpu(upcase[u2]);
if ((u1 == u2) && cnt)
do {
u1 = le16_to_cpu(*name1);
name1++;
u2 = le16_to_cpu(*name2);
u1 = le16_to_cpu(*name1);
name2++;
u2 = le16_to_cpu(*name2);
if (u1 < upcase_len)
u1 = le16_to_cpu(upcase[u1]);
if (u2 < upcase_len)
@@ -1139,6 +1137,69 @@ char *ntfs_uppercase_mbs(const char *low,
*/
void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
{
#if 1 /* Vista */
/*
* This is the table as defined by Vista
*/
/*
* "Start" is inclusive and "End" is exclusive, every value has the
* value of "Add" added to it.
*/
static int uc_run_table[][3] = { /* Start, End, Add */
{0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32},
{0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130},
{0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32},
{0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64},
{0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80},
{0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8},
{0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8},
{0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8},
{0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74},
{0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128},
{0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8},
{0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8},
{0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8},
{0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9},
{0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48},
{0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0}
};
/*
* "Start" is exclusive and "End" is inclusive, every second value is
* decremented by one.
*/
static int uc_dup_table[][2] = { /* Start, End */
{0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178},
{0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd},
{0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220},
{0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f},
{0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481},
{0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce},
{0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513},
{0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61},
{0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0}
};
/*
* Set the Unicode character at offset "Offset" to "Value". Note,
* "Value" is host endian.
*/
static int uc_byte_table[][2] = { /* Offset, Value */
{0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184},
{0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6},
{0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7},
{0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc},
{0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca},
{0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66},
{0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190},
{0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196},
{0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f},
{0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae},
{0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9},
{0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0}
};
#else /* Vista */
/*
* This is the table as defined by Windows XP
*/
static int uc_run_table[][3] = { /* Start, End, Add */
{0x0061, 0x007B, -32}, {0x0451, 0x045D, -80}, {0x1F70, 0x1F72, 74},
{0x00E0, 0x00F7, -32}, {0x045E, 0x0460, -80}, {0x1F72, 0x1F76, 86},
@@ -1175,6 +1236,7 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
{0x01A8, 0x01A7}, {0x01DD, 0x018E}, {0x0268, 0x0197},
{0}
};
#endif /* Vista */
int i, r;
int k, off;
@@ -1198,6 +1260,27 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
}
}
/*
* Allocate and build the default upcase table
*
* Returns the number of entries
* 0 if failed
*/
#define UPCASE_LEN 65536 /* default number of entries in upcase */
u32 ntfs_upcase_build_default(ntfschar **upcase)
{
u32 upcase_len = 0;
*upcase = (ntfschar*)ntfs_malloc(UPCASE_LEN*2);
if (*upcase) {
ntfs_upcase_table_build(*upcase, UPCASE_LEN*2);
upcase_len = UPCASE_LEN;
}
return (upcase_len);
}
/*
* Build a table for converting to lower case
*
@@ -1332,16 +1415,18 @@ BOOL ntfs_collapsible_chars(ntfs_volume *vol,
{
BOOL collapsible;
unsigned int ch;
unsigned int cs;
int i;
collapsible = shortlen == longlen;
if (collapsible)
for (i=0; i<shortlen; i++) {
ch = le16_to_cpu(longname[i]);
if ((ch >= vol->upcase_len)
|| ((shortname[i] != longname[i])
&& (shortname[i] != vol->upcase[ch])))
collapsible = FALSE;
for (i=0; collapsible && (i<shortlen); i++) {
ch = le16_to_cpu(longname[i]);
cs = le16_to_cpu(shortname[i]);
if ((cs != ch)
&& ((ch >= vol->upcase_len)
|| (cs >= vol->upcase_len)
|| (vol->upcase[cs] != vol->upcase[ch])))
collapsible = FALSE;
}
return (collapsible);
}
@@ -61,6 +61,7 @@ extern char *ntfs_uppercase_mbs(const char *low,
const ntfschar *upcase, u32 upcase_len);
extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
extern u32 ntfs_upcase_build_default(ntfschar **upcase);
extern ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt);
extern ntfschar *ntfs_str2ucs(const char *s, int *len);
@@ -54,6 +54,7 @@
#include <locale.h>
#endif
#include "param.h"
#include "compat.h"
#include "volume.h"
#include "attrib.h"
@@ -67,10 +68,11 @@
#include "dir.h"
#include "logging.h"
#include "cache.h"
#include "realpath.h"
#include "misc.h"
const char *ntfs_home =
"Ntfs-3g news, support and information: http://ntfs-3g.org\n";
"News, support and information: http://tuxera.com\n";
static const char *invalid_ntfs_msg =
"The device '%s' doesn't seem to have a valid NTFS.\n"
@@ -113,7 +115,7 @@ static const char *fakeraid_msg =
static const char *access_denied_msg =
"Please check '%s' and the ntfs-3g binary permissions,\n"
"and the mounting user ID. More explanation is provided at\n"
"http://ntfs-3g.org/support.html#unprivileged\n";
"http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n";
/**
* ntfs_volume_alloc - Create an NTFS volume object and initialise it
@@ -373,6 +375,12 @@ mft_has_no_attr_list:
/* Done with the $Mft mft record. */
ntfs_attr_put_search_ctx(ctx);
ctx = NULL;
/* Update the size fields in the inode. */
vol->mft_ni->data_size = vol->mft_na->data_size;
vol->mft_ni->allocated_size = vol->mft_na->allocated_size;
set_nino_flag(vol->mft_ni, KnownSize);
/*
* The volume is now setup so we can use all read access functions.
*/
@@ -482,13 +490,10 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags)
goto error_exit;
/* Create the default upcase table. */
vol->upcase_len = 65536;
vol->upcase = ntfs_malloc(vol->upcase_len * sizeof(ntfschar));
if (!vol->upcase)
vol->upcase_len = ntfs_upcase_build_default(&vol->upcase);
if (!vol->upcase_len || !vol->upcase)
goto error_exit;
ntfs_upcase_table_build(vol->upcase,
vol->upcase_len * sizeof(ntfschar));
/* Default with no locase table and case sensitive file names */
vol->locase = (ntfschar*)NULL;
NVolSetCaseSensitive(vol);
@@ -497,6 +502,12 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags)
NVolSetShowSysFiles(vol);
NVolSetShowHidFiles(vol);
NVolClearHideDotFiles(vol);
/* set default compression */
#if DEFAULT_COMPRESSION
NVolSetCompression(vol);
#else
NVolClearCompression(vol);
#endif
if (flags & MS_RDONLY)
NVolSetReadOnly(vol);
@@ -868,6 +879,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags)
VOLUME_INFORMATION *vinf;
ntfschar *vname;
int i, j, eo;
unsigned int k;
u32 u;
vol = ntfs_volume_startup(dev, flags);
@@ -1025,6 +1037,17 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags)
ntfs_log_perror("Failed to close $UpCase");
goto error_exit;
}
/* Consistency check of $UpCase, restricted to plain ASCII chars */
k = 0x20;
while ((k < vol->upcase_len)
&& (k < 0x7f)
&& (le16_to_cpu(vol->upcase[k])
== ((k < 'a') || (k > 'z') ? k : k + 'A' - 'a')))
k++;
if (k < 0x7f) {
ntfs_log_error("Corrupted file $UpCase\n");
goto io_error_exit;
}
/*
* Now load $Volume and set the version information and flags in the
@@ -1172,7 +1195,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags)
* Check for dirty logfile and hibernated Windows.
* We care only about read-write mounts.
*/
if (!(flags & MS_RDONLY)) {
if (!(flags & (MS_RDONLY | MS_FORENSIC))) {
if (!(flags & MS_IGNORE_HIBERFILE) &&
ntfs_volume_check_hiberfile(vol, 1) < 0)
goto error_exit;
@@ -1184,10 +1207,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags)
if (ntfs_logfile_reset(vol))
goto error_exit;
}
}
/* make $TXF_DATA resident if present on the root directory */
if (!NVolReadOnly(vol) && fix_txf_data(vol))
goto error_exit;
if (fix_txf_data(vol))
goto error_exit;
}
return vol;
io_error_exit:
@@ -1350,18 +1373,6 @@ int ntfs_umount(ntfs_volume *vol, const BOOL force __attribute__((unused)))
#ifdef HAVE_MNTENT_H
#ifndef HAVE_REALPATH
/**
* realpath - If there is no realpath on the system
*/
static char *realpath(const char *path, char *resolved_path)
{
strncpy(resolved_path, path, PATH_MAX);
resolved_path[PATH_MAX] = '\0';
return resolved_path;
}
#endif
/**
* ntfs_mntent_check - desc
*
@@ -1385,7 +1396,7 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags)
err = errno;
goto exit;
}
if (!realpath(file, real_file)) {
if (!ntfs_realpath_canonicalize(file, real_file)) {
err = errno;
goto exit;
}
@@ -1394,7 +1405,7 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags)
goto exit;
}
while ((mnt = getmntent(f))) {
if (!realpath(mnt->mnt_fsname, real_fsname))
if (!ntfs_realpath_canonicalize(mnt->mnt_fsname, real_fsname))
continue;
if (!strcmp(real_file, real_fsname))
break;
@@ -1682,7 +1693,7 @@ void ntfs_mount_error(const char *volume, const char *mntpoint, int err)
int ntfs_set_locale(void)
{
#ifndef __HAIKU__
#ifndef __HAIKU__
const char *locale;
locale = setlocale(LC_ALL, "");
@@ -1692,7 +1703,7 @@ int ntfs_set_locale(void)
"'%s'.\n", locale);
return 1;
}
#endif
#endif
return 0;
}
@@ -1723,3 +1734,113 @@ int ntfs_volume_get_free_space(ntfs_volume *vol)
}
return (ret);
}
/**
* ntfs_volume_rename - change the current label on a volume
* @vol: volume to change the label on
* @label: the new label
* @label_len: the length of @label in ntfschars including the terminating NULL
* character, which is mandatory (the value can not exceed 128)
*
* Change the label on the volume @vol to @label.
*/
int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len)
{
ntfs_attr *na;
char *old_vol_name;
char *new_vol_name = NULL;
int new_vol_name_len;
int err;
if (NVolReadOnly(vol)) {
ntfs_log_error("Refusing to change label on read-only mounted "
"volume.\n");
errno = EROFS;
return -1;
}
label_len *= sizeof(ntfschar);
if (label_len > 0x100) {
ntfs_log_error("New label is too long. Maximum %u characters "
"allowed.\n",
(unsigned)(0x100 / sizeof(ntfschar)));
errno = ERANGE;
return -1;
}
na = ntfs_attr_open(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0);
if (!na) {
if (errno != ENOENT) {
err = errno;
ntfs_log_perror("Lookup of $VOLUME_NAME attribute "
"failed");
goto err_out;
}
/* The volume name attribute does not exist. Need to add it. */
if (ntfs_attr_add(vol->vol_ni, AT_VOLUME_NAME, AT_UNNAMED, 0,
(u8*) label, label_len))
{
err = errno;
ntfs_log_perror("Encountered error while adding "
"$VOLUME_NAME attribute");
goto err_out;
}
}
else {
s64 written;
if (NAttrNonResident(na)) {
err = errno;
ntfs_log_error("Error: Attribute $VOLUME_NAME must be "
"resident.\n");
goto err_out;
}
if (na->data_size != label_len) {
if (ntfs_attr_truncate(na, label_len)) {
err = errno;
ntfs_log_perror("Error resizing resident "
"attribute");
goto err_out;
}
}
if (label_len) {
written = ntfs_attr_pwrite(na, 0, label_len, label);
if (written == -1) {
err = errno;
ntfs_log_perror("Error when writing "
"$VOLUME_NAME data");
goto err_out;
}
else if (written != label_len) {
err = EIO;
ntfs_log_error("Partial write when writing "
"$VOLUME_NAME data.");
goto err_out;
}
}
}
new_vol_name_len =
ntfs_ucstombs(label, label_len, &new_vol_name, 0);
if (new_vol_name_len == -1) {
err = errno;
ntfs_log_perror("Error while decoding new volume name");
goto err_out;
}
old_vol_name = vol->vol_name;
vol->vol_name = new_vol_name;
free(old_vol_name);
err = 0;
err_out:
if (na)
ntfs_attr_close(na);
if (err)
errno = err;
return err ? -1 : 0;
}
@@ -5,6 +5,7 @@
* Copyright (c) 2004-2005 Richard Russon
* Copyright (c) 2005-2006 Yura Pakhuchiy
* Copyright (c) 2005-2009 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
@@ -57,6 +58,7 @@
#endif
#define MS_IGNORE_HIBERFILE 0x20000000
#define MS_FORENSIC 0x04000000 /* No modification during mount */
/* Forward declaration */
typedef struct _ntfs_volume ntfs_volume;
@@ -111,6 +113,7 @@ typedef enum {
NV_ShowHidFiles, /* 1: Show files marked hidden. */
NV_HideDotFiles, /* 1: Set hidden flag on dot files */
NV_Compression, /* 1: allow compression */
NV_NoFixupWarn, /* 1: Do not log fixup errors */
} ntfs_volume_state_bits;
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
@@ -145,6 +148,10 @@ typedef enum {
#define NVolSetCompression(nv) set_nvol_flag(nv, Compression)
#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression)
#define NVolNoFixupWarn(nv) test_nvol_flag(nv, NoFixupWarn)
#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn)
#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn)
/*
* NTFS version 1.1 and 1.2 are used by Windows NT4.
* NTFS version 2.x is used by Windows 2000 Beta
@@ -251,7 +258,9 @@ struct _ntfs_volume {
s64 free_mft_records; /* Same for free mft records (see above) */
BOOL efs_raw; /* volume is mounted for raw access to
efs-encrypted files */
#ifdef XATTR_MAPPINGS
struct XATTRMAPPING *xattr_mapping;
#endif /* XATTR_MAPPINGS */
#if CACHE_INODE_SIZE
struct CACHE_HEADER *xinode_cache;
#endif
@@ -293,6 +302,7 @@ extern int ntfs_volume_error(int err);
extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err);
extern int ntfs_volume_get_free_space(ntfs_volume *vol);
extern int ntfs_volume_rename(ntfs_volume *vol, ntfschar *label, int label_len);
extern int ntfs_set_shown_files(ntfs_volume *vol,
BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files);
@@ -0,0 +1,791 @@
/**
* xattrs.c : common functions to deal with system extended attributes
*
* 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_SETXATTR /* extended attributes support required */
#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 "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[0];
} __attribute__((__packed__));
#endif
#endif
static const char xattr_ntfs_3g[] = "ntfs-3g.";
static const char nf_ns_user_prefix[] = "user.";
static const int nf_ns_user_prefix_len = sizeof(nf_ns_user_prefix) - 1;
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_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_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 */
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
/*
* the returned value is the needed
* size. If it is too small, no copy
* is done, and the caller has to
* issue a new call with correct size.
*/
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;
default :
errno = EOPNOTSUPP;
res = -errno;
break;
}
return (res);
}
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
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
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;
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;
default :
errno = EOPNOTSUPP;
res = -errno;
break;
}
return (res);
}
#endif /* HAVE_SETXATTR */
@@ -0,0 +1,75 @@
/*
* xattrs.h : definitions related to system extended attributes
*
* 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
*/
#ifndef _NTFS_XATTR_H_
#define _NTFS_XATTR_H_
/*
* Identification of data mapped to the system name space
*/
enum SYSTEMXATTRS {
XATTR_UNMAPPED,
XATTR_NTFS_ACL,
XATTR_NTFS_ATTRIB,
XATTR_NTFS_ATTRIB_BE,
XATTR_NTFS_EFSINFO,
XATTR_NTFS_REPARSE_DATA,
XATTR_NTFS_OBJECT_ID,
XATTR_NTFS_DOS_NAME,
XATTR_NTFS_TIMES,
XATTR_NTFS_TIMES_BE,
XATTR_NTFS_CRTIME,
XATTR_NTFS_CRTIME_BE,
XATTR_POSIX_ACC,
XATTR_POSIX_DEF
} ;
struct XATTRMAPPING {
struct XATTRMAPPING *next;
enum SYSTEMXATTRS xattr;
char name[1]; /* variable length */
} ;
#ifdef XATTR_MAPPINGS
struct XATTRMAPPING *ntfs_xattr_build_mapping(ntfs_volume *vol,
const char *path);
void ntfs_xattr_free_mapping(struct XATTRMAPPING*);
#endif /* XATTR_MAPPINGS */
enum SYSTEMXATTRS ntfs_xattr_system_type(const char *name,
ntfs_volume *vol);
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 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 ntfs_xattr_system_removexattr(struct SECURITY_CONTEXT *scx,
enum SYSTEMXATTRS attr,
ntfs_inode *ni, ntfs_inode *dir_ni);
#endif /* _NTFS_XATTR_H_ */
+217 -139
View File
@@ -4,6 +4,7 @@
* Copyright (c) 2002-2005 Richard Russon
* Copyright (c) 2003-2006 Anton Altaparmakov
* Copyright (c) 2003 Lode Leroy
* Copyright (c) 2005-2007 Yura Pakhuchiy
*
* A set of shared functions for ntfs utilities
*
@@ -69,6 +70,7 @@
#include "volume.h"
#include "debug.h"
#include "dir.h"
/* #include "version.h" */
#include "logging.h"
#include "misc.h"
@@ -80,9 +82,120 @@ const char *ntfs_gpl = "This program is free software, released under the GNU "
"\"COPYING\" distributed with this program, or online at:\n"
"http://www.gnu.org/copyleft/gpl.html\n";
static const char *invalid_ntfs_msg =
"The device '%s' doesn't have a valid NTFS.\n"
"Maybe you selected the wrong device? Or the whole disk instead of a\n"
"partition (e.g. /dev/hda, not /dev/hda1)? Or the other way around?\n";
static const char *corrupt_volume_msg =
"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n"
"The usage of the /f parameter is very IMPORTANT! No modification was\n"
"made to NTFS by this software.\n";
static const char *hibernated_volume_msg =
"The NTFS partition is hibernated. Please resume Windows and turned it \n"
"off properly, so mounting could be done safely.\n";
static const char *unclean_journal_msg =
"Access is denied because the NTFS journal file is unclean. Choices are:\n"
" A) Shutdown Windows properly.\n"
" B) Click the 'Safely Remove Hardware' icon in the Windows taskbar\n"
" notification area before disconnecting the device.\n"
" C) Use 'Eject' from Windows Explorer to safely remove the device.\n"
" D) If you ran chkdsk previously then boot Windows again which will\n"
" automatically initialize the journal.\n"
" E) Submit 'force' option (WARNING: This solution it not recommended).\n"
" F) ntfsmount: Mount the volume read-only by using the 'ro' mount option.\n";
static const char *opened_volume_msg =
"Access is denied because the NTFS volume is already exclusively opened.\n"
"The volume may be already mounted, or another software may use it which\n"
"could be identified for example by the help of the 'fuser' command.\n";
static const char *dirty_volume_msg =
"Volume is scheduled for check.\n"
"Please boot into Windows TWICE, or use the 'force' option.\n"
"NOTE: If you had not scheduled check and last time accessed this volume\n"
"using ntfsmount and shutdown system properly, then init scripts in your\n"
"distribution are broken. Please report to your distribution developers\n"
"(NOT to us!) that init scripts kill ntfsmount or mount.ntfs-fuse during\n"
"shutdown instead of proper umount.\n";
static const char *fakeraid_msg =
"You seem to have a SoftRAID/FakeRAID hardware and must use an activated,\n"
"different device under /dev/mapper, (e.g. /dev/mapper/nvidia_eahaabcc1)\n"
"to mount NTFS. Please see the 'dmraid' documentation for help.\n";
/**
* utils_valid_device - Perform some safety checks on the device, before we start
* utils_set_locale
*/
#ifndef __HAIKU__
int utils_set_locale(void)
{
const char *locale;
locale = setlocale(LC_ALL, "");
if (!locale) {
locale = setlocale(LC_ALL, NULL);
ntfs_log_error("Failed to set locale, using default '%s'.\n",
locale);
return 1;
} else {
return 0;
}
}
#endif
/**
* linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with
* ntfs-3g's.
*/
int ntfs_mbstoucs_libntfscompat(const char *ins,
ntfschar **outs, int outs_len)
{
if(!outs) {
errno = EINVAL;
return -1;
}
else if(*outs != NULL) {
/* Note: libntfs's mbstoucs implementation allows the caller to
* specify a preallocated buffer while libntfs-3g's always
* allocates the output buffer.
*/
ntfschar *tmpstr = NULL;
int tmpstr_len;
tmpstr_len = ntfs_mbstoucs(ins, &tmpstr);
if(tmpstr_len >= 0) {
if((tmpstr_len + 1) > outs_len) {
/* Doing a realloc instead of reusing tmpstr
* because it emulates libntfs's mbstoucs more
* closely. */
ntfschar *re_outs = realloc(*outs,
sizeof(ntfschar)*(tmpstr_len + 1));
if(!re_outs)
tmpstr_len = -1;
else
*outs = re_outs;
}
if(tmpstr_len >= 0) {
/* The extra character is the \0 terminator. */
memcpy(*outs, tmpstr,
sizeof(ntfschar)*(tmpstr_len + 1));
}
free(tmpstr);
}
return tmpstr_len;
}
else
return ntfs_mbstoucs(ins, outs);
}
/**
* utils_valid_device - Perform some safety checks on the device, before start
* @name: Full pathname of the device/file to work with
* @force: Continue regardless of problems
*
@@ -98,7 +211,7 @@ int utils_valid_device(const char *name, int force)
struct stat st;
#ifdef __CYGWIN32__
/* FIXME: This doesn't work for Cygwin, so just return success for now... */
/* FIXME: This doesn't work for Cygwin, so just return success. */
return 1;
#endif
if (!name) {
@@ -107,37 +220,30 @@ int utils_valid_device(const char *name, int force)
}
if (stat(name, &st) == -1) {
if (errno == ENOENT) {
if (errno == ENOENT)
ntfs_log_error("The device %s doesn't exist\n", name);
} else {
ntfs_log_perror("Error getting information about %s", name);
}
else
ntfs_log_perror("Error getting information about %s",
name);
return 0;
}
if (!S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode)) {
ntfs_log_warning("%s is not a block device, "
"nor regular file.\n", name);
if (!force) {
ntfs_log_error("Use the force option to work with other"
" file types, for your own risk!\n");
return 0;
}
ntfs_log_warning("Forced to continue.\n");
}
/* Make sure the file system is not mounted. */
if (ntfs_check_if_mounted(name, &mnt_flags)) {
ntfs_log_perror("Failed to determine whether %s is mounted", name);
ntfs_log_perror("Failed to determine whether %s is mounted",
name);
if (!force) {
ntfs_log_error("Use the force option to ignore this error.\n");
ntfs_log_error("Use the force option to ignore this "
"error.\n");
return 0;
}
ntfs_log_warning("Forced to continue.\n");
} else if (mnt_flags & NTFS_MF_MOUNTED) {
ntfs_log_warning("The device %s, is mounted.\n", name);
if (!force) {
ntfs_log_error("Use the force option to work a mounted filesystem.\n");
ntfs_log_error("%s", opened_volume_msg);
ntfs_log_error("You can use force option to avoid this "
"check, but this is not recommended\n"
"and may lead to data corruption.\n");
return 0;
}
ntfs_log_warning("Forced to continue.\n");
@@ -149,7 +255,7 @@ int utils_valid_device(const char *name, int force)
/**
* utils_mount_volume - Mount an NTFS volume
*/
ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force)
ntfs_volume * utils_mount_volume(const char *device, unsigned long flags)
{
ntfs_volume *vol;
@@ -158,34 +264,55 @@ ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL f
return NULL;
}
if (!utils_valid_device(device, force))
/* Porting notes:
*
* libntfs-3g does not have the 'force' flag in ntfs_mount_flags.
* The 'force' flag in libntfs bypasses two safety checks when mounting
* read/write:
* 1. Do not mount when the VOLUME_IS_DIRTY flag in
* VOLUME_INFORMATION is set.
* 2. Do not mount when the logfile is unclean.
*
* libntfs-3g only has safety check number 2. The dirty flag is simply
* ignored because we are confident that we can handle a dirty volume.
* So we treat MS_RECOVER like NTFS_MNT_FORCE, knowing that the first
* check is always bypassed.
*/
if (!utils_valid_device(device, flags & MS_RECOVER))
return NULL;
vol = ntfs_mount(device, flags);
if (!vol) {
ntfs_log_perror("Couldn't mount device '%s'", device);
if (errno == EPERM)
ntfs_log_error("Windows was hibernated. Try to mount "
"volume in windows, shut down and try "
"again.\n");
if (errno == EOPNOTSUPP)
ntfs_log_error("Windows did not shut down properly. "
"Try to mount volume in windows, "
"shut down and try again.\n");
ntfs_log_perror("Failed to mount '%s'", device);
if (errno == EINVAL)
ntfs_log_error(invalid_ntfs_msg, device);
else if (errno == EIO)
ntfs_log_error("%s", corrupt_volume_msg);
else if (errno == EPERM)
ntfs_log_error("%s", hibernated_volume_msg);
else if (errno == EOPNOTSUPP)
ntfs_log_error("%s", unclean_journal_msg);
else if (errno == EBUSY)
ntfs_log_error("%s", opened_volume_msg);
else if (errno == ENXIO)
ntfs_log_error("%s", fakeraid_msg);
return NULL;
}
/* Porting notes:
* libntfs-3g does not record whether the volume log file was dirty
* before mount, so we can only warn if the VOLUME_IS_DIRTY flag is set
* in VOLUME_INFORMATION. */
if (vol->flags & VOLUME_IS_DIRTY) {
ntfs_log_warning("Volume is dirty.\n");
if (!force) {
ntfs_log_error("Run chkdsk and try again, or use the "
"force option.\n");
if (!(flags & MS_RECOVER)) {
ntfs_log_error("%s", dirty_volume_msg);
ntfs_umount(vol, FALSE);
return NULL;
}
ntfs_log_warning("Forced to continue.\n");
ntfs_log_error("WARNING: Dirty volume mount was forced by the "
"'force' mount option.\n");
}
return vol;
}
@@ -222,6 +349,8 @@ int utils_parse_size(const char *value, s64 *size, BOOL scale)
return 0;
}
ntfs_log_debug("Parsing size '%s'.\n", value);
result = strtoll(value, &suffix, 0);
if (result < 0 || errno == ERANGE) {
ntfs_log_error("Invalid size '%s'.\n", value);
@@ -252,6 +381,7 @@ int utils_parse_size(const char *value, s64 *size, BOOL scale)
}
}
ntfs_log_debug("Parsed size = %lld.\n", result);
*size = result;
return 1;
}
@@ -386,11 +516,12 @@ ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft)
* if parent is 5 (/) stop
* get inode of parent
*/
#define max_path 20
int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
{
// XXX option: names = posix/win32 or dos
// flags: path, filename, or both
const int max_path = 20;
ntfs_volume *vol;
ntfs_attr_search_ctx *ctx;
@@ -442,10 +573,10 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
&names[i], 0) < 0) {
char *temp;
ntfs_log_error("Couldn't translate filename to current locale.\n");
temp = ntfs_malloc(64);
temp = ntfs_malloc(30);
if (!temp)
return 0;
sprintf(temp, "<MFT%llu>", (unsigned
snprintf(temp, 30, "<MFT%llu>", (unsigned
long long)inode->mft_no);
names[i] = temp;
}
@@ -483,7 +614,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
if (!names[i])
continue;
len = sprintf(buffer + offset, "%c%s", PATH_SEP, names[i]); //bufsize - offset,
len = snprintf(buffer + offset, bufsize - offset, "%c%s", PATH_SEP, names[i]);
if (len >= (bufsize - offset)) {
ntfs_log_error("Pathname was truncated.\n");
break;
@@ -500,66 +631,7 @@ int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize)
return 1;
}
int utils_inode_get_ucsfilename(ntfs_inode *inode,ntfschar **uname, int *size)
{
// XXX option: names = posix/win32 or dos
// flags: path, filename, or both
ntfs_volume *vol;
ntfs_attr_search_ctx *ctx;
ATTR_RECORD *rec;
FILE_NAME_ATTR *attr;
int name_space;
int res=B_NO_ERROR;
*uname=NULL;
if (!inode || !uname || !size) {
res = EINVAL;
return 0;
}
vol = inode->vol;
ctx = ntfs_attr_get_search_ctx(inode, NULL);
if (!ctx) {
res = EINVAL;
return res;
}
name_space = 4;
while ((rec = find_attribute(AT_FILE_NAME, ctx)))
{
/* We know this will always be resident. */
attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset));
if (attr->file_name_type > name_space) { //XXX find the ...
continue;
}
name_space = attr->file_name_type;
if(*uname!=NULL)free(*uname);
*uname=(ntfschar*)ntfs_malloc(attr->file_name_length);
if(*uname==NULL)
{
res = EINVAL;
goto exit;
}
memcpy(*uname,attr->file_name, attr->file_name_length);
*size = attr->file_name_length;
}
exit:
ntfs_attr_put_search_ctx(ctx);
return res;
}
#undef max_path
/**
* utils_attr_get_name
@@ -578,45 +650,44 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b
attrdef = ntfs_attr_find_in_attrdef(vol, attr->type);
if (attrdef) {
name = NULL;
name = NULL;
namelen = ntfs_ucsnlen(attrdef->name, sizeof(attrdef->name));
if (ntfs_ucstombs(attrdef->name, namelen, &name, 0) < 0) {
ntfs_log_error("Couldn't translate attribute type to current locale.\n");
ntfs_log_error("Couldn't translate attribute type to "
"current locale.\n");
// <UNKNOWN>?
return 0;
}
len = sprintf(buffer, "%s", name);
len = snprintf(buffer, bufsize, "%s", name);
} else {
ntfs_log_error("Unknown attribute type 0x%02x\n", attr->type);
len = sprintf(buffer, "<UNKNOWN>");
len = snprintf(buffer, bufsize, "<UNKNOWN>");
}
if (len >= bufsize) {
ntfs_log_error("Attribute type was truncated.\n");
free(name);
return 0;
}
if (!attr->name_length) {
free(name);
return 0;
}
buffer += len;
bufsize -= len;
free(name);
name = NULL;
name = NULL;
namelen = attr->name_length;
if (ntfs_ucstombs((ntfschar *)((char *)attr + attr->name_offset),
namelen, &name, 0) < 0) {
ntfs_log_error("Couldn't translate attribute name to current locale.\n");
namelen, &name, 0) < 0) {
ntfs_log_error("Couldn't translate attribute name to current "
"locale.\n");
// <UNKNOWN>?
len = sprintf(buffer, "<UNKNOWN>");
len = snprintf(buffer, bufsize, "<UNKNOWN>");
return 0;
}
len = sprintf(buffer, "(%s)", name);
len = snprintf(buffer, bufsize, "(%s)", name);
free(name);
if (len >= bufsize) {
@@ -638,7 +709,10 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b
*
* This function has a static buffer in which it caches a section of $Bitmap.
* If the lcn, being tested, lies outside the range, the buffer will be
* refreshed.
* refreshed. @bmplcn stores offset to the first bit (in bits) stored in the
* buffer.
*
* NOTE: Be very carefull with shifts by 3 everywhere in this function.
*
* Return: 1 Cluster is in use
* 0 Cluster is free space
@@ -647,8 +721,7 @@ int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int b
int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
{
static unsigned char buffer[512];
static long long bmplcn = -sizeof(buffer) - 1; /* Which bit of $Bitmap is in the buffer */
static long long bmplcn = -(sizeof(buffer) << 3);
int byte, bit;
ntfs_attr *attr;
@@ -658,7 +731,8 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
}
/* Does lcn lie in the section of $Bitmap we already have cached? */
if ((lcn < bmplcn) || (lcn >= (bmplcn + (sizeof(buffer) << 3)))) {
if ((lcn < bmplcn)
|| (lcn >= (long long)(bmplcn + (sizeof(buffer) << 3)))) {
ntfs_log_debug("Bit lies outside cache.\n");
attr = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
if (!attr) {
@@ -670,7 +744,8 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
memset(buffer, 0xFF, sizeof(buffer));
bmplcn = lcn & (~((sizeof(buffer) << 3) - 1));
if (ntfs_attr_pread(attr, (bmplcn>>3), sizeof(buffer), buffer) < 0) {
if (ntfs_attr_pread(attr, (bmplcn >> 3), sizeof(buffer),
buffer) < 0) {
ntfs_log_perror("Couldn't read $Bitmap");
ntfs_attr_close(attr);
return -1;
@@ -682,7 +757,9 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
bit = 1 << (lcn & 7);
byte = (lcn >> 3) & (sizeof(buffer) - 1);
ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] & bit);
ntfs_log_debug("cluster = %lld, bmplcn = %lld, byte = %d, bit = %d, "
"in use %d\n", lcn, bmplcn, byte, bit, buffer[byte] &
bit);
return (buffer[byte] & bit);
}
@@ -707,19 +784,19 @@ int utils_cluster_in_use(ntfs_volume *vol, long long lcn)
int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref)
{
static u8 buffer[512];
static s64 bmpmref = -sizeof(buffer) - 1; /* Which bit of $BITMAP is in the buffer */
static s64 bmpmref = -(sizeof(buffer) << 3) - 1; /* Which bit of $BITMAP is in the buffer */
int byte, bit;
ntfs_log_trace("Entering.\n");
if (!vol) {
errno = EINVAL;
return -1;
}
ntfs_log_trace("entering\n");
/* Does mref lie in the section of $Bitmap we already have cached? */
if (((s64)MREF(mref) < bmpmref) || ((s64)MREF(mref) >= (bmpmref +
(sizeof(buffer) << 3)))) {
if (((s64)MREF(mref) < bmpmref)
|| ((s64)MREF(mref) >= (s64)(bmpmref + (sizeof(buffer) << 3)))) {
ntfs_log_debug("Bit lies outside cache.\n");
/* Mark the buffer as not in use, in case the read is shorter. */
@@ -791,20 +868,19 @@ int utils_is_metadata(ntfs_inode *inode)
file = inode->mrec;
if (file && (file->base_mft_record != 0)) {
num = MREF(file->base_mft_record);
num = MREF_LE(file->base_mft_record);
if (__metadata(vol, num) == 1)
return 1;
}
file = inode->mrec;
rec = find_first_attribute(AT_FILE_NAME, inode->mrec);
if (!rec)
return -1;
/* We know this will always be resident. */
attr = (FILE_NAME_ATTR *) ((char *) rec + le16_to_cpu(rec->value_offset));
attr = (FILE_NAME_ATTR *)((char *)rec + le16_to_cpu(rec->value_offset));
num = MREF(attr->parent_directory);
num = MREF_LE(attr->parent_directory);
if ((num != FILE_root) && (__metadata(vol, num) == 1))
return 1;
@@ -890,7 +966,7 @@ struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol)
return NULL;
}
ctx = calloc(1, sizeof *ctx);
ctx = (struct mft_search_ctx*)calloc(1, sizeof *ctx);
ctx->mft_num = -1;
ctx->vol = vol;
@@ -997,7 +1073,7 @@ int mft_next_record(struct mft_search_ctx *ctx)
ctx->flags_match |= FEMR_NOT_IN_USE;
ctx->inode = calloc(1, sizeof(*ctx->inode));
ctx->inode = (ntfs_inode*)calloc(1, sizeof(*ctx->inode));
if (!ctx->inode) {
ntfs_log_error("Out of memory. Aborting.\n");
return -1;
@@ -1047,7 +1123,7 @@ int mft_next_record(struct mft_search_ctx *ctx)
}
/**
* ntfs_get_parent_ref - find mft reference of parent directory of an inode
* ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode
* @ni: ntfs inode whose parent directory to find
*
* Find the parent directory of the ntfs inode @ni. To do this, find the first
@@ -1064,7 +1140,7 @@ int mft_next_record(struct mft_search_ctx *ctx)
* Return the mft reference of the parent directory on success or -1 on error
* with errno set to the error code.
*/
MFT_REF ntfs_get_parent_ref(ntfs_inode *ni)
MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
{
MFT_REF mref;
ntfs_attr_search_ctx *ctx;
@@ -1082,21 +1158,21 @@ MFT_REF ntfs_get_parent_ref(ntfs_inode *ni)
if (!ctx)
return ERR_MREF(-1);
if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
ntfs_log_debug("No file name found in inode 0x%llx. Corrupt "
"inode.\n", (unsigned long long)ni->mft_no);
ntfs_log_error("No file name found in inode %lld\n",
(unsigned long long)ni->mft_no);
goto err_out;
}
if (ctx->attr->non_resident) {
ntfs_log_debug("File name attribute must be resident. Corrupt inode "
"0x%llx.\n", (unsigned long long)ni->mft_no);
ntfs_log_error("File name attribute must be resident (inode "
"%lld)\n", (unsigned long long)ni->mft_no);
goto io_err_out;
}
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) >
(u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n",
(unsigned long long)ni->mft_no);
ntfs_log_error("Corrupt file name attribute in inode %lld.\n",
(unsigned long long)ni->mft_no);
goto io_err_out;
}
mref = le64_to_cpu(fn->parent_directory);
@@ -1110,3 +1186,5 @@ err_out:
errno = eo;
return ERR_MREF(-1);
}
+21 -5
View File
@@ -39,26 +39,23 @@
#endif
extern const char *ntfs_bugs;
extern const char *ntfs_home;
extern const char *ntfs_gpl;
//int utils_set_locale(void);
int utils_set_locale(void);
int utils_parse_size(const char *value, s64 *size, BOOL scale);
int utils_parse_range(const char *string, s64 *start, s64 *finish, BOOL scale);
int utils_inode_get_name(ntfs_inode *inode, char *buffer, int bufsize);
int utils_inode_get_ucsfilename(ntfs_inode *inode,ntfschar **, int *size);
int utils_attr_get_name(ntfs_volume *vol, ATTR_RECORD *attr, char *buffer, int bufsize);
int utils_cluster_in_use(ntfs_volume *vol, long long lcn);
int utils_mftrec_in_use(ntfs_volume *vol, MFT_REF mref);
int utils_is_metadata(ntfs_inode *inode);
void utils_dump_mem(void *buf, int start, int length, int flags);
MFT_REF ntfs_get_parent_ref(ntfs_inode *ni);
ATTR_RECORD * find_attribute(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx);
ATTR_RECORD * find_first_attribute(const ATTR_TYPES type, MFT_RECORD *mft);
int utils_valid_device(const char *name, int force);
ntfs_volume * utils_mount_volume(const char *device, unsigned long flags, BOOL force);
ntfs_volume * utils_mount_volume(const char *device, unsigned long flags);
/**
* defines...
@@ -88,6 +85,7 @@ struct mft_search_ctx {
struct mft_search_ctx * mft_get_search_ctx(ntfs_volume *vol);
void mft_put_search_ctx(struct mft_search_ctx *ctx);
int mft_next_record(struct mft_search_ctx *ctx);
MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni);
// Flags for dump mem
#define DM_DEFAULTS 0
@@ -99,4 +97,22 @@ int mft_next_record(struct mft_search_ctx *ctx);
#define DM_BLUE (1 << 5)
#define DM_BOLD (1 << 6)
/* MAX_PATH definition was missing in ntfs-3g's headers. */
#ifndef MAX_PATH
#define MAX_PATH 1024
#endif
/**
* linux-ntfs's ntfs_mbstoucs has different semantics, so we emulate it with
* ntfs-3g's.
*/
int ntfs_mbstoucs_libntfscompat(const char *ins,
ntfschar **outs, int outs_len);
/* This simple utility function was missing from libntfs-3g. */
static __inline__ ntfschar *ntfs_attr_get_name(ATTR_RECORD *attr)
{
return (ntfschar*)((u8*)attr + le16_to_cpu(attr->name_offset));
}
#endif /* _NTFS_UTILS_H_ */