NTFS: update libntfs-3g to latest stable version
This commit is contained in:
@@ -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, §_size)) {
|
||||
ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n",
|
||||
(int) sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#elif defined(DKIOCGETBLOCKSIZE)
|
||||
{
|
||||
/* Mac OS X */
|
||||
uint32_t sect_size = 0;
|
||||
|
||||
if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, §_size)) {
|
||||
ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n",
|
||||
(int) sect_size);
|
||||
return sect_size;
|
||||
}
|
||||
}
|
||||
#else
|
||||
errno = EOPNOTSUPP;
|
||||
#endif
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user