Extended transparent compression support to sequential writing to compressed files

N2009_11_14_FIXES
jpandre 2009-04-03 13:33:24 +00:00
parent e2d1f3a1c3
commit 2fbaecd759
10 changed files with 1487 additions and 106 deletions

View File

@ -23,8 +23,8 @@
# Autoconf
AC_PREREQ(2.59)
AC_INIT([ntfs-3g],[2009.4.4],[ntfs-3g-devel@lists.sf.net])
LIBNTFS_3G_VERSION="54"
AC_INIT([ntfs-3g],[2009.2.1],[ntfs-3g-devel@lists.sf.net])
LIBNTFS_3G_VERSION="49"
AC_CONFIG_SRCDIR([src/ntfs-3g.c])
# Environment

View File

@ -175,6 +175,7 @@ struct _ntfs_attr {
runlist_element *rl;
ntfs_inode *ni;
ATTR_TYPES type;
ATTR_FLAGS data_flags;
ntfschar *name;
u32 name_len;
unsigned long state;
@ -246,7 +247,8 @@ typedef union {
} attr_val;
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
const BOOL compressed, const BOOL encrypted, const BOOL sparse,
const ATTR_FLAGS data_flags, const BOOL encrypted,
const BOOL sparse,
const s64 allocated_size, const s64 data_size,
const s64 initialized_size, const s64 compressed_size,
const u8 compression_unit);
@ -261,6 +263,7 @@ extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
void *b);
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
const void *b);
extern int ntfs_attr_pclose(ntfs_attr *na);
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
ntfschar *name, u32 name_len, s64 *data_size);
@ -295,6 +298,8 @@ extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
ntfschar *name, u8 name_len, u8 *val, s64 size);
extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask);
extern int ntfs_attr_rm(ntfs_attr *na);
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);

View File

@ -29,5 +29,11 @@
extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count,
void *b);
extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos,
s64 offs, s64 to_write, s64 rounded,
const void *b, int compressed_part);
extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, s64 offs);
#endif /* defined _NTFS_COMPRESS_H */

View File

@ -49,6 +49,8 @@ struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
s64 length; /* Run length in clusters. */
};
extern runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries);
extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,

View File

