summaryrefslogtreecommitdiff
Diffstat
-rwxr-xr-xAndroid.mk138
-rwxr-xr-xAndroid.mk.bak89
-rwxr-xr-xCREDITS20
-rwxr-xr-xINSTALL200
-rwxr-xr-xMakefile715
-rwxr-xr-xMakefile.am106
-rwxr-xr-xMakefile.in955
-rwxr-xr-xNEWS4
-rwxr-xr-xREADME80
-rwxr-xr-xTODO.ntfsprogs126
-rwxr-xr-xaclocal.m47091
-rwxr-xr-xcompile21
-rwxr-xr-xconfig.guess292
-rw-r--r--[-rwxr-xr-x]config.h165
-rwxr-xr-xconfig.h.in110
-rwxr-xr-xconfig.log3290
-rwxr-xr-xconfig.status1220
-rwxr-xr-xconfig.sub104
-rwxr-xr-xconfigure26448
-rwxr-xr-xconfigure.ac291
-rwxr-xr-xdepcomp87
-rwxr-xr-xinclude/Makefile.in181
-rwxr-xr-xinclude/fuse-lite/Makefile.in112
-rwxr-xr-xinclude/fuse-lite/fuse.h61
-rwxr-xr-xinclude/fuse-lite/fuse_common.h49
-rwxr-xr-xinclude/fuse-lite/fuse_kernel.h6
-rwxr-xr-xinclude/fuse-lite/fuse_lowlevel.h20
-rwxr-xr-xinclude/ntfs-3g/Makefile.am4
-rwxr-xr-xinclude/ntfs-3g/Makefile.in163
-rwxr-xr-xinclude/ntfs-3g/acls.h3
-rwxr-xr-xinclude/ntfs-3g/attrib.h50
-rwxr-xr-xinclude/ntfs-3g/cache.h7
-rwxr-xr-xinclude/ntfs-3g/compat.h6
-rwxr-xr-xinclude/ntfs-3g/compress.h6
-rwxr-xr-xinclude/ntfs-3g/debug.h4
-rwxr-xr-xinclude/ntfs-3g/device.h18
-rwxr-xr-xinclude/ntfs-3g/device_io.h21
-rwxr-xr-xinclude/ntfs-3g/dir.h11
-rwxr-xr-xinclude/ntfs-3g/layout.h32
-rwxr-xr-xinclude/ntfs-3g/lcnalloc.h1
-rwxr-xr-xinclude/ntfs-3g/logging.h3
-rwxr-xr-xinclude/ntfs-3g/mst.h3
-rwxr-xr-xinclude/ntfs-3g/ntfstime.h12
-rwxr-xr-xinclude/ntfs-3g/param.h72
-rwxr-xr-xinclude/ntfs-3g/realpath.h24
-rwxr-xr-xinclude/ntfs-3g/runlist.h3
-rwxr-xr-xinclude/ntfs-3g/security.h20
-rwxr-xr-xinclude/ntfs-3g/types.h8
-rwxr-xr-xinclude/ntfs-3g/unistr.h13
-rwxr-xr-xinclude/ntfs-3g/volume.h78
-rwxr-xr-xinclude/ntfs-3g/xattrs.h75
-rwxr-xr-xinstall-sh5
-rwxr-xr-xlibfuse-lite/Makefile.am3
-rwxr-xr-xlibfuse-lite/Makefile.in188
-rwxr-xr-xlibfuse-lite/fuse.c418
-rwxr-xr-xlibfuse-lite/fuse_kern_chan.c2
-rwxr-xr-xlibfuse-lite/fuse_lowlevel.c53
-rwxr-xr-xlibfuse-lite/fuse_opt.c20
-rwxr-xr-xlibfuse-lite/fuse_session.c12
-rwxr-xr-xlibfuse-lite/fusermount.c97
-rwxr-xr-xlibfuse-lite/helper.c21
-rwxr-xr-xlibfuse-lite/mount.c486
-rwxr-xr-xlibfuse-lite/mount_util.c246
-rwxr-xr-xlibfuse-lite/mount_util.h11
-rwxr-xr-xlibntfs-3g/Makefile.am15
-rwxr-xr-xlibntfs-3g/Makefile.in420
-rwxr-xr-xlibntfs-3g/acls.c219
-rwxr-xr-xlibntfs-3g/attrib.c1205
-rwxr-xr-xlibntfs-3g/bootsect.c4
-rwxr-xr-xlibntfs-3g/cache.c7
-rwxr-xr-xlibntfs-3g/compress.c962
-rwxr-xr-xlibntfs-3g/device.c252
-rwxr-xr-xlibntfs-3g/dir.c374
-rwxr-xr-xlibntfs-3g/efs.c217
-rwxr-xr-xlibntfs-3g/index.c30
-rwxr-xr-xlibntfs-3g/inode.c46
-rwxr-xr-xlibntfs-3g/lcnalloc.c36
-rw-r--r--libntfs-3g/libntfs-3g.pc10
-rw-r--r--libntfs-3g/libntfs-3g.script.so2
-rwxr-xr-xlibntfs-3g/logfile.c23
-rwxr-xr-xlibntfs-3g/logging.c24
-rwxr-xr-xlibntfs-3g/mft.c12
-rwxr-xr-xlibntfs-3g/mst.c24
-rwxr-xr-xlibntfs-3g/object_id.c9
-rwxr-xr-xlibntfs-3g/realpath.c103
-rwxr-xr-xlibntfs-3g/reparse.c67
-rwxr-xr-xlibntfs-3g/runlist.c62
-rwxr-xr-xlibntfs-3g/security.c345
-rwxr-xr-xlibntfs-3g/unistr.c244
-rwxr-xr-xlibntfs-3g/unix_io.c19
-rwxr-xr-xlibntfs-3g/volume.c365
-rwxr-xr-xlibntfs-3g/win32_io.c708
-rwxr-xr-xlibntfs-3g/xattrs.c791
-rwxr-xr-xlibtool9301
-rwxr-xr-xltmain.sh8439
-rwxr-xr-xm4/libtool.m47377
-rwxr-xr-xm4/ltoptions.m4368
-rwxr-xr-xm4/ltsugar.m4123
-rwxr-xr-xm4/ltversion.m423
-rwxr-xr-xm4/lt~obsolete.m492
-rwxr-xr-xmissing49
-rwxr-xr-xntfsprogs/Makefile.am154
-rwxr-xr-xntfsprogs/Makefile.in1199
-rwxr-xr-xntfsprogs/attrdef.c168
-rwxr-xr-xntfsprogs/attrdef.h7
-rwxr-xr-xntfsprogs/boot.c268
-rwxr-xr-xntfsprogs/boot.h7
-rwxr-xr-xntfsprogs/cluster.c118
-rwxr-xr-xntfsprogs/cluster.h39
-rwxr-xr-xntfsprogs/list.h194
-rw-r--r--ntfsprogs/mkntfs.8290
-rwxr-xr-xntfsprogs/mkntfs.8.in290
-rwxr-xr-xntfsprogs/mkntfs.c5177
-rw-r--r--ntfsprogs/ntfscat.8136
-rwxr-xr-xntfsprogs/ntfscat.8.in136
-rwxr-xr-xntfsprogs/ntfscat.c440
-rwxr-xr-xntfsprogs/ntfscat.h46
-rwxr-xr-xntfsprogs/ntfsck.c883
-rw-r--r--ntfsprogs/ntfsclone.8391
-rwxr-xr-xntfsprogs/ntfsclone.8.in391
-rwxr-xr-xntfsprogs/ntfsclone.c2701
-rw-r--r--ntfsprogs/ntfscluster.8124
-rwxr-xr-xntfsprogs/ntfscluster.8.in124
-rwxr-xr-xntfsprogs/ntfscluster.c563
-rwxr-xr-xntfsprogs/ntfscluster.h63
-rw-r--r--ntfsprogs/ntfscmp.877
-rwxr-xr-xntfsprogs/ntfscmp.8.in77
-rwxr-xr-xntfsprogs/ntfscmp.c1012
-rw-r--r--ntfsprogs/ntfscp.8111
-rwxr-xr-xntfsprogs/ntfscp.8.in111
-rwxr-xr-xntfsprogs/ntfscp.c590
-rwxr-xr-xntfsprogs/ntfsdecrypt.c1436
-rwxr-xr-xntfsprogs/ntfsdump_logfile.c779
-rw-r--r--ntfsprogs/ntfsfix.881
-rwxr-xr-xntfsprogs/ntfsfix.8.in81
-rwxr-xr-xntfsprogs/ntfsfix.c1657
-rw-r--r--ntfsprogs/ntfsinfo.889
-rwxr-xr-xntfsprogs/ntfsinfo.8.in89
-rwxr-xr-xntfsprogs/ntfsinfo.c2384
-rw-r--r--ntfsprogs/ntfslabel.8118
-rwxr-xr-xntfsprogs/ntfslabel.8.in118
-rwxr-xr-xntfsprogs/ntfslabel.c458
-rw-r--r--ntfsprogs/ntfsls.8172
-rwxr-xr-xntfsprogs/ntfsls.8.in172
-rwxr-xr-xntfsprogs/ntfsls.c717
-rwxr-xr-xntfsprogs/ntfsmftalloc.c368
-rwxr-xr-xntfsprogs/ntfsmove.c923
-rwxr-xr-xntfsprogs/ntfsmove.h46
-rw-r--r--ntfsprogs/ntfsprogs.869
-rwxr-xr-xntfsprogs/ntfsprogs.8.in69
-rw-r--r--ntfsprogs/ntfsresize.8326
-rwxr-xr-xntfsprogs/ntfsresize.8.in326
-rwxr-xr-xntfsprogs/ntfsresize.c4497
-rwxr-xr-xntfsprogs/ntfstruncate.c809
-rw-r--r--ntfsprogs/ntfsundelete.8324
-rwxr-xr-xntfsprogs/ntfsundelete.8.in324
-rwxr-xr-xntfsprogs/ntfsundelete.c2490
-rwxr-xr-xntfsprogs/ntfsundelete.h112
-rwxr-xr-xntfsprogs/ntfswipe.c2131
-rwxr-xr-xntfsprogs/ntfswipe.h54
-rwxr-xr-xntfsprogs/sd.c607
-rwxr-xr-xntfsprogs/sd.h11
-rwxr-xr-xntfsprogs/utils.c1184
-rwxr-xr-xntfsprogs/utils.h137
-rwxr-xr-xprog.IAB1071
-rwxr-xr-xprog.IAD5
-rwxr-xr-xprog.IMB466
-rwxr-xr-xprog.IMD2
-rwxr-xr-xprog.PFI2
-rwxr-xr-xprog.PO1
-rwxr-xr-xprog.PR14
-rwxr-xr-xprog.PRI219
-rwxr-xr-xprog.PS979
-rwxr-xr-xprog.SearchResults3
-rwxr-xr-xprog.WK39
-rwxr-xr-xsrc/Makefile.am86
-rwxr-xr-xsrc/Makefile.in938
-rwxr-xr-xsrc/lowntfs-3g.c1538
-rw-r--r--src/ntfs-3g.8448
-rwxr-xr-xsrc/ntfs-3g.8.in217
-rwxr-xr-xsrc/ntfs-3g.c1357
-rw-r--r--src/ntfs-3g.probe.881
-rwxr-xr-xsrc/ntfs-3g.probe.8.in4
-rwxr-xr-xsrc/ntfs-3g.probe.c9
-rw-r--r--src/ntfs-3g.secaudit.8184
-rwxr-xr-xsrc/ntfs-3g.secaudit.8.in15
-rw-r--r--src/ntfs-3g.usermap.896
-rwxr-xr-xsrc/ntfs-3g_common.c745
-rwxr-xr-xsrc/ntfs-3g_common.h185
-rwxr-xr-xsrc/secaudit.c673
-rwxr-xr-xsrc/secaudit.h32
-rwxr-xr-xsrc/usermap.c3
-rw-r--r--[-rwxr-xr-x]stamp-h10
193 files changed, 80411 insertions, 44563 deletions
diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c
index 12ea369..1a9ef37 100755
--- a/libntfs-3g/attrib.c
+++ b/libntfs-3g/attrib.c
@@ -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-2013 Jean-Pierre Andre
* Copyright (c) 2010 Erik Larsson
*
* This program/include file is free software; you can redistribute it and/or
@@ -65,9 +65,6 @@
#include "misc.h"
#include "efs.h"
-#define STANDARD_COMPRESSION_UNIT 4
-#define MAX_COMPRESSION_CLUSTER_SIZE 4096
-
ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') };
ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'),
const_cpu_to_le16('S'),
@@ -405,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);
@@ -457,7 +454,8 @@ 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) {
+ if ((type == AT_DATA)
+ && (a->non_resident ? !a->initialized_size : !a->value_length)) {
/*
* Define/redefine the compression state if stream is
* empty, based on the compression mark on parent
@@ -465,17 +463,23 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
* inode (for named data streams). The compression mark
* may change any time, the compression state can only
* change when stream is wiped out.
+ *
+ * Also prevent compression on NTFS version < 3.0
+ * or cluster size > 4K or compression is disabled
*/
a->flags &= ~ATTR_COMPRESSION_MASK;
- if ((na->ni->flags & FILE_ATTR_COMPRESSED)
+ if ((ni->flags & FILE_ATTR_COMPRESSED)
+ && (ni->vol->major_ver >= 3)
+ && NVolCompression(ni->vol)
&& (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE))
a->flags |= ATTR_IS_COMPRESSED;
}
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 "
@@ -586,6 +590,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
@@ -609,6 +689,11 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na)
ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n",
(unsigned long long)na->ni->mft_no, na->type);
+ /* avoid multiple full runlist mappings */
+ if (NAttrFullyMapped(na)) {
+ ret = 0;
+ goto out;
+ }
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
if (!ctx)
goto out;
@@ -682,8 +767,10 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na)
(long long)highest_vcn, (long long)last_vcn);
goto err_out;
}
- if (errno == ENOENT)
+ if (errno == ENOENT) {
+ NAttrSetFullyMapped(na);
ret = 0;
+ }
err_out:
ntfs_attr_put_search_ctx(ctx);
out:
@@ -1105,12 +1192,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;
@@ -1140,9 +1242,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);
@@ -1190,24 +1310,49 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
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.
+ * for a compressed file, be sure to allocate the full
+ * compression block, as we may need space to decompress
+ * existing compressed data.
+ * So allocate the space common to compression block
+ * and existing hole.
*/
- rlc = ntfs_cluster_alloc(vol, (*rl)->vcn, (*rl)->length,
+ VCN alloc_vcn;
+
+ if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn)
+ alloc_vcn = (*rl)->vcn;
+ else
+ alloc_vcn = from_vcn & -na->compression_block_clusters;
+ need = (alloc_vcn | (na->compression_block_clusters - 1))
+ + 1 - alloc_vcn;
+ if (need > (*rl)->length) {
+ ntfs_log_error("Cannot allocate %lld clusters"
+ " within a hole of %lld\n",
+ (long long)need,
+ (long long)(*rl)->length);
+ errno = EIO;
+ goto err_out;
+ }
+ rlc = ntfs_cluster_alloc(vol, alloc_vcn, need,
lcn_seek_from, DATA_ZONE);
} else
rlc = ntfs_cluster_alloc(vol, from_vcn, need,
lcn_seek_from, DATA_ZONE);
if (!rlc)
goto err_out;
+ if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE))
+ na->compressed_size += need << vol->cluster_size_bits;
*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.
+ * For a compressed attribute, we must be sure there are two
+ * available entries, so reserve them before it gets too late.
*/
- if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK))
- *rl = ntfs_rl_extend(*rl,1);
+ if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) {
+ runlist_element *oldrl = na->rl;
+ na->rl = *rl;
+ *rl = ntfs_rl_extend(na,*rl,2);
+ if (!*rl) na->rl = oldrl; /* restore to original if failed */
+ }
if (!*rl) {
eo = errno;
ntfs_log_perror("Failed to merge runlists");
@@ -1218,8 +1363,9 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
errno = eo;
goto err_out;
}
+ na->unused_runs = 2;
na->rl = *rl;
- if (*update_from == -1)
+ if ((*update_from == -1) || (from_vcn < *update_from))
*update_from = from_vcn;
*rl = ntfs_attr_find_vcn(na, cur_vcn);
if (!*rl) {
@@ -1269,6 +1415,330 @@ err_out:
static int stuff_hole(ntfs_attr *na, const s64 pos);
+/*
+ * Split an existing hole for overwriting with data
+ * The hole may have to be split into two or three parts, so
+ * that the overwritten part fits within a single compression block
+ *
+ * No cluster allocation is needed, this will be done later in
+ * standard hole filling, hence no need to reserve runs for
+ * future needs.
+ *
+ * Returns the number of clusters with existing compressed data
+ * in the compression block to be written to
+ * (or the full block, if it was a full hole)
+ * -1 if there were an error
+ */
+
+static int split_compressed_hole(ntfs_attr *na, runlist_element **prl,
+ s64 pos, s64 count, VCN *update_from)
+{
+ int compressed_part;
+ int cluster_size_bits = na->ni->vol->cluster_size_bits;
+ runlist_element *rl = *prl;
+
+ compressed_part
+ = na->compression_block_clusters;
+ /* reserve entries in runlist if we have to split */
+ if (rl->length > na->compression_block_clusters) {
+ *prl = ntfs_rl_extend(na,*prl,2);
+ if (!*prl) {
+ compressed_part = -1;
+ } else {
+ rl = *prl;
+ na->unused_runs = 2;
+ }
+ }
+ if (*prl && (rl->length > na->compression_block_clusters)) {
+ /*
+ * Locate the update part relative to beginning of
+ * current run
+ */
+ int beginwrite = (pos >> cluster_size_bits) - rl->vcn;
+ s32 endblock = (((pos + count - 1) >> cluster_size_bits)
+ | (na->compression_block_clusters - 1)) + 1 - rl->vcn;
+
+ compressed_part = na->compression_block_clusters
+ - (rl->length & (na->compression_block_clusters - 1));
+ if ((beginwrite + compressed_part) >= na->compression_block_clusters)
+ compressed_part = na->compression_block_clusters;
+ /*
+ * if the run ends beyond end of needed block
+ * we have to split the run
+ */
+ if (endblock < rl[0].length) {
+ runlist_element *xrl;
+ int n;
+
+ /*
+ * we have to split into three parts if the run
+ * does not end within the first compression block.
+ * This means the hole begins before the
+ * compression block.
+ */
+ if (endblock > na->compression_block_clusters) {
+ if (na->unused_runs < 2) {
+ntfs_log_error("No free run, case 1\n");
+ }
+ na->unused_runs -= 2;
+ xrl = rl;
+ n = 0;
+ while (xrl->length) {
+ xrl++;
+ n++;
+ }
+ do {
+ xrl[2] = *xrl;
+ xrl--;
+ } while (xrl != rl);
+ rl[1].length = na->compression_block_clusters;
+ rl[2].length = rl[0].length - endblock;
+ rl[0].length = endblock
+ - na->compression_block_clusters;
+ rl[1].lcn = LCN_HOLE;
+ rl[2].lcn = LCN_HOLE;
+ rl[1].vcn = rl[0].vcn + rl[0].length;
+ rl[2].vcn = rl[1].vcn
+ + na->compression_block_clusters;
+ rl = ++(*prl);
+ } else {
+ /*
+ * split into two parts and use the
+ * first one
+ */
+ if (!na->unused_runs) {
+ntfs_log_error("No free run, case 2\n");
+ }
+ na->unused_runs--;
+ xrl = rl;
+ n = 0;
+ while (xrl->length) {
+ xrl++;
+ n++;
+ }
+ do {
+ xrl[1] = *xrl;
+ xrl--;
+ } while (xrl != rl);
+ if (beginwrite < endblock) {
+ /* we will write into the first part of hole */
+ rl[1].length = rl[0].length - endblock;
+ rl[0].length = endblock;
+ rl[1].vcn = rl[0].vcn + rl[0].length;
+ rl[1].lcn = LCN_HOLE;
+ } else {
+ /* we will write into the second part of hole */
+// impossible ?
+ rl[1].length = rl[0].length - endblock;
+ rl[0].length = endblock;
+ rl[1].vcn = rl[0].vcn + rl[0].length;
+ rl[1].lcn = LCN_HOLE;
+ rl = ++(*prl);
+ }
+ }
+ } else {
+ if (rl[1].length) {
+ runlist_element *xrl;
+ int n;
+
+ /*
+ * split into two parts and use the
+ * last one
+ */
+ if (!na->unused_runs) {
+ntfs_log_error("No free run, case 4\n");
+ }
+ na->unused_runs--;
+ xrl = rl;
+ n = 0;
+ while (xrl->length) {
+ xrl++;
+ n++;
+ }
+ do {
+ xrl[1] = *xrl;
+ xrl--;
+ } while (xrl != rl);
+ } else {
+ rl[2].lcn = rl[1].lcn;
+ rl[2].vcn = rl[1].vcn;
+ rl[2].length = rl[1].length;
+ }
+ rl[1].vcn -= na->compression_block_clusters;
+ rl[1].lcn = LCN_HOLE;
+ rl[1].length = na->compression_block_clusters;
+ rl[0].length -= na->compression_block_clusters;
+ if (pos >= (rl[1].vcn << cluster_size_bits)) {
+ rl = ++(*prl);
+ }
+ }
+ if ((*update_from == -1) || ((*prl)->vcn < *update_from))
+ *update_from = (*prl)->vcn;
+ }
+ return (compressed_part);
+}
+
+/*
+ * Borrow space from adjacent hole for appending data
+ * The hole may have to be split so that the end of hole is not
+ * affected by cluster allocation and overwriting
+ * Cluster allocation is needed for the overwritten compression block
+ *
+ * Must always leave two unused entries in the runlist
+ *
+ * Returns the number of clusters with existing compressed data
+ * in the compression block to be written to
+ * -1 if there were an error
+ */
+
+static int borrow_from_hole(ntfs_attr *na, runlist_element **prl,
+ s64 pos, s64 count, VCN *update_from, BOOL wasnonresident)
+{
+ int compressed_part = 0;
+ int cluster_size_bits = na->ni->vol->cluster_size_bits;
+ runlist_element *rl = *prl;
+ s32 endblock;
+ long long allocated;
+ runlist_element *zrl;
+ int irl;
+ BOOL undecided;
+ BOOL nothole;
+
+ /* check whether the compression block is fully allocated */
+ endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn;
+ allocated = 0;
+ zrl = rl;
+ irl = 0;
+ while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) {
+ allocated += zrl->length;
+ zrl++;
+ irl++;
+ }
+
+ undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED);
+ nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE);
+
+ if (undecided || nothole) {
+ runlist_element *orl = na->rl;
+ s64 olcn = (*prl)->lcn;
+#if PARTIAL_RUNLIST_UPDATING
+ VCN prevblock;
+#endif
+ /*
+ * 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 {
+ /*
+ * Mapping the runlist may cause its relocation,
+ * and relocation may be at the same place with
+ * relocated contents.
+ * Have to find the current run again when this
+ * happens.
+ */
+ if ((na->rl != orl) || ((*prl)->lcn != olcn)) {
+ zrl = &na->rl[irl];
+ while (zrl->length && (zrl->lcn != olcn))
+ zrl++;
+ *prl = zrl;
+ }
+ if (!(*prl)->length) {
+ ntfs_log_error("Mapped run not found,"
+ " inode %lld lcn 0x%llx\n",
+ (long long)na->ni->mft_no,
+ (long long)olcn);
+ rl = (runlist_element*)NULL;
+ } else {
+ rl = ntfs_rl_extend(na,*prl,2);
+ na->unused_runs = 2;
+ }
+ }
+ *prl = rl;
+ if (rl && undecided) {
+ allocated = 0;
+ zrl = rl;
+ irl = 0;
+ while (zrl->length && (zrl->lcn >= 0)
+ && (allocated < endblock)) {
+ allocated += zrl->length;
+ zrl++;
+ irl++;
+ }
+ }
+ }
+ /*
+ * compression block not fully allocated and followed
+ * by a hole : we must allocate in the hole.
+ */
+ if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) {
+ s64 xofs;
+
+ /*
+ * split the hole if not fully needed
+ */
+ if ((allocated + zrl->length) > endblock) {
+ runlist_element *xrl;
+
+ *prl = ntfs_rl_extend(na,*prl,1);
+ if (*prl) {
+ /* beware : rl was reallocated */
+ rl = *prl;
+ zrl = &rl[irl];
+ na->unused_runs = 0;
+ xrl = zrl;
+ while (xrl->length) xrl++;
+ do {
+ xrl[1] = *xrl;
+ } while (xrl-- != zrl);
+ zrl->length = endblock - allocated;
+ zrl[1].length -= zrl->length;
+ zrl[1].vcn = zrl->vcn + zrl->length;
+ }
+ }
+ if (*prl) {
+ if (wasnonresident)
+ compressed_part = na->compression_block_clusters
+ - zrl->length;
+ xofs = 0;
+ if (ntfs_attr_fill_hole(na,
+ zrl->length << cluster_size_bits,
+ &xofs, &zrl, update_from))
+ compressed_part = -1;
+ else {
+ /* go back to initial cluster, now reallocated */
+ while (zrl->vcn > (pos >> cluster_size_bits))
+ zrl--;
+ *prl = zrl;
+ }
+ }
+ }
+ if (!*prl) {
+ ntfs_log_error("No elements to borrow from a hole\n");
+ compressed_part = -1;
+ } else
+ if ((*update_from == -1) || ((*prl)->vcn < *update_from))
+ *update_from = (*prl)->vcn;
+ 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
@@ -1304,9 +1774,9 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
unsigned int undo_initialized_size : 1;
unsigned int undo_data_size : 1;
} need_to = { 0, 0 };
- BOOL makingnonresident = FALSE;
BOOL wasnonresident = FALSE;
BOOL compressed;
+ BOOL updatemap;
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,
@@ -1320,6 +1790,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
vol = na->ni->vol;
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
+ na->unused_runs = 0; /* prepare overflow checks */
/*
* Encrypted attributes are only supported in raw mode. We return
* access denied, which is what Windows NT4 does, too.
@@ -1341,23 +1812,14 @@ 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. */
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.
+ * Compression is restricted to data streams and
+ * 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)
+ != ATTR_IS_COMPRESSED))) {
errno = EOPNOTSUPP;
goto errno_set;
}
@@ -1368,11 +1830,39 @@ 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;
+ }
+ /*
+ * If we avoided updating the runlist, we must be sure
+ * to cancel the enlargement and put back the runlist to
+ * a clean state if we get into some error.
+ */
+ if (NAttrDataAppending(na))
+ need_to.undo_data_size = 1;
+#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);
@@ -1387,7 +1877,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
* so truncate the requested count if needed (big buffers).
*/
if (compressed) {
- fullcount = na->data_size - pos;
+ fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos;
if (count > fullcount)
count = fullcount;
}
@@ -1431,9 +1921,31 @@ 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,
@@ -1441,9 +1953,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
* before it gets too late.
*/
if (compressed) {
- na->rl = ntfs_rl_extend(na->rl,2);
+ na->rl = ntfs_rl_extend(na,na->rl,2);
if (!na->rl)
goto err_out;
+ na->unused_runs = 2;
}
/* Set initialized_size to @pos + @count. */
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
@@ -1461,8 +1974,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
ctx->attr->initialized_size = cpu_to_sle64(pos + count);
/* fix data_size for compressed files */
- if (compressed)
+ if (compressed) {
+ na->data_size = pos + count;
ctx->attr->data_size = ctx->attr->initialized_size;
+ }
if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
ctx->mrec)) {
/*
@@ -1481,7 +1996,11 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
: na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
- na->ni->allocated_size = na->allocated_size;
+ if ((compressed || NAttrSparse(na))
+ && NAttrNonResident(na))
+ na->ni->allocated_size = na->compressed_size;
+ else
+ na->ni->allocated_size = na->allocated_size;
set_nino_flag(na->ni,KnownSize);
}
#endif
@@ -1509,7 +2028,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
}
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).
@@ -1530,49 +2048,51 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
if ((rl->lcn == (LCN)LCN_HOLE)
&& wasnonresident) {
if (rl->length < na->compression_block_clusters)
+ /*
+ * the needed block is in a hole smaller
+ * than the compression block : we can use
+ * it fully
+ */
compressed_part
= na->compression_block_clusters
- rl->length;
else {
- compressed_part
- = na->compression_block_clusters;
- if (rl->length > na->compression_block_clusters) {
- rl[2].lcn = rl[1].lcn;
- rl[2].vcn = rl[1].vcn;
- rl[2].length = rl[1].length;
- rl[1].vcn -= compressed_part;
- rl[1].lcn = LCN_HOLE;
- rl[1].length = compressed_part;
- rl[0].length -= compressed_part;
- ofs -= rl->length << vol->cluster_size_bits;
- rl++;
- }
+ /*
+ * the needed block is in a hole bigger
+ * than the compression block : we must
+ * split the hole and use it partially
+ */
+ compressed_part = split_compressed_hole(na,
+ &rl, pos, count, &update_from);
}
- /* normal hole filling will do later */
- } else
- 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 >= 0) {
+ /*
+ * the needed block contains data, make
+ * sure the full compression block is
+ * allocated. Borrow from hole if needed
+ */
+ compressed_part = borrow_from_hole(na,
+ &rl, pos, count, &update_from,
+ wasnonresident);
}
+ }
+
+ if (compressed_part < 0)
+ goto err_out;
+
+ /* just making non-resident, so not yet compressed */
+ if (NAttrBeingNonResident(na)
+ && (compressed_part < na->compression_block_clusters))
+ compressed_part = 0;
}
+ ofs = pos - (rl->vcn << vol->cluster_size_bits);
/*
* 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.
*/
- for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) {
+ for (hole_end = 0; count; rl++, ofs = 0) {
if (rl->lcn == LCN_RL_NOT_MAPPED) {
rl = ntfs_attr_find_vcn(na, rl->vcn);
if (!rl) {
@@ -1653,7 +2173,8 @@ retry:
if (compressed) {
written = ntfs_compressed_pwrite(na,
rl, wpos, ofs, to_write,
- rounding, b, compressed_part);
+ rounding, cb, compressed_part,
+ &update_from);
} else {
written = ntfs_pwrite(vol->dev, wpos,
rounding, cb);
@@ -1666,7 +2187,8 @@ retry:
if (compressed) {
written = ntfs_compressed_pwrite(na,
rl, wpos, ofs, to_write,
- to_write, b, compressed_part);
+ to_write, b, compressed_part,
+ &update_from);
} else
written = ntfs_pwrite(vol->dev, wpos,
to_write, b);
@@ -1694,10 +2216,21 @@ retry:
done:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
- /* Update mapping pairs if needed. */
- if ((update_from != -1)
- || (compressed && !makingnonresident))
- if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) {
+ /*
+ * Update mapping pairs if needed.
+ * For a compressed file, we try to make a partial update
+ * 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);
+#endif
+ if (updatemap) {
+ if (ntfs_attr_update_mapping_pairs(na,
+ (update_from < 0 ? 0 : update_from))) {
/*
* FIXME: trying to recover by goto rl_err_out;
* could cause driver hang by infinite looping.
@@ -1705,6 +2238,10 @@ done:
total = -1;
goto out;
}
+ if (!wasnonresident)
+ NAttrClearBeingNonResident(na);
+ NAttrClearDataAppending(na);
+ }
out:
ntfs_log_leave("\n");
return total;
@@ -1761,10 +2298,13 @@ err_out:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
/* Update mapping pairs if needed. */
- if (update_from != -1)
- ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/);
+ updatemap = (compressed
+ ? NAttrFullyMapped(na) != 0 : update_from != -1);
+ 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:
@@ -1782,7 +2322,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;
@@ -1795,6 +2334,7 @@ int ntfs_attr_pclose(ntfs_attr *na)
goto errno_set;
}
vol = na->ni->vol;
+ na->unused_runs = 0;
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
!= const_cpu_to_le16(0);
/*
@@ -1810,15 +2350,25 @@ int ntfs_attr_pclose(ntfs_attr *na)
if (!compressed || !NAttrNonResident(na))
goto out;
+ /* safety check : no recursion on close */
+ if (NAttrComprClosing(na)) {
+ errno = EIO;
+ ntfs_log_error("Bad ntfs_attr_pclose"
+ " recursion on inode %lld\n",
+ (long long)na->ni->mft_no);
+ goto out;
+ }
+ NAttrSetComprClosing(na);
/*
- * For a compressed attribute, we must be sure there is an
- * available entry, so reserve it before it gets too late.
+ * For a compressed attribute, we must be sure there are two
+ * available entries, so reserve them before it gets too late.
*/
if (ntfs_attr_map_whole_runlist(na))
goto err_out;
- na->rl = ntfs_rl_extend(na->rl,1);
+ na->rl = ntfs_rl_extend(na,na->rl,2);
if (!na->rl)
goto err_out;
+ na->unused_runs = 2;
/* Find the runlist element containing the terminal vcn. */
rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits);
if (!rl) {
@@ -1839,10 +2389,16 @@ int ntfs_attr_pclose(ntfs_attr *na)
* 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 >= 0) {
+ runlist_element *xrl;
+
+ xrl = rl;
+ do {
+ xrl++;
+ } while (xrl->lcn >= 0);
+ compressed_part = (-xrl->length)
+ & (na->compression_block_clusters - 1);
+ } else
if (rl->lcn == (LCN)LCN_HOLE) {
if (rl->length < na->compression_block_clusters)
compressed_part
@@ -1877,7 +2433,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)",
@@ -1897,14 +2452,15 @@ int ntfs_attr_pclose(ntfs_attr *na)
retry:
failed = 0;
+ if (update_from < 0) update_from = 0;
if (!NVolReadOnly(vol)) {
- failed = ntfs_compressed_close(na, rl, ofs);
+ failed = ntfs_compressed_close(na, rl, ofs, &update_from);
#if CACHE_NIDATA_SIZE
if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY
? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
: na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
- na->ni->allocated_size = na->allocated_size;
+ na->ni->allocated_size = na->compressed_size;
set_nino_flag(na->ni,KnownSize);
}
#endif
@@ -1919,7 +2475,8 @@ retry:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
/* Update mapping pairs if needed. */
- if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) {
+ if (NAttrFullyMapped(na))
+ if (ntfs_attr_update_mapping_pairs(na, update_from)) {
/*
* FIXME: trying to recover by goto rl_err_out;
* could cause driver hang by infinite looping.
@@ -1928,6 +2485,7 @@ retry:
goto out;
}
out:
+ NAttrClearComprClosing(na);
ntfs_log_leave("\n");
return (!ok);
rl_err_out:
@@ -1942,7 +2500,8 @@ err_out:
if (ctx)
ntfs_attr_put_search_ctx(ctx);
/* Update mapping pairs if needed. */
- ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/);
+ if (NAttrFullyMapped(na))
+ ntfs_attr_update_mapping_pairs(na, 0);
errno = eo;
errno_set:
ok = FALSE;
@@ -1983,6 +2542,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,
@@ -1996,9 +2556,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;
}
@@ -2996,6 +3558,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))) {
@@ -3170,8 +3740,8 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size)
* EIO - I/O error occurred or damaged filesystem.
*/
int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
- ntfschar *name, u8 name_len, u8 *val, u32 size,
- ATTR_FLAGS data_flags)
+ const ntfschar *name, u8 name_len, const u8 *val,
+ u32 size, ATTR_FLAGS data_flags)
{
ntfs_attr_search_ctx *ctx;
u32 length;
@@ -3302,7 +3872,7 @@ put_err_out:
* EIO - I/O error occurred or damaged filesystem.
*/
int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
- ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
+ const ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
ATTR_FLAGS flags)
{
ntfs_attr_search_ctx *ctx;
@@ -3585,7 +4155,7 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx)
* On success return 0. On error return -1 with errno set to the error code.
*/
int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
- ntfschar *name, u8 name_len, u8 *val, s64 size)
+ ntfschar *name, u8 name_len, const u8 *val, s64 size)
{
u32 attr_rec_size;
int err, i, offset;
@@ -3704,6 +4274,8 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
add_attr_record:
if ((ni->flags & FILE_ATTR_COMPRESSED)
+ && (ni->vol->major_ver >= 3)
+ && NVolCompression(ni->vol)
&& (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)
&& ((type == AT_DATA)
|| ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30))))
@@ -3746,7 +4318,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");
@@ -3778,8 +4350,8 @@ err_out:
* 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)
+int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, const ntfschar *name,
+ u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask)
{
ntfs_attr_search_ctx *ctx;
int res;
@@ -4189,6 +4761,13 @@ int ntfs_attr_make_non_resident(ntfs_attr *na,
- 1) & ~(vol->cluster_size - 1);
if (new_allocated_size > 0) {
+ if ((a->flags & ATTR_COMPRESSION_MASK)
+ == ATTR_IS_COMPRESSED) {
+ /* must allocate full compression blocks */
+ new_allocated_size = ((new_allocated_size - 1)
+ | ((1L << (STANDARD_COMPRESSION_UNIT
+ + vol->cluster_size_bits)) - 1)) + 1;
+ }
/* Start by allocating clusters to hold the attribute value. */
rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >>
vol->cluster_size_bits, -1, DATA_ZONE);
@@ -4201,6 +4780,7 @@ int ntfs_attr_make_non_resident(ntfs_attr *na,
* we can use ntfs_attr_pwrite().
*/
NAttrSetNonResident(na);
+ NAttrSetBeingNonResident(na);
na->rl = rl;
na->allocated_size = new_allocated_size;
na->data_size = na->initialized_size = le32_to_cpu(a->value_length);
@@ -4319,6 +4899,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);
@@ -4335,6 +4916,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize);
* @newsize: new size (in bytes) to which to resize the attribute
*
* Change the size of a resident, open ntfs attribute @na to @newsize bytes.
+ * Can also be used to force an attribute non-resident. In this case, the
+ * size cannot be changed.
*
* On success return 0
* On error return values are:
@@ -4345,7 +4928,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize);
* 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_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
+static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize,
+ hole_type holes)
{
ntfs_attr_search_ctx *ctx;
ntfs_volume *vol;
@@ -4383,7 +4967,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
* attribute non-resident if the attribute type supports it. If it is
* smaller we can go ahead and attempt the resize.
*/
- if (newsize < vol->mft_record_size) {
+ if ((newsize < vol->mft_record_size) && (holes != HOLES_NONRES)) {
/* Perform the resize of the attribute record. */
if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
newsize))) {
@@ -4397,7 +4981,14 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30
: na->type == AT_DATA && na->name == AT_UNNAMED) {
na->ni->data_size = na->data_size;
- na->ni->allocated_size = na->allocated_size;
+ if (((na->data_flags & ATTR_COMPRESSION_MASK)
+ || NAttrSparse(na))
+ && NAttrNonResident(na))
+ na->ni->allocated_size
+ = na->compressed_size;
+ else
+ na->ni->allocated_size
+ = na->allocated_size;
set_nino_flag(na->ni,KnownSize);
if (na->type == AT_DATA)
NInoFileNameSetDirty(na->ni);
@@ -4416,8 +5007,23 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
if (!ntfs_attr_make_non_resident(na, ctx)) {
ntfs_inode_mark_dirty(ctx->ntfs_ino);
ntfs_attr_put_search_ctx(ctx);
+ /*
+ * do not truncate when forcing non-resident, this
+ * could cause the attribute to be made resident again,
+ * so size changes are not allowed.
+ */
+ if (holes == HOLES_NONRES) {
+ ret = 0;
+ if (newsize != na->data_size) {
+ ntfs_log_error("Cannot change size when"
+ " forcing non-resident\n");
+ errno = EIO;
+ ret = STATUS_ERROR;
+ }
+ return (ret);
+ }
/* Resize non-resident attribute */
- return ntfs_attr_truncate(na, newsize);
+ return ntfs_attr_truncate_i(na, newsize, holes);
} else if (errno != ENOSPC && errno != EPERM) {
err = errno;
ntfs_log_perror("Failed to make attribute non-resident");
@@ -4454,10 +5060,26 @@ 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)) {
+ err = errno;
+ ntfs_attr_close(tna);
+ goto put_err_out;
+ }
ntfs_inode_mark_dirty(tna->ni);
ntfs_attr_close(tna);
ntfs_attr_put_search_ctx(ctx);
- return ntfs_resident_attr_resize(na, newsize);
+ return ntfs_resident_attr_resize_i(na, newsize, holes);
}
/* Check whether error occurred. */
if (errno != ENOENT) {
@@ -4477,7 +5099,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_log_perror("Could not free space in MFT record");
return -1;
}
- return ntfs_resident_attr_resize(na, newsize);
+ return ntfs_resident_attr_resize_i(na, newsize, holes);
}
/*
@@ -4516,7 +5138,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_attr_put_search_ctx(ctx);
if (ntfs_inode_add_attrlist(ni))
return -1;
- return ntfs_resident_attr_resize(na, newsize);
+ return ntfs_resident_attr_resize_i(na, newsize, holes);
}
/* Allocate new mft record. */
ni = ntfs_mft_record_alloc(vol, ni);
@@ -4537,7 +5159,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
ntfs_attr_put_search_ctx(ctx);
/* Try to perform resize once again. */
- return ntfs_resident_attr_resize(na, newsize);
+ return ntfs_resident_attr_resize_i(na, newsize, holes);
resize_done:
/*
@@ -4558,11 +5180,39 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize)
int ret;
ntfs_log_enter("Entering\n");
- ret = ntfs_resident_attr_resize_i(na, newsize);
+ ret = ntfs_resident_attr_resize_i(na, newsize, HOLES_OK);
ntfs_log_leave("\n");
return ret;
}
+/*
+ * Force an attribute to be made non-resident without
+ * changing its size.
+ *
+ * This is particularly needed when the attribute has no data,
+ * as the non-resident variant requires more space in the MFT
+ * record, and may imply expelling some other attribute.
+ *
+ * As a consequence the existing ntfs_attr_search_ctx's have to
+ * be closed or reinitialized.
+ *
+ * returns 0 if successful,
+ * < 0 if failed, with errno telling why
+ */
+
+int ntfs_attr_force_non_resident(ntfs_attr *na)
+{
+ int res;
+
+ res = ntfs_resident_attr_resize_i(na, na->data_size, HOLES_NONRES);
+ if (!res && !NAttrNonResident(na)) {
+ res = -1;
+ errno = EIO;
+ ntfs_log_error("Failed to force non-resident\n");
+ }
+ return (res);
+}
+
/**
* ntfs_attr_make_resident - convert a non-resident to a resident attribute
* @na: open ntfs attribute to make resident
@@ -4673,6 +5323,8 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
*/
if (!na->data_size
&& (na->type == AT_DATA)
+ && (na->ni->vol->major_ver >= 3)
+ && NVolCompression(na->ni->vol)
&& (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)
&& (na->ni->flags & FILE_ATTR_COMPRESSED)) {
a->flags |= ATTR_IS_COMPRESSED;
@@ -4719,7 +5371,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");
@@ -4731,6 +5382,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;
@@ -4745,7 +5397,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;
@@ -4757,15 +5409,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
@@ -4798,6 +5456,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. */
@@ -4817,6 +5476,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,
@@ -4831,7 +5491,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);
@@ -4846,7 +5507,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
* allocated size in the index.
*/
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
- if (sparse)
+ if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK))
na->ni->allocated_size = na->compressed_size;
else
na->ni->allocated_size = na->allocated_size;
@@ -4863,7 +5524,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;
@@ -4873,9 +5535,10 @@ static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn)
const runlist_element *stop_rl;
int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1;
BOOL finished_build;
+ BOOL first_updated = FALSE;
retry:
- if (!na || !na->rl || from_vcn) {
+ if (!na || !na->rl) {
errno = EINVAL;
ntfs_log_perror("%s: na=%p", __FUNCTION__, na);
return -1;
@@ -4890,6 +5553,67 @@ 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 = 0;
+ runlist_element *xrl;
+
+ /*
+ * If attribute was not sparse, we only
+ * have to check whether there is a hole
+ * in the updated region.
+ */
+ for (xrl = na->rl; xrl->length; xrl++) {
+ if (xrl->lcn < 0) {
+ if (xrl->lcn == LCN_HOLE) {
+ sparse = 1;
+ break;
+ }
+ if (xrl->lcn != LCN_RL_NOT_MAPPED) {
+ sparse = -1;
+ break;
+ }
+ }
+ }
+ 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
@@ -4907,6 +5631,8 @@ retry:
CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) {
a = ctx->attr;
m = ctx->mrec;
+ if (!a->lowest_vcn)
+ first_updated = TRUE;
/*
* If runlist is updating not from the beginning, then set
* @stop_vcn properly, i.e. to the lowest vcn of record that
@@ -4949,7 +5675,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;
@@ -5056,6 +5782,34 @@ retry:
ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__);
goto put_err_out;
}
+ /*
+ * If the base extent was skipped in the above process,
+ * we still may have to update the sizes.
+ */
+ if (!first_updated) {
+ le16 spcomp;
+
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (!ntfs_attr_lookup(na->type, na->name, na->name_len,
+ CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ a = ctx->attr;
+ a->allocated_size = cpu_to_sle64(na->allocated_size);
+ spcomp = na->data_flags
+ & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
+ if (spcomp)
+ a->compressed_size = cpu_to_sle64(na->compressed_size);
+ if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) {
+ na->ni->allocated_size
+ = (spcomp
+ ? na->compressed_size
+ : na->allocated_size);
+ NInoFileNameSetDirty(na->ni);
+ }
+ } else {
+ ntfs_log_error("Failed to update sizes in base extent\n");
+ goto put_err_out;
+ }
+ }
/* Deallocate not used attribute extents and return with success. */
if (finished_build) {
@@ -5189,7 +5943,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;
}
@@ -5233,8 +5987,19 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize)
}
/* The first cluster outside the new allocation. */
- first_free_vcn = (newsize + vol->cluster_size - 1) >>
- vol->cluster_size_bits;
+ if (na->data_flags & ATTR_COMPRESSION_MASK)
+ /*
+ * For compressed files we must keep full compressions blocks,
+ * but currently we do not decompress/recompress the last
+ * block to truncate the data, so we may leave more allocated
+ * clusters than really needed.
+ */
+ first_free_vcn = (((newsize - 1)
+ | (na->compression_block_size - 1)) + 1)
+ >> vol->cluster_size_bits;
+ else
+ first_free_vcn = (newsize + vol->cluster_size - 1) >>
+ vol->cluster_size_bits;
/*
* Compare the new allocation with the old one and only deallocate
* clusters if there is a change.
@@ -5348,7 +6113,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;
@@ -5375,6 +6141,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. */
@@ -5385,16 +6153,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;
@@ -5463,9 +6248,20 @@ 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;
+#if PARTIAL_RUNLIST_UPDATING
+ /*
+ * Write mapping pairs for new runlist, unless this is
+ * a temporary state before appending data.
+ * If the update is not done, we must be sure to do
+ * it later, and to get to a clean state even on errors.
+ */
+ if ((holes != HOLES_DELAY)
+ && ntfs_attr_update_mapping_pairs_i(na, start_update,
+ holes)) {
+#else
/* Write mapping pairs for new runlist. */
- if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >>
- vol->cluster_size_bits*/)) {
+ if (ntfs_attr_update_mapping_pairs(na, 0)) {
+#endif
err = errno;
ntfs_log_perror("Mapping pairs update failed");
goto rollback;
@@ -5551,12 +6347,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;
}
@@ -5565,6 +6362,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
@@ -5581,7 +6379,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;
@@ -5623,8 +6422,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
!= const_cpu_to_le16(0);
if (compressed
&& NAttrNonResident(na)
- && (((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)
- || (newsize && (newsize < na->data_size)))) {
+ && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) {
errno = EOPNOTSUPP;
ntfs_log_perror("Failed to truncate compressed attribute");
goto out;
@@ -5632,31 +6430,55 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
if (NAttrNonResident(na)) {
/*
* For compressed data, the last block must be fully
- * allocated, and we do not known the size of compression
+ * allocated, and we do not know 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 (from where we are about to write),
* so we silently do not allocate more.
*
- * Note : do not request truncate on compressed files
+ * Note : do not request upsizing of compressed files
* unless being able to face the consequences !
*/
- if (compressed && newsize)
+ if (compressed && newsize && (newsize > na->data_size))
fullsize = (na->initialized_size
| (na->compression_block_size - 1)) + 1;
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
- ret = ntfs_resident_attr_resize(na, newsize);
+ ret = ntfs_resident_attr_resize_i(na, newsize, holes);
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
*
@@ -5717,7 +6539,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
@@ -5774,7 +6596,8 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
na = ntfs_attr_open(ni, type, name, name_len);
if (!na) {
- ntfs_log_perror("ntfs_attr_open failed");
+ ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx",
+ (long long)ni->mft_no,(long)le32_to_cpu(type));
goto err_exit;
}
data = ntfs_malloc(na->data_size);
@@ -5797,9 +6620,91 @@ 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, const 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,
+int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, const ntfschar *name,
u32 name_len)
{
ntfs_attr_search_ctx *ctx;