Allowed creating holes in compressed files
parent
b725b77e88
commit
88473752c5
|
@ -1249,6 +1249,8 @@ err_out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int stuff_hole(ntfs_attr *na, const s64 pos);
|
||||
|
||||
/**
|
||||
* ntfs_attr_pwrite - positioned write to an ntfs attribute
|
||||
* @na: ntfs attribute to write to
|
||||
|
@ -1310,6 +1312,15 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
errno = EACCES;
|
||||
goto errno_set;
|
||||
}
|
||||
/*
|
||||
* Fill the gap, when writing beyond the end of a compressed
|
||||
* file. This will make recursive calls
|
||||
*/
|
||||
if (compressed
|
||||
&& (na->type == AT_DATA)
|
||||
&& (pos > na->initialized_size)
|
||||
&& stuff_hole(na,pos))
|
||||
goto errno_set;
|
||||
/* If this is a compressed attribute it needs special treatment. */
|
||||
wasnonresident = NAttrNonResident(na) != 0;
|
||||
makingnonresident = wasnonresident /* yes : already changed */
|
||||
|
@ -1407,10 +1418,12 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
goto err_out;
|
||||
/*
|
||||
* For a compressed attribute, we must be sure there is an
|
||||
* available entry, so reserve it before it gets too late.
|
||||
* available entry, and, when reopening a compressed file,
|
||||
* we may need to split a hole. So reserve the entries
|
||||
* before it gets too late.
|
||||
*/
|
||||
if (compressed) {
|
||||
na->rl = ntfs_rl_extend(na->rl,1);
|
||||
na->rl = ntfs_rl_extend(na->rl,2);
|
||||
if (!na->rl)
|
||||
goto err_out;
|
||||
}
|
||||
|
@ -1487,29 +1500,45 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
*/
|
||||
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))
|
||||
if ((rl->lcn == (LCN)LCN_HOLE)
|
||||
&& wasnonresident) {
|
||||
if (rl->length < na->compression_block_clusters)
|
||||
compressed_part
|
||||
= na->compression_block_clusters
|
||||
- rl->length;
|
||||
= 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++;
|
||||
}
|
||||
}
|
||||
/* 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--;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Scatter the data from the linear data buffer to the volume. Note, a
|
||||
|
@ -1785,11 +1814,15 @@ int ntfs_attr_pclose(ntfs_attr *na)
|
|||
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;
|
||||
if (rl->lcn == (LCN)LCN_HOLE) {
|
||||
if (rl->length < na->compression_block_clusters)
|
||||
compressed_part
|
||||
= na->compression_block_clusters
|
||||
- rl->length;
|
||||
else
|
||||
compressed_part
|
||||
= na->compression_block_clusters;
|
||||
}
|
||||
/* done, if the last block set was compressed */
|
||||
if (compressed_part)
|
||||
goto out;
|
||||
|
@ -5552,13 +5585,14 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
|
|||
* 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.
|
||||
* 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
|
||||
* unless being able to face the consequences !
|
||||
*/
|
||||
if (compressed && newsize)
|
||||
fullsize = (na->data_size
|
||||
fullsize = (na->initialized_size
|
||||
| (na->compression_block_size - 1)) + 1;
|
||||
else
|
||||
fullsize = newsize;
|
||||
|
@ -5573,6 +5607,93 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff a hole in a compressed file
|
||||
*
|
||||
* An unallocated hole must be aligned on compression block size.
|
||||
* If needed current block and target block are stuffed with zeroes.
|
||||
*
|
||||
* Returns 0 if succeeded,
|
||||
* -1 if it failed (as explained in errno)
|
||||
*/
|
||||
|
||||
static int stuff_hole(ntfs_attr *na, const s64 pos)
|
||||
{
|
||||
s64 size;
|
||||
s64 begin_size;
|
||||
s64 end_size;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
/*
|
||||
* If the attribute is resident, the compression block size
|
||||
* is not defined yet and we can make no decision.
|
||||
* So we first try resizing to the target and if the
|
||||
* attribute is still resident, we're done
|
||||
*/
|
||||
if (!NAttrNonResident(na)) {
|
||||
ret = ntfs_resident_attr_resize(na, pos);
|
||||
if (!ret && !NAttrNonResident(na))
|
||||
na->initialized_size = na->data_size = pos;
|
||||
}
|
||||
if (!ret && NAttrNonResident(na)) {
|
||||
/* does the hole span over several compression block ? */
|
||||
if ((pos ^ na->initialized_size)
|
||||
& ~(na->compression_block_size - 1)) {
|
||||
begin_size = ((na->initialized_size - 1)
|
||||
| (na->compression_block_size - 1))
|
||||
+ 1 - na->initialized_size;
|
||||
end_size = pos & (na->compression_block_size - 1);
|
||||
size = (begin_size > end_size ? begin_size : end_size);
|
||||
} else {
|
||||
/* short stuffing in a single compression block */
|
||||
begin_size = size = pos - na->initialized_size;
|
||||
end_size = 0;
|
||||
}
|
||||
if (size)
|
||||
buf = (char*)ntfs_malloc(size);
|
||||
else
|
||||
buf = (char*)NULL;
|
||||
if (buf || !size) {
|
||||
memset(buf,0,size);
|
||||
/* stuff into current block */
|
||||
if (begin_size
|
||||
&& (ntfs_attr_pwrite(na,
|
||||
na->initialized_size, begin_size, buf)
|
||||
!= begin_size))
|
||||
ret = -1;
|
||||
/* create an unstuffed hole */
|
||||
if (!ret
|
||||
&& ((na->initialized_size + end_size) < pos)
|
||||
&& ntfs_non_resident_attr_expand(na,
|
||||
pos - end_size))
|
||||
ret = -1;
|
||||
else
|
||||
na->initialized_size
|
||||
= na->data_size = pos - end_size;
|
||||
/* stuff into the target block */
|
||||
if (!ret && end_size
|
||||
&& (ntfs_attr_pwrite(na,
|
||||
na->initialized_size, end_size, buf)
|
||||
!= end_size))
|
||||
ret = -1;
|
||||
if (buf)
|
||||
free(buf);
|
||||
} else
|
||||
ret = -1;
|
||||
}
|
||||
/* make absolutely sure we have reached the target */
|
||||
if (!ret && (na->initialized_size != pos)) {
|
||||
ntfs_log_error("Failed to stuff a compressed file"
|
||||
"target %lld reached %lld\n",
|
||||
(long long)pos, (long long)na->initialized_size);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attr_readall - read the entire data from an ntfs attribute
|
||||
* @ni: open ntfs inode in which the ntfs attribute resides
|
||||
|
|
|
@ -839,11 +839,12 @@ do_next_cb:
|
|||
* Returns the amount of data read
|
||||
*/
|
||||
|
||||
static int read_clusters(ntfs_volume *vol, const runlist_element *rl,
|
||||
s64 offs, int to_read, char *inbuf)
|
||||
static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl,
|
||||
s64 offs, u32 to_read, char *inbuf)
|
||||
{
|
||||
int count;
|
||||
int got, xgot;
|
||||
u32 count;
|
||||
int xgot;
|
||||
u32 got;
|
||||
s64 xpos;
|
||||
BOOL first;
|
||||
char *xinbuf;
|
||||
|
@ -863,14 +864,14 @@ static int read_clusters(ntfs_volume *vol, const runlist_element *rl,
|
|||
if ((to_read - got) < count)
|
||||
count = to_read - got;
|
||||
xgot = ntfs_pread(vol->dev, xpos, count, xinbuf);
|
||||
if (xgot == count) {
|
||||
if (xgot == (int)count) {
|
||||
got += count;
|
||||
xpos += count;
|
||||
xinbuf += count;
|
||||
xrl++;
|
||||
}
|
||||
first = FALSE;
|
||||
} while ((xgot == count) && (got < to_read));
|
||||
} while ((xgot == (int)count) && (got < to_read));
|
||||
return (got);
|
||||
}
|
||||
|
||||
|
@ -920,8 +921,9 @@ static int write_clusters(ntfs_volume *vol, const runlist_element *rl,
|
|||
* 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)
|
||||
* or 0 if all zeroes (nothing is written)
|
||||
* or -1 if could not compress (nothing is written)
|
||||
* or -2 if there were an irrecoverable error (errno set)
|
||||
*/
|
||||
|
||||
static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
|
||||
|
@ -929,6 +931,7 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
|
|||
{
|
||||
ntfs_volume *vol;
|
||||
char *outbuf;
|
||||
char *pbuf;
|
||||
unsigned int compsz;
|
||||
int written;
|
||||
int rounded;
|
||||
|
@ -937,9 +940,16 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
|
|||
unsigned int sz;
|
||||
unsigned int bsz;
|
||||
BOOL fail;
|
||||
BOOL allzeroes;
|
||||
/* a single compressed zero */
|
||||
static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ;
|
||||
/* a couple of compressed zeroes */
|
||||
static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ;
|
||||
/* more compressed zeroes, to be followed by some count */
|
||||
static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ;
|
||||
|
||||
vol = na->ni->vol;
|
||||
written = 0; /* default return */
|
||||
written = -1; /* 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
|
||||
|
@ -948,21 +958,43 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
|
|||
if (outbuf) {
|
||||
fail = FALSE;
|
||||
compsz = 0;
|
||||
allzeroes = TRUE;
|
||||
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]);
|
||||
pbuf = &outbuf[compsz];
|
||||
sz = ntfs_compress_block(&inbuf[p],bsz,pbuf);
|
||||
/* fail if all the clusters (or more) are needed */
|
||||
if (!sz || ((compsz + sz + clsz + 2)
|
||||
> na->compression_block_size))
|
||||
fail = TRUE;
|
||||
else
|
||||
compsz += sz;
|
||||
else {
|
||||
if (allzeroes) {
|
||||
/* check whether this is all zeroes */
|
||||
switch (sz) {
|
||||
case 4 :
|
||||
allzeroes = !memcmp(
|
||||
pbuf,onezero,4);
|
||||
break;
|
||||
case 5 :
|
||||
allzeroes = !memcmp(
|
||||
pbuf,twozeroes,5);
|
||||
break;
|
||||
case 6 :
|
||||
allzeroes = !memcmp(
|
||||
pbuf,morezeroes,4);
|
||||
break;
|
||||
default :
|
||||
allzeroes = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
compsz += sz;
|
||||
}
|
||||
}
|
||||
if (!fail) {
|
||||
if (!fail && !allzeroes) {
|
||||
/* add a couple of null bytes, space has been checked */
|
||||
outbuf[compsz++] = 0;
|
||||
outbuf[compsz++] = 0;
|
||||
|
@ -973,9 +1005,11 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
|
|||
// previously written text has been spoilt, should return a specific error
|
||||
ntfs_log_error("error writing compressed data\n");
|
||||
errno = EIO;
|
||||
written = -1;
|
||||
written = -2;
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (!fail)
|
||||
written = 0;
|
||||
free(outbuf);
|
||||
}
|
||||
return (written);
|
||||
|
@ -1001,6 +1035,8 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
|
|||
s64 freelcn;
|
||||
s64 freevcn;
|
||||
int freelength;
|
||||
BOOL mergeholes;
|
||||
BOOL beginhole;
|
||||
ntfs_volume *vol;
|
||||
runlist_element *freerl;
|
||||
|
||||
|
@ -1024,11 +1060,21 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
|
|||
freevcn = rl->vcn + usedcnt;
|
||||
freelength = rl->length - usedcnt;
|
||||
/* new count of allocated clusters */
|
||||
rl->length = usedcnt;
|
||||
freerl = ++rl;
|
||||
rl->length = usedcnt; /* warning : can be zero */
|
||||
if (!((freevcn + freecnt)
|
||||
& (na->compression_block_clusters - 1))) {
|
||||
if (freelength > 0) {
|
||||
beginhole = !usedcnt && !rl->vcn;
|
||||
mergeholes = !usedcnt
|
||||
&& rl[0].vcn
|
||||
&& (rl[-1].lcn == LCN_HOLE);
|
||||
if (mergeholes) {
|
||||
freerl = rl;
|
||||
freerl->length = freecnt;
|
||||
} else
|
||||
freerl = ++rl;
|
||||
if ((freelength > 0)
|
||||
&& !mergeholes
|
||||
&& (usedcnt || beginhole)) {
|
||||
/*
|
||||
* move the unused part to the end. Doing so,
|
||||
* the vcn will be out of order. This does
|
||||
|
@ -1050,10 +1096,18 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
|
|||
/* 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;
|
||||
if (mergeholes) {
|
||||
/* merge with adjacent hole */
|
||||
freerl--;
|
||||
freerl->length += freecnt;
|
||||
} else {
|
||||
if (beginhole)
|
||||
freerl--;
|
||||
/* 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;
|
||||
|
@ -1076,26 +1130,34 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
|
|||
*/
|
||||
|
||||
static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
|
||||
s64 offs, int compsz, int pos,
|
||||
s64 offs, u32 compsz, int pos,
|
||||
char *outbuf, s64 to_write, const void *b)
|
||||
{
|
||||
int fail = 1;
|
||||
char *compbuf;
|
||||
int decompsz;
|
||||
int got;
|
||||
u32 decompsz;
|
||||
u32 got;
|
||||
|
||||
compbuf = (char*)ntfs_malloc(compsz);
|
||||
if (compbuf) {
|
||||
if (compsz == na->compression_block_size) {
|
||||
/* if the full block was requested, it was a hole */
|
||||
memset(outbuf,0,compsz);
|
||||
memcpy(&outbuf[pos],b,to_write);
|
||||
fail = 0;
|
||||
} else {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
free(compbuf);
|
||||
}
|
||||
return (fail);
|
||||
}
|
||||
|
@ -1117,9 +1179,9 @@ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
|
|||
|
||||
if (compress) {
|
||||
written = ntfs_comp_set(na, rl, offs, count, outbuf);
|
||||
if (!written)
|
||||
if (written == -1)
|
||||
compress = FALSE;
|
||||
if ((written > 0)
|
||||
if ((written >= 0)
|
||||
&& ntfs_compress_free(na,rl,offs + written,
|
||||
offs + na->compression_block_size))
|
||||
written = -1;
|
||||
|
@ -1160,7 +1222,7 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
|
|||
s64 got;
|
||||
s64 start_vcn;
|
||||
s64 nextblock;
|
||||
int compsz;
|
||||
u32 compsz;
|
||||
char *inbuf;
|
||||
char *outbuf;
|
||||
BOOL fail;
|
||||
|
@ -1259,7 +1321,7 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
|
|||
* if compression was not successful,
|
||||
* only write the part which was requested
|
||||
*/
|
||||
if ((written > 0)
|
||||
if ((written >= 0)
|
||||
/* free the unused clusters */
|
||||
&& !ntfs_compress_free(na,brl,
|
||||
written + roffs,
|
||||
|
@ -1348,7 +1410,7 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs)
|
|||
if (got == to_read) {
|
||||
written = ntfs_comp_set(na, brl, roffs,
|
||||
to_read, inbuf);
|
||||
if ((written > 0)
|
||||
if ((written >= 0)
|
||||
/* free the unused clusters */
|
||||
&& !ntfs_compress_free(na,brl,
|
||||
written + roffs,
|
||||
|
@ -1356,7 +1418,7 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs)
|
|||
done = TRUE;
|
||||
} else
|
||||
/* if compression failed, leave uncompressed */
|
||||
if (!written)
|
||||
if (written == -1)
|
||||
done = TRUE;
|
||||
}
|
||||
} else
|
||||
|
|
|
@ -1156,14 +1156,26 @@ 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) {
|
||||
/*
|
||||
* for compressed files, only deleting contents and expanding
|
||||
* are implemented. Expanding is done by inserting a final
|
||||
* zero, which is optimized as creating a hole when possible.
|
||||
*/
|
||||
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
&& size
|
||||
&& (size < na->initialized_size)) {
|
||||
errno = EOPNOTSUPP;
|
||||
goto exit;
|
||||
}
|
||||
oldsize = na->data_size;
|
||||
if (ntfs_attr_truncate(na, size))
|
||||
goto exit;
|
||||
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
&& (size > na->initialized_size)) {
|
||||
char zero = 0;
|
||||
if (ntfs_attr_pwrite(na, size - 1, 1, &zero) <= 0)
|
||||
goto exit;
|
||||
} else
|
||||
if (ntfs_attr_truncate(na, size))
|
||||
goto exit;
|
||||
if (oldsize != size)
|
||||
set_archive(ni);
|
||||
|
||||
|
|
Loading…
Reference in New Issue