@ -59,6 +59,8 @@
#include "logging.h"
#include "misc.h"
#define STANDARD_COMPRESSION_UNIT 4
ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') };
ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'),
const_cpu_to_le16('S'),
@ -330,15 +332,17 @@ static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
* Final initialization for an ntfs attribute.
*/
void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
const BOOL compressed, const BOOL encrypted, const BOOL sparse,
const ATTR_FLAGS data_flags,
const BOOL encrypted, const BOOL sparse,
const s64 allocated_size, const s64 data_size,
const s64 initialized_size, const s64 compressed_size,
const u8 compression_unit)
{
if (!NAttrInitialized(na)) {
na->data_flags = data_flags;
if (non_resident)
NAttrSetNonResident(na);
if (compressed)
if (data_flags & ATTR_COMPRESSION_MASK)
NAttrSetCompressed(na);
if (encrypted)
NAttrSetEncrypted(na);
@ -347,7 +351,7 @@ void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
na->allocated_size = allocated_size;
na->data_size = data_size;
na->initialized_size = initialized_size;
if (compressed || sparse) {
if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) {
ntfs_volume *vol = na->ni->vol;
na->compressed_size = compressed_size;
@ -434,12 +438,25 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
*/
if (type == AT_ATTRIBUTE_LIST)
a->flags = 0;
if ((type == AT_DATA) && !a->initialized_size) {
/*
* Define/redefine the compression state if stream is
* empty, based on the compression mark on parent
* directory (for unnamed data streams) or on current
* inode (for named data streams). The compression mark
* may change any time, the compression state can only
* change when stream is void.
*/
a->flags &= ~ATTR_COMPRESSION_MASK;
if (na->ni->flags & FILE_ATTR_COMPRESSED)
a->flags |= ATTR_IS_COMPRESSED;
}
cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
if (na->type == AT_DATA && na->name == AT_UNNAMED &&
((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) ||
(!(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 "
@ -449,14 +466,15 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
}
if (a->non_resident) {
if ((a->flags & ATTR_IS_COMPRESSED) && !a->compression_unit) {
if ((a->flags & ATTR_COMPRESSION_MASK)
&& !a->compression_unit) {
errno = EIO;
ntfs_log_perror("Compressed inode %lld attr 0x%x has "
"no compression unit",
(unsigned long long)ni->mft_no, type);
goto put_err_out;
}
ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED,
ntfs_attr_init(na, TRUE, a->flags,
a->flags & ATTR_IS_ENCRYPTED,
a->flags & ATTR_IS_SPARSE,
sle64_to_cpu(a->allocated_size),
@ -466,7 +484,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
cs ? a->compression_unit : 0);
} else {
s64 l = le32_to_cpu(a->value_length);
ntfs_attr_init(na, FALSE, a->flags & ATTR_IS_COMPRESSED,
ntfs_attr_init(na, FALSE, a->flags,
a->flags & ATTR_IS_ENCRYPTED,
a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l,
cs ? (l + 7) & ~7 : 0, 0);
@ -794,8 +812,16 @@ static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b)
/* Sanity checking arguments is done in ntfs_attr_pread(). */
if (NAttrCompressed(na) && NAttrNonResident(na))
return ntfs_compressed_attr_pread(na, pos, count, b);
if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) {
if ((na->data_flags & ATTR_COMPRESSION_MASK)
== ATTR_IS_COMPRESSED)
return ntfs_compressed_attr_pread(na, pos, count, b);
else {
/* compression mode not supported */
errno = EOPNOTSUPP;
return -1;
}
}
/*
* Encrypted non-resident attributes are not supported. We return
* access denied, which is what Windows NT4 does, too.
@ -1034,6 +1060,7 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
runlist_element **rl, VCN *update_from)
{
s64 to_write;
s64 need;
ntfs_volume *vol = na->ni->vol;
int eo, ret = -1;
runlist *rlc;
@ -1067,7 +1094,16 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
while (rlc->vcn) {
rlc--;
if (rlc->lcn >= 0) {
lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn);
/*
* avoid fragmenting a compressed file
* Windows does not do that, and that may
* not be desirable for files which can
* be updated
*/
if (na->data_flags & ATTR_COMPRESSION_MASK)
lcn_seek_from = rlc->lcn + rlc->length;
else
lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn);
break;
}
}
@ -1085,14 +1121,29 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
}
}
rlc = ntfs_cluster_alloc(vol, from_vcn,
((*ofs + to_write - 1) >> vol->cluster_size_bits)
+ 1 + (*rl)->vcn - from_vcn,
need = ((*ofs + to_write - 1) >> vol->cluster_size_bits)
+ 1 + (*rl)->vcn - from_vcn;
if ((na->data_flags & ATTR_COMPRESSION_MASK)
&& (need < na->compression_block_clusters)) {
/*
* for a compressed file, be sure to allocate the full hole.
* We may need space to decompress existing compressed data.
*/
rlc = ntfs_cluster_alloc(vol, (*rl)->vcn, (*rl)->length,
lcn_seek_from, DATA_ZONE);
} else
rlc = ntfs_cluster_alloc(vol, from_vcn, need,
lcn_seek_from, DATA_ZONE);
if (!rlc)
goto err_out;
*rl = ntfs_runlists_merge(na->rl, rlc);
/*
* For a compressed attribute, we must be sure there is an
* available entry, so reserve it before it gets too late.
*/
if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK))
*rl = ntfs_rl_extend(*rl,1);
if (!*rl) {
eo = errno;
ntfs_log_perror("Failed to merge runlists");
@ -1177,14 +1228,19 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
s64 total = 0;
VCN update_from = -1;
ntfs_volume *vol;
s64 fullcount;
ntfs_attr_search_ctx *ctx = NULL;
runlist_element *rl;
s64 hole_end;
int eo;
int compressed_part;
struct {
unsigned int undo_initialized_size : 1;
unsigned int undo_data_size : 1;
} need_to = { 0, 0 };
BOOL makingnonresident = FALSE;
BOOL wasnonresident = FALSE;
BOOL compressed;
ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count "
"0x%llx.\n", (long long)na->ni->mft_no, na->type,
@ -1196,6 +1252,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
goto errno_set;
}
vol = na->ni->vol;
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
/*
* Encrypted non-resident attributes are not supported. We return
* access denied, which is what Windows NT4 does, too.
@ -1205,16 +1263,32 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
goto errno_set;
}
/* If this is a compressed attribute it needs special treatment. */
if (NAttrCompressed(na)) {
wasnonresident = NAttrNonResident(na) != 0;
makingnonresident = wasnonresident /* yes : already changed */
&& !pos && (count == na->initialized_size);
/*
* Writing to compressed files is currently restricted
* to appending data. However we have to accept
* recursive write calls to make the attribute non resident.
* These are writing at position 0 up to initialized_size.
* Compression is also restricted to data streams.
* Only ATTR_IS_COMPRESSED compression mode is supported.
*/
if (compressed
&& ((na->type != AT_DATA)
|| ((na->data_flags & ATTR_COMPRESSION_MASK)
!= ATTR_IS_COMPRESSED)
|| ((pos != na->initialized_size)
&& (pos || (count != na->initialized_size))))) {
// TODO: Implement writing compressed attributes! (AIA)
// return ntfs_attr_pwrite_compressed(ntfs_attr *na,
// const s64 pos, s64 count, void *b);
errno = EOPNOTSUPP;
goto errno_set;
}
if (!count)
goto out;
/* for a compressed file, get prepared to reserve a full block */
fullcount = count;
/* If the write reaches beyond the end, extend the attribute. */
old_data_size = na->data_size;
if (pos + count > na->data_size) {
@ -1222,7 +1296,23 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
ntfs_log_perror("Failed to enlarge attribute");
goto errno_set;
}
/* resizing may change the compression mode */
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
need_to.undo_data_size = 1;
}
/*
* For compressed data, a single full block was allocated
* to deal with compression, possibly in a previous call.
* We are not able to process several blocks because
* some clusters are freed after compression and
* new allocations have to be done before proceeding,
* so truncate the requested count if needed (big buffers).
*/
if (compressed) {
fullcount = na->data_size - pos;
if (count > fullcount)
count = fullcount;
}
old_initialized_size = na->initialized_size;
/* If it is a resident attribute, write the data to the mft record. */
@ -1267,6 +1357,15 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
if (pos + count > na->initialized_size) {
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
/*
* For a compressed attribute, we must be sure there is an
* available entry, so reserve it before it gets too late.
*/
if (compressed) {
na->rl = ntfs_rl_extend(na->rl,1);
if (!na->rl)
goto err_out;
}
/* Set initialized_size to @pos + @count. */
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
if (!ctx)
@ -1282,6 +1381,9 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
goto err_out;
ctx->attr->initialized_size = cpu_to_sle64(pos + count);
/* fix data_size for compressed files */
if (compressed)
ctx->attr->data_size = ctx->attr->initialized_size;
if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
ctx->mrec)) {
/*
@ -1315,16 +1417,57 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
*/
if (errno == ENOENT) {
errno = EIO;
ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__);
ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__);
}
goto err_out;
}
ofs = pos - (rl->vcn << vol->cluster_size_bits);
/*
* Determine if there is compressed data in the current
* compression block (when appending to an existing file).
* If so, decompression will be needed, and the full block
* must be allocated to be identified as uncompressed.
* This comes in two variants, depending on whether
* compression has saved at least one cluster.
* The compressed size can never be over full size by
* more than 485 (maximum for 15 compression blocks
* compressed to 4098 and the last 3640 bytes compressed
* to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485)
* This is less than the smallest cluster, so the hole is
* is never beyond the cluster next to the position of
* the first uncompressed byte to write.
*/
compressed_part = 0;
if (compressed) {
if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) {
s64 xofs;
if (wasnonresident)
compressed_part = na->compression_block_clusters
- rl[1].length;
rl++;
xofs = 0;
if (ntfs_attr_fill_hole(na,
rl->length << vol->cluster_size_bits,
&xofs, &rl, &update_from))
goto err_out;
/* the fist allocated cluster was not merged */
if (!xofs)
rl--;
} else
if ((rl->lcn == (LCN)LCN_HOLE)
&& wasnonresident
&& (rl->length < na->compression_block_clusters))
compressed_part
= na->compression_block_clusters
- rl->length;
/* normal hole filling will do later */
}
/*
* Scatter the data from the linear data buffer to the volume. Note, a
* partial final vcn is taken care of by the @count capping of write
* length.
*/
ofs = pos - (rl->vcn << vol->cluster_size_bits);
for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) {
if (rl->lcn == LCN_RL_NOT_MAPPED) {
rl = ntfs_attr_find_vcn(na, rl->vcn);
@ -1332,7 +1475,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
if (errno == ENOENT) {
errno = EIO;
ntfs_log_perror("%s: Failed to find VCN"
" #2", __FUNCTION__);
" #4", __FUNCTION__);
}
goto rl_err_out;
}
@ -1345,7 +1488,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
goto rl_err_out;
}
if (rl->lcn < (LCN)0) {
hole_end = rl->vcn + rl->length;
if (rl->lcn != (LCN)LCN_HOLE) {
@ -1355,10 +1497,17 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
(long long)rl->lcn);
goto rl_err_out;
}
if (ntfs_attr_fill_hole(na, count, &ofs, &rl, &update_from))
if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl,
&update_from))
goto err_out;
}
if (compressed) {
while (ofs >= (rl->length << vol->cluster_size_bits)) {
ofs -= rl->length << vol->cluster_size_bits;
rl++;
}
}
/* It is a real lcn, write it to the volume. */
to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs);
retry:
@ -1378,6 +1527,9 @@ retry:
* This will cause the kernel not to seek and read disk
* blocks during write(2) to fill the end of the buffer
* which increases write speed by 2-10 fold typically.
*
* This is done even for compressed files, because
* data is generally first written uncompressed.
*/
if (rounding && ((wend == na->initialized_size) ||
(wend < (hole_end << vol->cluster_size_bits)))){
@ -1393,13 +1545,27 @@ retry:
memcpy(cb, b, to_write);
memset(cb + to_write, 0, rounding - to_write);
written = ntfs_pwrite(vol->dev, wpos, rounding, cb);
if (written == rounding)
written = to_write;
if (compressed) {
written = ntfs_compressed_pwrite(na,
rl, wpos, ofs, to_write,
rounding, b, compressed_part);
} else {
written = ntfs_pwrite(vol->dev, wpos,
rounding, cb);
if (written == rounding)
written = to_write;
}
free(cb);
} else
written = ntfs_pwrite(vol->dev, wpos, to_write, b);
} else {
if (compressed) {
written = ntfs_compressed_pwrite(na,
rl, wpos, ofs, to_write,
to_write, b, compressed_part);
} else
written = ntfs_pwrite(vol->dev, wpos,
to_write, b);
}
} else
written = to_write;
/* If everything ok, update progress counters and continue. */
@ -1408,20 +1574,23 @@ retry:
count -= written;
b = (const u8*)b + written;
}
if (written == to_write)
continue;
/* If the syscall was interrupted, try again. */
if (written == (s64)-1 && errno == EINTR)
goto retry;
if (!written)
errno = EIO;
goto rl_err_out;
if (written != to_write) {
/* Partial write cannot be dealt with, stop there */
/* If the syscall was interrupted, try again. */
if (written == (s64)-1 && errno == EINTR)
goto retry;
if (!written)
errno = EIO;
goto rl_err_out;
}
compressed_part = 0;
}
done:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
/* Update mapping pairs if needed. */
if (update_from != -1)
if ((update_from != -1)
|| (compressed && !makingnonresident))
if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) {
/*
* FIXME: trying to recover by goto rl_err_out;
@ -1497,6 +1666,172 @@ errno_set:
goto out;
}
int ntfs_attr_pclose(ntfs_attr *na)
{
s64 written, ofs;
BOOL ok = TRUE;
VCN update_from = -1;
ntfs_volume *vol;
ntfs_attr_search_ctx *ctx = NULL;
runlist_element *rl;
int eo;
s64 hole;
int compressed_part;
BOOL compressed;
ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n",
na->ni->mft_no, na->type);
if (!na || !na->ni || !na->ni->vol) {
errno = EINVAL;
ntfs_log_perror("%s", __FUNCTION__);
goto errno_set;
}
vol = na->ni->vol;
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
/*
* Encrypted non-resident attributes are not supported. We return
* access denied, which is what Windows NT4 does, too.
*/
if (NAttrEncrypted(na) && NAttrNonResident(na)) {
errno = EACCES;
goto errno_set;
}
/* If this is not a compressed attribute get out */
/* same if it is resident */
if (!compressed || !NAttrNonResident(na))
goto out;
/*
* For a compressed attribute, we must be sure there is an
* available entry, so reserve it before it gets too late.
*/
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
na->rl = ntfs_rl_extend(na->rl,1);
if (!na->rl)
goto err_out;
/* Find the runlist element containing the terminal vcn. */
rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits);
if (!rl) {
/*
* If the vcn is not present it is an out of bounds write.
* However, we have already written the last byte uncompressed,
* so getting this here must be an error of some kind.
*/
if (errno == ENOENT) {
errno = EIO;
ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__);
}
goto err_out;
}
/*
* Scatter the data from the linear data buffer to the volume. Note, a
* partial final vcn is taken care of by the @count capping of write
* length.
*/
compressed_part = 0;
if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE))
compressed_part
= na->compression_block_clusters - rl[1].length;
else
if ((rl->lcn == (LCN)LCN_HOLE)
&& (rl->length < na->compression_block_clusters))
compressed_part
= na->compression_block_clusters
- rl->length;
/* done, if the last block set was compressed */
if (compressed_part)
goto out;
ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits);
if (rl->lcn == LCN_RL_NOT_MAPPED) {
rl = ntfs_attr_find_vcn(na, rl->vcn);
if (!rl) {
if (errno == ENOENT) {
errno = EIO;
ntfs_log_perror("%s: Failed to find VCN"
" #6", __FUNCTION__);
}
goto rl_err_out;
}
/* Needed for case when runs merged. */
ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits);
}
if (!rl->length) {
errno = EIO;
ntfs_log_perror("%s: Zero run length", __FUNCTION__);
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)",
__FUNCTION__,
(long long)rl->lcn);
goto rl_err_out;
}
if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from))
goto err_out;
}
while (ofs >= (rl->length << vol->cluster_size_bits)) {
ofs -= rl->length << vol->cluster_size_bits;
rl++;
}
retry:
if (!NVolReadOnly(vol)) {
written = ntfs_compressed_close(na, rl, ofs);
/* If everything ok, update progress counters and continue. */
if (!written)
goto done;
}
/* If the syscall was interrupted, try again. */
if (written == (s64)-1 && errno == EINTR)
goto retry;
if (!written)
errno = EIO;
goto rl_err_out;
done:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
/* Update mapping pairs if needed. */
if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) {
/*
* FIXME: trying to recover by goto rl_err_out;
* could cause driver hang by infinite looping.
*/
ok = FALSE;
goto out;
}
out:
ntfs_log_leave("\n");
return (!ok);
rl_err_out:
/*
* need not restore old sizes, only compressed_size
* can have changed. It has been set according to
* the current runlist while updating the mapping pairs,
* and must be kept consistent with the runlists.
*/
err_out:
eo = errno;
if (ctx)
ntfs_attr_put_search_ctx(ctx);
/* Update mapping pairs if needed. */
ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/);
errno = eo;
errno_set:
ok = FALSE;
goto out;
}
/**
* ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read
* @na: multi sector transfer protected ntfs attribute to read from
@ -2727,7 +3062,7 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size)
*/
int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
ntfschar *name, u8 name_len, u8 *val, u32 size,
ATTR_FLAGS flags)
ATTR_FLAGS data_flags)
{
ntfs_attr_search_ctx *ctx;
u32 length;
@ -2791,8 +3126,9 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
a->non_resident = 0;
a->name_length = name_len;
a->name_offset = (name_len
? cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) : 0);
a->flags = flags;
? cpu_to_le16(offsetof(ATTR_RECORD, resident_end))
: const_cpu_to_le16(0));
a->flags = data_flags;
a->instance = m->next_attr_instance;
a->value_length = cpu_to_le32(size);
a->value_offset = cpu_to_le16(length - ((size + 7) & ~7));
@ -2929,7 +3265,8 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
a->instance = m->next_attr_instance;
a->lowest_vcn = cpu_to_sle64(lowest_vcn);
a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size);
a->compression_unit = (flags & ATTR_IS_COMPRESSED) ? 4 : 0;
a->compression_unit = (flags & ATTR_IS_COMPRESSED)
? STANDARD_COMPRESSION_UNIT : 0;
/* If @lowest_vcn == 0, than setup empty attribute. */
if (!lowest_vcn) {
a->highest_vcn = cpu_to_sle64(-1);
@ -3255,9 +3592,17 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
add_attr_record:
if (is_resident) {
ATTR_FLAGS data_flags;
if ((ni->flags & FILE_ATTR_COMPRESSED)
&& ((type == AT_DATA)
|| ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30))))
data_flags = ATTR_IS_COMPRESSED;
else
data_flags = const_cpu_to_le16(0);
/* Add resident attribute. */
offset = ntfs_resident_attr_record_add(attr_ni, type, name,
name_len, val, size, 0);
name_len, val, size, data_flags);
if (offset < 0) {
if (errno == ENOSPC && can_be_non_resident)
goto add_non_resident;
@ -3271,7 +3616,7 @@ add_attr_record:
add_non_resident:
/* Add non resident attribute. */
offset = ntfs_non_resident_attr_record_add(attr_ni, type, name,
name_len, 0, 8, 0);
name_len, 0, 8, const_cpu_to_le16(0));
if (offset < 0) {
err = errno;
ntfs_log_perror("Failed to add non resident attribute");
@ -3318,6 +3663,34 @@ err_out:
return -1;
}
/*
* Change an attribute flag
*/
int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask)
{
ntfs_attr_search_ctx *ctx;
int res;
res = -1;
/* Search for designated attribute */
ctx = ntfs_attr_get_search_ctx(ni, NULL);
if (ctx) {
if (!ntfs_attr_lookup(type, name, name_len,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
/* do the requested change (all small endian le16) */
ctx->attr->flags = (ctx->attr->flags & ~mask)
| (flags & mask);
NInoSetDirty(ni);
res = 0;
}
ntfs_attr_put_search_ctx(ctx);
}
return (res);
}
/**
* ntfs_attr_rm - remove attribute from ntfs inode
* @na: opened ntfs attribute to delete
@ -3724,7 +4097,6 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
* FIXME: For now just clear all of these as we don't support them when
* writing.
*/
NAttrClearCompressed(na);
NAttrClearSparse(na);
NAttrClearEncrypted(na);
@ -3751,7 +4123,10 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
goto cluster_free_err_out;
}
/* Calculate new offsets for the name and the mapping pairs array. */
name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7;
if (na->ni->flags & FILE_ATTR_COMPRESSED)
name_ofs = (sizeof(ATTR_REC) + 7) & ~7;
else
name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7;
mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
/*
* Determine the size of the resident part of the non-resident
@ -3778,10 +4153,6 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
a->name_length * sizeof(ntfschar));
a->name_offset = cpu_to_le16(name_ofs);
/* Update the flags to match the in-memory ones. */
a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED |
ATTR_COMPRESSION_MASK);
/* Setup the fields specific to non-resident attributes. */
a->lowest_vcn = cpu_to_sle64(0);
a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >>
@ -3789,7 +4160,26 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
a->mapping_pairs_offset = cpu_to_le16(mp_ofs);
a->compression_unit = 0;
/*
* Update the flags to match the in-memory ones.
* However cannot change the compression state if we had
* a fuse_file_info open with a mark for release.
* The decisions about compression can only be made when
* creating/recreating the stream, not when making non resident.
*/
a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED);
if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) {
/* support only ATTR_IS_COMPRESSED compression mode */
a->compression_unit = STANDARD_COMPRESSION_UNIT;
a->compressed_size = const_cpu_to_le64(0);
na->compression_block_size
= 1 << (a->compression_unit + vol->cluster_size_bits);
na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT;
} else {
a->compression_unit = 0;
a->flags &= ~ATTR_COMPRESSION_MASK;
na->data_flags = a->flags;
}
memset(&a->reserved1, 0, sizeof(a->reserved1));
@ -3886,7 +4276,8 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
/* Update attribute size everywhere. */
na->data_size = na->initialized_size = newsize;
na->allocated_size = (newsize + 7) & ~7;
if (NAttrCompressed(na) || NAttrSparse(na))
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|| NAttrSparse(na))
na->compressed_size = na->allocated_size;
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
@ -4109,8 +4500,8 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
if (ntfs_attr_can_be_resident(vol, na->type))
return -1;
if (NAttrCompressed(na) || NAttrEncrypted(na)) {
ntfs_log_trace("Making compressed or encrypted files resident is not "
if (NAttrEncrypted(na)) {
ntfs_log_trace("Making encrypted files resident is not "
"implemented yet.\n");
errno = EOPNOTSUPP;
return -1;
@ -4158,6 +4549,16 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
a->flags = 0;
a->value_length = cpu_to_le32(na->data_size);
a->value_offset = cpu_to_le16(val_ofs);
/*
* If a data stream was wiped out, adjust the compression mode
* to current state of compression flag
*/
if (!na->data_size
&& (na->type == AT_DATA)
&& (na->ni->flags & FILE_ATTR_COMPRESSED)) {
a->flags |= ATTR_IS_COMPRESSED;
na->data_flags = a->flags;
}
/*
* File names cannot be non-resident so we would never see this here
* but at least it serves as a reminder that there may be attributes
@ -4211,7 +4612,6 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
/* Update in-memory struct ntfs_attr. */
NAttrClearNonResident(na);
NAttrClearCompressed(na);
NAttrClearSparse(na);
NAttrClearEncrypted(na);
na->initialized_size = na->data_size;
@ -4279,8 +4679,8 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
NAttrSetSparse(na);
a->flags |= ATTR_IS_SPARSE;
a->compression_unit = 4; /* Windows set it so, even if attribute
is not actually compressed. */
a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows
set it so, even if attribute is not actually compressed. */
memmove((u8*)a + le16_to_cpu(a->name_offset) + 8,
(u8*)a + le16_to_cpu(a->name_offset),
@ -4312,7 +4712,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
}
/* Update compressed size if required. */
if (sparse) {
if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) {
s64 new_compr_size;
new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl);
@ -4588,7 +4988,8 @@ retry:
cur_max_mp_size = le32_to_cpu(m->bytes_allocated) -
le32_to_cpu(m->bytes_in_use) -
(offsetof(ATTR_RECORD, compressed_size) +
((NAttrCompressed(na) || NAttrSparse(na)) ?
(((na->data_flags & ATTR_COMPRESSION_MASK)
|| NAttrSparse(na)) ?
sizeof(a->compressed_size) : 0)) -
((sizeof(ntfschar) * na->name_len + 7) & ~7);
if (mp_size > cur_max_mp_size)
@ -5047,6 +5448,8 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize)
int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
{
int ret = STATUS_ERROR;
s64 fullsize;
BOOL compressed;
if (!na || newsize < 0 ||
(na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) {
@ -5075,17 +5478,41 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
}
/*
* TODO: Implement making handling of compressed attributes.
* Currently we can only expand the attribute or delete it,
* and only for ATTR_IS_COMPRESSED. This is however possible
* for resident attributes when there is no open fuse context
* (important case : $INDEX_ROOT:$I30)
*/
if (NAttrCompressed(na)) {
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
if (compressed
&& NAttrNonResident(na)
&& (((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)
|| (newsize && (newsize < na->data_size)))) {
errno = EOPNOTSUPP;
ntfs_log_perror("Failed to truncate compressed attribute");
goto out;
}
if (NAttrNonResident(na)) {
if (newsize > na->data_size)
ret = ntfs_non_resident_attr_expand(na, newsize);
/*
* For compressed data, the last block must be fully
* allocated, and we do not known the size of compression
* block until the attribute has been made non-resident.
* Moreover we can only process a single compression
* block at a time, so we silently do not allocate more.
*
* Note : do not request truncate on compressed files
* unless being able to face the consequences !
*/
if (compressed && newsize)
fullsize = (na->data_size
| (na->compression_block_size - 1)) + 1;
else
ret = ntfs_non_resident_attr_shrink(na, newsize);
fullsize = newsize;
if (fullsize > na->data_size)
ret = ntfs_non_resident_attr_expand(na, fullsize);
else
ret = ntfs_non_resident_attr_shrink(na, fullsize);
} else
ret = ntfs_resident_attr_resize(na, newsize);
out:

View File

@ -5,6 +5,7 @@
* Copyright (c) 2004-2005 Anton Altaparmakov
* Copyright (c) 2004-2006 Szabolcs Szakacsits
* Copyright (c) 2005 Yura Pakhuchiy
* Copyright (c) 2009 Jean-Pierre Andre
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
@ -46,6 +47,7 @@
#include "layout.h"
#include "runlist.h"
#include "compress.h"
#include "lcnalloc.h"
#include "logging.h"
#include "misc.h"
@ -64,6 +66,261 @@ 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];
} ;
/*
* 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 i;
unsigned int pp;
BOOL less;
BOOL done;
const unsigned char *key;
int c;
unsigned int 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 const unsigned char *p1,*p2;
i = 1;
p1 = key;
p2 = &pctx->inbuf[pp];
mxi = NTFS_SB_SIZE - r;
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->dad[pp];
pctx->dad[r] = i;
pctx->lson[r] = pctx->lson[pp];
pctx->rson[r] = pctx->rson[pp];
pctx->dad[pctx->lson[pp]] = r;
pctx->dad[pctx->rson[pp]] = r;
if (pctx->rson[i] == pp)
pctx->rson[i] = r;
else
pctx->lson[i] = r;
pctx->dad[pp] = NIL; /* remove pp */
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
*
* Returns the end of the longest current string which matched
* or zero if there was a bug
*/
static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, unsigned int rr, int dd)
{
unsigned int bestlen = 0;
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;
}
pctx->len -= dd;
dd = 0;
}
}
} while (dd-- > 0);
return (rr);
bug :
return (0);
}
/*
* Compress an input block
*
* Returns the size of the compressed block (including header)
* or zero if there was an error
*/
static unsigned int ntfs_compress_block(const char *inbuf, unsigned int size, char *outbuf)
{
struct COMPRESS_CONTEXT *pctx;
char *ptag;
int dd;
unsigned int rr;
unsigned int last_match_length;
unsigned int q;
unsigned int xout;
unsigned int ntag;
pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT));
if (pctx) {
pctx->inbuf = (const unsigned char*)inbuf;
ntfs_init_compress_tree(pctx);
xout = 2;
ntag = 0;
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;
}
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;
}
last_match_length = pctx->match_length;
dd = last_match_length;
if (dd-- > 0) {
rr = ntfs_nextmatch(pctx,rr,dd);
if (!rr)
goto bug;
}
/*
* 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)) {
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);
outbuf[0] = 0xff;
outbuf[1] = 0x3f;
xout = NTFS_SB_SIZE + 2;
}
free(pctx);
} else {
xout = 0;
errno = ENOMEM;
}
return (xout); /* 0 for an error, > size if cannot compress */
bug :
return (0);
}
/**
* ntfs_decompress - decompress a compression block into an array of pages
* @dest: buffer to which to write the decompressed data
@ -108,7 +365,7 @@ do_next_sb:
* position in the compression block is one byte before its end so the
* first two checks do not detect it.
*/
if (cb == cb_end || !le16_to_cpup((u16*)cb) || dest == dest_end) {
if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) {
ntfs_log_debug("Completed. Returning success (0).\n");
return 0;
}
@ -123,12 +380,12 @@ do_next_sb:
goto return_overflow;
/* Setup the current sub-block source pointers and validate range. */
cb_sb_start = cb;
cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK)
cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK)
+ 3;
if (cb_sb_end > cb_end)
goto return_overflow;
/* Now, we are ready to process the current sub-block (sb). */
if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) {
if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) {
ntfs_log_debug("Found uncompressed sub-block.\n");
/* This sb is not compressed, just copy it into destination. */
/* Advance source position to first data byte. */
@ -202,7 +459,7 @@ do_next_tag:
for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1)
lg++;
/* Get the phrase token into i. */
pt = le16_to_cpup((u16*)cb);
pt = le16_to_cpup((le16*)cb);
/*
* Calculate starting position of the byte sequence in
* the destination using the fact that p = (pt >> (12 - lg)) + 1
@ -332,13 +589,19 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
u8 *dest, *cb, *cb_pos, *cb_end;
u32 cb_size;
int err;
ATTR_FLAGS data_flags;
FILE_ATTR_FLAGS compression;
unsigned int nr_cbs, cb_clusters;
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n",
(unsigned long long)na->ni->mft_no, na->type,
(long long)pos, (long long)count);
if (!na || !NAttrCompressed(na) || !na->ni || !na->ni->vol || !b ||
pos < 0 || count < 0) {
data_flags = na->data_flags;
compression = na->ni->flags & FILE_ATTR_COMPRESSED;
if (!na || !na->ni || !na->ni->vol || !b
|| ((data_flags & ATTR_COMPRESSION_MASK)
!= ATTR_IS_COMPRESSED)
|| pos < 0 || count < 0) {
errno = EINVAL;
return -1;
}
@ -379,12 +642,12 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
cb_clusters = na->compression_block_clusters;
/* Need a temporary buffer for each loaded compression block. */
cb = ntfs_malloc(cb_size);
cb = (u8*)ntfs_malloc(cb_size);
if (!cb)
return -1;
/* Need a temporary buffer for each uncompressed block. */
dest = ntfs_malloc(cb_size);
dest = (u8*)ntfs_malloc(cb_size);
if (!dest) {
free(cb);
return -1;
@ -450,6 +713,7 @@ do_next_cb:
to_read = min(count, cb_size - ofs);
ofs += vcn << vol->cluster_size_bits;
NAttrClearCompressed(na);
na->data_flags &= ~ATTR_COMPRESSION_MASK;
tdata_size = na->data_size;
tinitialized_size = na->initialized_size;
na->data_size = na->initialized_size = na->allocated_size;
@ -459,7 +723,8 @@ do_next_cb:
err = errno;
na->data_size = tdata_size;
na->initialized_size = tinitialized_size;
NAttrSetCompressed(na);
na->ni->flags |= compression;
na->data_flags = data_flags;
free(cb);
free(dest);
if (total)
@ -475,7 +740,8 @@ do_next_cb:
} while (to_read > 0);
na->data_size = tdata_size;
na->initialized_size = tinitialized_size;
NAttrSetCompressed(na);
na->ni->flags |= compression;
na->data_flags = data_flags;
ofs = 0;
} else {
s64 tdata_size, tinitialized_size;
@ -496,6 +762,7 @@ do_next_cb:
*/
to_read = cb_size;
NAttrClearCompressed(na);
na->data_flags &= ~ATTR_COMPRESSION_MASK;
tdata_size = na->data_size;
tinitialized_size = na->initialized_size;
na->data_size = na->initialized_size = na->allocated_size;
@ -507,7 +774,8 @@ do_next_cb:
err = errno;
na->data_size = tdata_size;
na->initialized_size = tinitialized_size;
NAttrSetCompressed(na);
na->ni->flags |= compression;
na->data_flags = data_flags;
free(cb);
free(dest);
if (total)
@ -520,7 +788,8 @@ do_next_cb:
} while (to_read > 0);
na->data_size = tdata_size;
na->initialized_size = tinitialized_size;
NAttrSetCompressed(na);
na->ni->flags |= compression;
na->data_flags = data_flags;
/* Just a precaution. */
if (cb_pos + 2 <= cb_end)
*(u16*)cb_pos = 0;
@ -550,3 +819,537 @@ do_next_cb:
/* Return number of bytes read. */
return total + total2;
}
/*
* Read data from a set of clusters
*
* Returns the amount of data read
*/
static int read_clusters(ntfs_volume *vol, const runlist_element *rl,
s64 offs, int to_read, char *inbuf)
{
int count;
int got, xgot;
s64 xpos;
BOOL first;
char *xinbuf;
const runlist_element *xrl;
got = 0;
xrl = rl;
xinbuf = inbuf;
first = TRUE;
do {
count = xrl->length << vol->cluster_size_bits;
xpos = xrl->lcn << vol->cluster_size_bits;
if (first) {
count -= offs;
xpos += offs;
}
if ((to_read - got) < count)
count = to_read - got;
xgot = ntfs_pread(vol->dev, xpos, count, xinbuf);
if (xgot == count) {
got += count;
xpos += count;
xinbuf += count;
xrl++;
}
first = FALSE;
} while ((xgot == count) && (got < to_read));
return (got);
}
/*
* Write data to a set of clusters
*
* Returns the amount of data written
*/
static int write_clusters(ntfs_volume *vol, const runlist_element *rl,
s64 offs, int to_write, const char *outbuf)
{
int count;
int put, xput;
s64 xpos;
BOOL first;
const char *xoutbuf;
const runlist_element *xrl;
put = 0;
xrl = rl;
xoutbuf = outbuf;
first = TRUE;
do {
count = xrl->length << vol->cluster_size_bits;
xpos = xrl->lcn << vol->cluster_size_bits;
if (first) {
count -= offs;
xpos += offs;
}
if ((to_write - put) < count)
count = to_write - put;
xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf);
if (xput == count) {
put += count;
xpos += count;
xoutbuf += count;
xrl++;
}
first = FALSE;
} while ((xput == count) && (put < to_write));
return (put);
}
/*
* Compress and write a set of blocks
*
* returns the size actually written (rounded to a full cluster)
* or 0 if could not compress (nothing is written)
* or -1 if there were an irrecoverable error (errno set)
*/
static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
s64 offs, unsigned int insz, const char *inbuf)
{
ntfs_volume *vol;
char *outbuf;
unsigned int compsz;
int written;
int rounded;
unsigned int clsz;
unsigned int p;
unsigned int sz;
unsigned int bsz;
BOOL fail;
vol = na->ni->vol;
written = 0; /* default return */
clsz = 1 << vol->cluster_size_bits;
/* may need 2 extra bytes per block and 2 more bytes */
outbuf = (char*)ntfs_malloc(na->compression_block_size
+ 2*(na->compression_block_size/NTFS_SB_SIZE)
+ 2);
if (outbuf) {
fail = FALSE;
compsz = 0;
for (p=0; (p<insz) && !fail; p+=NTFS_SB_SIZE) {
if ((p + NTFS_SB_SIZE) < insz)
bsz = NTFS_SB_SIZE;
else
bsz = insz - p;
sz = ntfs_compress_block(&inbuf[p],bsz,
&outbuf[compsz]);
/* fail if all the clusters (or more) are needed */
if (!sz || ((compsz + sz + clsz + 2)
> na->compression_block_size))
fail = TRUE;
else
compsz += sz;
}
if (!fail) {
/* add a couple of null bytes, space has been checked */
outbuf[compsz++] = 0;
outbuf[compsz++] = 0;
/* write a full cluster, to avoid partial reading */
rounded = ((compsz - 1) | (clsz - 1)) + 1;
written = write_clusters(vol, rl, offs, rounded, outbuf);
if (written != rounded) {
// previously written text has been spoilt, should return a specific error
ntfs_log_error("error writing compressed data\n");
errno = EIO;
written = -1;
}
}
free(outbuf);
}
return (written);
}
/*
* Free unneeded clusters after compression
*
* This generally requires an empty slot at the end of runlist,
* but we do not want to reallocate the runlist here because
* there are many pointers to it.
* So the empty slot has to be reserved beforehand
*
* Returns zero unless some error occurred (described by errno)
*/
static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
s64 used, s64 reserved)
{
int freecnt;
int usedcnt;
int res;
s64 freelcn;
s64 freevcn;
int freelength;
ntfs_volume *vol;
runlist_element *freerl;
res = -1; /* default return */
vol = na->ni->vol;
freecnt = (reserved - used) >> vol->cluster_size_bits;
usedcnt = (reserved >> vol->cluster_size_bits) - freecnt;
/* skip entries fully used, if any */
while (rl->length && (rl->length < usedcnt)) {
usedcnt -= rl->length;
rl++;
}
if (rl->length) {
/*
* Splitting the current allocation block requires
* an extra runlist element to create the hole.
* The required entry has been prereserved when
* mapping the runlist.
*/
freelcn = rl->lcn + usedcnt;
freevcn = rl->vcn + usedcnt;
freelength = rl->length - usedcnt;
/* new count of allocated clusters */
rl->length = usedcnt;
freerl = ++rl;
if (!((freevcn + freecnt)
& (na->compression_block_clusters - 1))) {
if (freelength > 0) {
/*
* move the unused part to the end. Doing so,
* the vcn will be out of order. This does
* not harm, the vcn are meaningless now, and
* only the lcn are meaningful for freeing.
*/
/* locate current end */
while (rl->length)
rl++;
/* new terminator relocated */
rl[1].vcn = rl->vcn;
rl[1].lcn = LCN_ENOENT;
rl[1].length = 0;
/* hole, currently allocated */
rl->vcn = freevcn;
rl->lcn = freelcn;
rl->length = freelength;
}
/* free the hole */
res = ntfs_cluster_free_from_rl(vol,freerl);
if (!res) {
/* mark hole as free */
freerl->lcn = LCN_HOLE;
freerl->vcn = freevcn;
freerl->length = freecnt;
/* and set up the new end */
freerl[1].lcn = LCN_ENOENT;
freerl[1].vcn = freevcn + freecnt;
freerl[1].length = 0;
}
} else {
ntfs_log_error("Bad end of a compression block set\n");
errno = EIO;
}
} else {
ntfs_log_error("No cluster to free after compression\n");
errno = EIO;
}
return (res);
}
/*
* Read existing data, decompress and append buffer
* Do nothing if something fails
*/
static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
s64 offs, int compsz, int pos,
char *outbuf, s64 to_write, const void *b)
{
int fail = 1;
char *compbuf;
int decompsz;
int got;
compbuf = (char*)ntfs_malloc(compsz);
if (compbuf) {
/* must align to full block for decompression */
decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1;
got = read_clusters(na->ni->vol, rl, offs, compsz, compbuf);
if ((got == compsz)
&& !ntfs_decompress((u8*)outbuf,decompsz,
(u8*)compbuf,compsz)) {
memcpy(&outbuf[pos],b,to_write);
fail = 0;
}
free(compbuf);
}
return (fail);
}
/*
* Flush a full compression block
*
* returns the size actually written (rounded to a full cluster)
* or 0 if could not compress (and written uncompressed)
* or -1 if there were an irrecoverable error (errno set)
*/
static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
const char *outbuf, int count, BOOL compress)
{
int rounded;
int written;
int clsz;
if (compress) {
written = ntfs_comp_set(na, rl, offs, count, outbuf);
if (!written)
compress = FALSE;
if ((written > 0)
&& ntfs_compress_free(na,rl,written,
na->compression_block_size))
written = -1;
}
if (!compress) {
clsz = 1 << na->ni->vol->cluster_size_bits;
rounded = ((count - 1) | (clsz - 1)) + 1;
written = write_clusters(na->ni->vol, rl,
offs, rounded, outbuf);
if (written != rounded)
written = -1;
}
return (written);
}
/*
* Write some data to be compressed.
* Compression only occurs when a few clusters (usually 16) are
* full. When this occurs an extra runlist slot may be needed, so
* it has to be reserved beforehand.
*
* Returns the size of uncompressed data written,
* or zero if an error occurred.
* When the returned size is less than requested, new clusters have
* to be allocated before the function is called again.
*/
s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
s64 offs, s64 to_write, s64 rounded,
const void *b, int compressed_part)
{
ntfs_volume *vol;
runlist_element *brl; /* entry containing the beginning of block */
int compression_length;
s64 written;
s64 to_read;
s64 roffs;
s64 got;
s64 start_vcn;
s64 nextblock;
int compsz;
char *inbuf;
char *outbuf;
BOOL fail;
BOOL done;
BOOL compress;
written = 0; /* default return */
vol = na->ni->vol;
compression_length = na->compression_block_clusters;
compress = FALSE;
done = FALSE;
/*
* Cannot accept writing beyond the current compression set
* because when compression occurs, clusters are freed
* and have to be reallocated.
* (cannot happen with standard fuse 4K buffers)
* Caller has to avoid this situation, or face consequences.
*/
nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits))
| (na->compression_block_size - 1)) + 1;
if ((offs + to_write + (wrl->vcn << vol->cluster_size_bits))
>= nextblock) {
/* it is time to compress */
compress = TRUE;
/* only process what we can */
to_write = rounded = nextblock
- (offs + (wrl->vcn << vol->cluster_size_bits));
}
start_vcn = 0;
fail = FALSE;
brl = wrl;
roffs = 0;
/*
* If we are about to compress or we need to decompress
* existing data, we have to process a full set of blocks.
* So relocate the parameters to the beginning of allocation
* containing the first byte of the set of blocks.
*/
if (compress || compressed_part) {
/* find the beginning of block */
start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits))
& -compression_length;
while (brl->vcn && (brl->vcn > start_vcn)) {
/* jumping back a hole means big trouble */
if (brl->lcn == (LCN)LCN_HOLE) {
ntfs_log_error("jump back over a hole when appending\n");
fail = TRUE;
errno = EIO;
}
brl--;
offs += brl->length << vol->cluster_size_bits;
}
roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits;
}
if (compressed_part && !fail) {
/*
* The set of compression blocks contains compressed data
* (we are reopening an existing file to append to it)
* Decompress the data and append
*/
compsz = compressed_part << vol->cluster_size_bits;
// improve the needed size
outbuf = (char*)ntfs_malloc(na->compression_block_size);
if (outbuf) {
to_read = offs - roffs;
if (!ntfs_read_append(na, brl, roffs, compsz,
to_read, outbuf, to_write, b)) {
written = ntfs_flush(na, brl, roffs,
outbuf, to_read + to_write, compress);
if (written >= 0) {
written = to_write;
done = TRUE;
}
}
free(outbuf);
}
} else {
if (compress && !fail) {
/*
* we are filling up a block, read the full set of blocks
* and compress it
*/
inbuf = (char*)ntfs_malloc(na->compression_block_size);
if (inbuf) {
to_read = offs - roffs;
if (to_read)
got = read_clusters(vol, brl, roffs,
to_read, inbuf);
else
got = 0;
if (got == to_read) {
memcpy(&inbuf[to_read],b,to_write);
written = ntfs_comp_set(na, brl, roffs,
to_read + to_write, inbuf);
/*
* if compression was not successful,
* only write the part which was requested
*/
if ((written > 0)
/* free the unused clusters */
&& !ntfs_compress_free(na,brl,
written + roffs,
na->compression_block_size
+ roffs)) {
done = TRUE;
written = to_write;
}
}
free(inbuf);
}
}
if (!done) {
/*
* if the compression block is not full, or
* if compression failed for whatever reason,
* write uncompressed
*/
/* check we are not overflowing current allocation */
if ((wpos + rounded)
> ((wrl->lcn + wrl->length)
<< vol->cluster_size_bits)) {
ntfs_log_error("writing on unallocated clusters\n");
errno = EIO;
} else {
written = ntfs_pwrite(vol->dev, wpos,
rounded, b);
if (written == rounded)
written = to_write;
}
}
}
return (written);
}
/*
* Close a file written compressed.
* This compresses the last partial compression block of the file.
* An empty runlist slot has to be reserved beforehand.
*
* Returns zero if closing is successful.
*/
int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs)
{
ntfs_volume *vol;
runlist_element *brl; /* entry containing the beginning of block */
int compression_length;
s64 written;
s64 to_read;
s64 roffs;
s64 got;
s64 start_vcn;
char *inbuf;
BOOL fail;
BOOL done;
vol = na->ni->vol;
compression_length = na->compression_block_clusters;
done = FALSE;
/*
* There generally is an uncompressed block at end of file,
* read the full block and compress it
*/
inbuf = (char*)ntfs_malloc(na->compression_block_size);
if (inbuf) {
start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits))
& -compression_length;
to_read = offs + ((wrl->vcn - start_vcn) << vol->cluster_size_bits);
brl = wrl;
fail = FALSE;
while (brl->vcn && (brl->vcn > start_vcn)) {
if (brl->lcn == (LCN)LCN_HOLE) {
ntfs_log_error("jump back over a hole when closing\n");
fail = TRUE;
errno = EIO;
}
brl--;
}
if (!fail) {
/* roffs can be an offset from another uncomp block */
roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits;
if (to_read) {
got = read_clusters(vol, brl, roffs, to_read,
inbuf);
if (got == to_read) {
written = ntfs_comp_set(na, brl, roffs,
to_read, inbuf);
if ((written > 0)
/* free the unused clusters */
&& !ntfs_compress_free(na,brl,
written + roffs,
na->compression_block_size + roffs)) {
done = TRUE;
} else
/* if compression failed, leave uncompressed */
if (!written)
done = TRUE;
}
} else
done = TRUE;
free(inbuf);
}
}
return (!done);
}

View File

@ -1218,6 +1218,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
si->file_attributes = FILE_ATTR_SYSTEM;
ni->flags = FILE_ATTR_SYSTEM;
}
if ((dir_ni->flags & FILE_ATTR_COMPRESSED)
&& (S_ISREG(type) || S_ISDIR(type)))
ni->flags |= FILE_ATTR_COMPRESSED;
/* Add STANDARD_INFORMATION to inode. */
if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0,
(u8*)si, si_len)) {

View File

@ -111,6 +111,27 @@ static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size,
return realloc(rl, new_size);
}
/*
* Extend a runlist by some entry count
* The runlist may have to be reallocated
*
* Returns the reallocated runlist
* or NULL if reallocation was not possible (with errno set)
*/
runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries)
{
int last;
last = 0;
while (rl[last].length)
last++;
rl = ntfs_rl_realloc(rl,last+1,last+more_entries+1);
if (!rl)
errno = ENOMEM;
return (rl);
}
/**
* ntfs_rl_are_mergeable - test if two runlists can be joined together
* @dst: original runlist

View File

@ -3877,6 +3877,7 @@ int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)),
{
u32 attrib;
le32 settable;
ATTR_FLAGS dirflags;
int res;
res = -1;
@ -3885,12 +3886,31 @@ int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)),
/* copy to avoid alignment problems */
memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
settable = FILE_ATTR_SETTABLE;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
settable |= FILE_ATTR_COMPRESSED;
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoSetDirty(ni);
res = 0;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
/*
* Accept changing compression for a directory
* and set index root accordingly
*/
settable |= FILE_ATTR_COMPRESSED;
if ((ni->flags ^ cpu_to_le32(attrib))
& FILE_ATTR_COMPRESSED) {
if (ni->flags & FILE_ATTR_COMPRESSED)
dirflags = const_cpu_to_le16(0);
else
dirflags = ATTR_IS_COMPRESSED;
res = ntfs_attr_set_flags(ni,
AT_INDEX_ROOT,
NTFS_INDEX_I30, 4,
dirflags,
ATTR_COMPRESSION_MASK);
}
}
if (!res) {
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoSetDirty(ni);
}
} else
errno = EEXIST;
} else
@ -4461,6 +4481,7 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
{
ntfs_inode *ni;
le32 settable;
ATTR_FLAGS dirflags;
int res;
res = 0; /* default return */
@ -4468,11 +4489,30 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
if (ni) {
settable = FILE_ATTR_SETTABLE;
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
/*
* Accept changing compression for a directory
* and set index root accordingly
*/
settable |= FILE_ATTR_COMPRESSED;
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoSetDirty(ni);
if ((ni->flags ^ cpu_to_le32(attrib))
& FILE_ATTR_COMPRESSED) {
if (ni->flags & FILE_ATTR_COMPRESSED)
dirflags = const_cpu_to_le16(0);
else
dirflags = ATTR_IS_COMPRESSED;
res = ntfs_attr_set_flags(ni,
AT_INDEX_ROOT,
NTFS_INDEX_I30, 4,
dirflags,
ATTR_COMPRESSION_MASK);
}
}
if (!res) {
ni->flags = (ni->flags & ~settable)
| (cpu_to_le32(attrib) & settable);
NInoSetDirty(ni);
}
if (!ntfs_inode_close(ni))
res = -1;
} else

View File

@ -880,6 +880,11 @@ static int ntfs_fuse_open(const char *org_path,
if (NAttrEncrypted(na))
res = -EACCES;
#endif
/* mark a future need to compress the last chunk */
if ((res >= 0)
&& (na->data_flags & ATTR_COMPRESSION_MASK)
&& (fi->flags & (O_WRONLY | O_RDWR)))
fi->fh |= 1;
ntfs_attr_close(na);
} else
res = -errno;
@ -978,10 +983,6 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
}
while (size) {
s64 ret = ntfs_attr_pwrite(na, offset, size, buf);
if (0 <= ret && ret < (s64)size)
ntfs_log_perror("ntfs_attr_pwrite partial write to '%s'"
" (%lld: %lld <> %lld)", path, (long long)offset,
(long long)size, (long long)ret);
if (ret <= 0) {
res = -errno;
goto exit;
@ -1005,6 +1006,48 @@ out:
return res;
}
static int ntfs_fuse_release(const char *org_path,
struct fuse_file_info *fi)
{
ntfs_inode *ni = NULL;
ntfs_attr *na = NULL;
char *path = NULL;
ntfschar *stream_name;
int stream_name_len, res;
/* Only for marked descriptors there is something to do */
if (!(fi->fh & 1)) {
res = 0;
goto out;
}
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
if (stream_name_len < 0) {
res = stream_name_len;
goto out;
}
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
if (!ni) {
res = -errno;
goto exit;
}
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
if (!na) {
res = -errno;
goto exit;
}
res = ntfs_attr_pclose(na);
exit:
if (na)
ntfs_attr_close(na);
if (ntfs_inode_close(ni))
set_fuse_error(&res);
free(path);
if (stream_name_len)
free(stream_name);
out:
return res;
}
/*
* Common part for truncate() and ftruncate()
*/
@ -1049,7 +1092,11 @@ static int ntfs_fuse_trunc(const char *org_path, off_t size,
goto exit;
}
#endif
/* for compressed files, only deleting contents is implemented */
if (NAttrCompressed(na) && size) {
errno = EOPNOTSUPP;
goto exit;
}
if (ntfs_attr_truncate(na, size))
goto exit;
@ -1211,7 +1258,7 @@ static int ntfs_fuse_access(const char *path, int type)
#endif
static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
const char *target)
const char *target, struct fuse_file_info *fi)
{
char *name;
ntfschar *uname = NULL, *utarget = NULL;
@ -1325,6 +1372,10 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
security.uid, security.gid, perm) < 0)
set_fuse_error(&res);
#endif
}
/* mark a need to compress the end of file */
if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) {
fi->fh |= 1;
}
NInoSetDirty(ni);
if (ntfs_inode_close(ni))
@ -1349,7 +1400,8 @@ exit:
}
static int ntfs_fuse_create_stream(const char *path,
ntfschar *stream_name, const int stream_name_len)
ntfschar *stream_name, const int stream_name_len,
struct fuse_file_info *fi)
{
ntfs_inode *ni;
int res = 0;
@ -1361,11 +1413,13 @@ static int ntfs_fuse_create_stream(const char *path,
/*
* If such file does not exist, create it and try once
* again to add stream to it.
* Note : no fuse_file_info for creation of main file
*/
res = ntfs_fuse_create(path, S_IFREG, 0, NULL);
res = ntfs_fuse_create(path, S_IFREG, 0, NULL,
(struct fuse_file_info*)NULL);
if (!res)
return ntfs_fuse_create_stream(path,
stream_name, stream_name_len);
stream_name, stream_name_len,fi);
else
res = -errno;
}
@ -1373,12 +1427,16 @@ static int ntfs_fuse_create_stream(const char *path,
}
if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0))
res = -errno;
if ((res >= 0) && (ni->flags & FILE_ATTR_COMPRESSED)
&& (fi->flags & (O_WRONLY | O_RDWR)))
fi->fh |= 1;
if (ntfs_inode_close(ni))
set_fuse_error(&res);
return res;
}
static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev)
static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev,
struct fuse_file_info *fi)
{
char *path = NULL;
ntfschar *stream_name;
@ -1393,10 +1451,11 @@ static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev)
goto exit;
}
if (!stream_name_len)
res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev, NULL);
res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev,
NULL,fi);
else
res = ntfs_fuse_create_stream(path, stream_name,
stream_name_len);
stream_name_len,fi);
exit:
free(path);
if (stream_name_len)
@ -1406,20 +1465,22 @@ exit:
static int ntfs_fuse_mknod(const char *path, mode_t mode, dev_t dev)
{
return ntfs_fuse_mknod_common(path, mode, dev);
return ntfs_fuse_mknod_common(path, mode, dev,
(struct fuse_file_info*)NULL);
}
static int ntfs_fuse_create_file(const char *path, mode_t mode,
struct fuse_file_info *fi __attribute__((unused)))
struct fuse_file_info *fi)
{
return ntfs_fuse_mknod_common(path, mode, 0);
return ntfs_fuse_mknod_common(path, mode, 0, fi);
}
static int ntfs_fuse_symlink(const char *to, const char *from)
{
if (ntfs_fuse_is_named_data_stream(from))
return -EINVAL; /* n/a for named data streams. */
return ntfs_fuse_create(from, S_IFLNK, 0, to);
return ntfs_fuse_create(from, S_IFLNK, 0, to,
(struct fuse_file_info*)NULL);
}
static int ntfs_fuse_link(const char *old_path, const char *new_path)
@ -1754,7 +1815,8 @@ static int ntfs_fuse_mkdir(const char *path,
{
if (ntfs_fuse_is_named_data_stream(path))
return -EINVAL; /* n/a for named data streams. */
return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL);
return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL,
(struct fuse_file_info*)NULL);
}
static int ntfs_fuse_rmdir(const char *path)
@ -2357,6 +2419,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
ntfs_attr *na = NULL;
ntfschar *lename = NULL;
int res, lename_len;
size_t part, total;
int attr;
int namespace;
struct SECURITY_CONTEXT security;
@ -2522,15 +2585,25 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
goto exit;
}
} else {
if (ntfs_attr_truncate(na, (s64)size)) {
/* currently compressed streams can only be wiped out */
if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
res = -errno;
goto exit;
}
}
res = ntfs_attr_pwrite(na, 0, size, value);
if (res != (s64) size)
total = 0;
if (size) {
do {
part = ntfs_attr_pwrite(na, total, size - total,
&value[total]);
if (part > 0)
total += part;
} while ((part > 0) && (total < size));
if (total != size)
res = -errno;
else
res = ntfs_attr_pclose(na);
} else
res = 0;
exit:
if (na)
@ -2754,6 +2827,7 @@ static struct fuse_operations ntfs_3g_ops = {
.readlink = ntfs_fuse_readlink,
.readdir = ntfs_fuse_readdir,
.open = ntfs_fuse_open,
.release = ntfs_fuse_release,
.read = ntfs_fuse_read,
.write = ntfs_fuse_write,
.truncate = ntfs_fuse_truncate,