1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * compress.c - Compressed attribute handling code.  Originated from the Linux-NTFS
3987da915Sopenharmony_ci *		project.
4987da915Sopenharmony_ci *
5987da915Sopenharmony_ci * Copyright (c) 2004-2005 Anton Altaparmakov
6987da915Sopenharmony_ci * Copyright (c) 2004-2006 Szabolcs Szakacsits
7987da915Sopenharmony_ci * Copyright (c)      2005 Yura Pakhuchiy
8987da915Sopenharmony_ci * Copyright (c) 2009-2014 Jean-Pierre Andre
9987da915Sopenharmony_ci * Copyright (c)      2014 Eric Biggers
10987da915Sopenharmony_ci *
11987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or
12987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published
13987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or
14987da915Sopenharmony_ci * (at your option) any later version.
15987da915Sopenharmony_ci *
16987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be
17987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
18987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19987da915Sopenharmony_ci * GNU General Public License for more details.
20987da915Sopenharmony_ci *
21987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
22987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G
23987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
24987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25987da915Sopenharmony_ci */
26987da915Sopenharmony_ci
27987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H
28987da915Sopenharmony_ci#include "config.h"
29987da915Sopenharmony_ci#endif
30987da915Sopenharmony_ci
31987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
32987da915Sopenharmony_ci#include <stdio.h>
33987da915Sopenharmony_ci#endif
34987da915Sopenharmony_ci#ifdef HAVE_STRING_H
35987da915Sopenharmony_ci#include <string.h>
36987da915Sopenharmony_ci#endif
37987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
38987da915Sopenharmony_ci#include <stdlib.h>
39987da915Sopenharmony_ci#endif
40987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
41987da915Sopenharmony_ci#include <errno.h>
42987da915Sopenharmony_ci#endif
43987da915Sopenharmony_ci
44987da915Sopenharmony_ci#include "attrib.h"
45987da915Sopenharmony_ci#include "debug.h"
46987da915Sopenharmony_ci#include "volume.h"
47987da915Sopenharmony_ci#include "types.h"
48987da915Sopenharmony_ci#include "layout.h"
49987da915Sopenharmony_ci#include "runlist.h"
50987da915Sopenharmony_ci#include "compress.h"
51987da915Sopenharmony_ci#include "lcnalloc.h"
52987da915Sopenharmony_ci#include "logging.h"
53987da915Sopenharmony_ci#include "misc.h"
54987da915Sopenharmony_ci
55987da915Sopenharmony_ci#undef le16_to_cpup
56987da915Sopenharmony_ci/* the standard le16_to_cpup() crashes for unaligned data on some processors */
57987da915Sopenharmony_ci#define le16_to_cpup(p) (*(u8*)(p) + (((u8*)(p))[1] << 8))
58987da915Sopenharmony_ci
59987da915Sopenharmony_ci/**
60987da915Sopenharmony_ci * enum ntfs_compression_constants - constants used in the compression code
61987da915Sopenharmony_ci */
62987da915Sopenharmony_citypedef enum {
63987da915Sopenharmony_ci	/* Token types and access mask. */
64987da915Sopenharmony_ci	NTFS_SYMBOL_TOKEN	=	0,
65987da915Sopenharmony_ci	NTFS_PHRASE_TOKEN	=	1,
66987da915Sopenharmony_ci	NTFS_TOKEN_MASK		=	1,
67987da915Sopenharmony_ci
68987da915Sopenharmony_ci	/* Compression sub-block constants. */
69987da915Sopenharmony_ci	NTFS_SB_SIZE_MASK	=	0x0fff,
70987da915Sopenharmony_ci	NTFS_SB_SIZE		=	0x1000,
71987da915Sopenharmony_ci	NTFS_SB_IS_COMPRESSED	=	0x8000,
72987da915Sopenharmony_ci} ntfs_compression_constants;
73987da915Sopenharmony_ci
74987da915Sopenharmony_ci/* Match length at or above which ntfs_best_match() will stop searching for
75987da915Sopenharmony_ci * longer matches.  */
76987da915Sopenharmony_ci#define NICE_MATCH_LEN 18
77987da915Sopenharmony_ci
78987da915Sopenharmony_ci/* Maximum number of potential matches that ntfs_best_match() will consider at
79987da915Sopenharmony_ci * each position.  */
80987da915Sopenharmony_ci#define MAX_SEARCH_DEPTH 24
81987da915Sopenharmony_ci
82987da915Sopenharmony_ci/* log base 2 of the number of entries in the hash table for match-finding.  */
83987da915Sopenharmony_ci#define HASH_SHIFT 14
84987da915Sopenharmony_ci
85987da915Sopenharmony_ci/* Constant for the multiplicative hash function.  */
86987da915Sopenharmony_ci#define HASH_MULTIPLIER 0x1E35A7BD
87987da915Sopenharmony_ci
88987da915Sopenharmony_cistruct COMPRESS_CONTEXT {
89987da915Sopenharmony_ci	const unsigned char *inbuf;
90987da915Sopenharmony_ci	int bufsize;
91987da915Sopenharmony_ci	int size;
92987da915Sopenharmony_ci	int rel;
93987da915Sopenharmony_ci	int mxsz;
94987da915Sopenharmony_ci	s16 head[1 << HASH_SHIFT];
95987da915Sopenharmony_ci	s16 prev[NTFS_SB_SIZE];
96987da915Sopenharmony_ci} ;
97987da915Sopenharmony_ci
98987da915Sopenharmony_ci/*
99987da915Sopenharmony_ci *		Hash the next 3-byte sequence in the input buffer
100987da915Sopenharmony_ci */
101987da915Sopenharmony_cistatic inline unsigned int ntfs_hash(const u8 *p)
102987da915Sopenharmony_ci{
103987da915Sopenharmony_ci	u32 str;
104987da915Sopenharmony_ci	u32 hash;
105987da915Sopenharmony_ci
106987da915Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
107987da915Sopenharmony_ci	/* Unaligned access allowed, and little endian CPU.
108987da915Sopenharmony_ci	 * Callers ensure that at least 4 (not 3) bytes are remaining.  */
109987da915Sopenharmony_ci	str = *(const u32 *)p & 0xFFFFFF;
110987da915Sopenharmony_ci#else
111987da915Sopenharmony_ci	str = ((u32)p[0] << 0) | ((u32)p[1] << 8) | ((u32)p[2] << 16);
112987da915Sopenharmony_ci#endif
113987da915Sopenharmony_ci
114987da915Sopenharmony_ci	hash = str * HASH_MULTIPLIER;
115987da915Sopenharmony_ci
116987da915Sopenharmony_ci	/* High bits are more random than the low bits.  */
117987da915Sopenharmony_ci	return hash >> (32 - HASH_SHIFT);
118987da915Sopenharmony_ci}
119987da915Sopenharmony_ci
120987da915Sopenharmony_ci/*
121987da915Sopenharmony_ci *		Search for the longest sequence matching current position
122987da915Sopenharmony_ci *
123987da915Sopenharmony_ci *	A hash table, each entry of which points to a chain of sequence
124987da915Sopenharmony_ci *	positions sharing the corresponding hash code, is maintained to speed up
125987da915Sopenharmony_ci *	searching for matches.  To maintain the hash table, either
126987da915Sopenharmony_ci *	ntfs_best_match() or ntfs_skip_position() has to be called for each
127987da915Sopenharmony_ci *	consecutive position.
128987da915Sopenharmony_ci *
129987da915Sopenharmony_ci *	This function is heavily used; it has to be optimized carefully.
130987da915Sopenharmony_ci *
131987da915Sopenharmony_ci *	This function sets pctx->size and pctx->rel to the length and offset,
132987da915Sopenharmony_ci *	respectively, of the longest match found.
133987da915Sopenharmony_ci *
134987da915Sopenharmony_ci *	The minimum match length is assumed to be 3, and the maximum match
135987da915Sopenharmony_ci *	length is assumed to be pctx->mxsz.  If this function produces
136987da915Sopenharmony_ci *	pctx->size < 3, then no match was found.
137987da915Sopenharmony_ci *
138987da915Sopenharmony_ci *	Note: for the following reasons, this function is not guaranteed to find
139987da915Sopenharmony_ci *	*the* longest match up to pctx->mxsz:
140987da915Sopenharmony_ci *
141987da915Sopenharmony_ci *	(1) If this function finds a match of NICE_MATCH_LEN bytes or greater,
142987da915Sopenharmony_ci *	    it ends early because a match this long is good enough and it's not
143987da915Sopenharmony_ci *	    worth spending more time searching.
144987da915Sopenharmony_ci *
145987da915Sopenharmony_ci *	(2) If this function considers MAX_SEARCH_DEPTH matches with a single
146987da915Sopenharmony_ci *	    position, it ends early and returns the longest match found so far.
147987da915Sopenharmony_ci *	    This saves a lot of time on degenerate inputs.
148987da915Sopenharmony_ci */
149987da915Sopenharmony_cistatic void ntfs_best_match(struct COMPRESS_CONTEXT *pctx, const int i,
150987da915Sopenharmony_ci			    int best_len)
151987da915Sopenharmony_ci{
152987da915Sopenharmony_ci	const u8 * const inbuf = pctx->inbuf;
153987da915Sopenharmony_ci	const u8 * const strptr = &inbuf[i]; /* String we're matching against */
154987da915Sopenharmony_ci	s16 * const prev = pctx->prev;
155987da915Sopenharmony_ci	const int max_len = min(pctx->bufsize - i, pctx->mxsz);
156987da915Sopenharmony_ci	const int nice_len = min(NICE_MATCH_LEN, max_len);
157987da915Sopenharmony_ci	int depth_remaining = MAX_SEARCH_DEPTH;
158987da915Sopenharmony_ci	const u8 *best_matchptr = strptr;
159987da915Sopenharmony_ci	unsigned int hash;
160987da915Sopenharmony_ci	s16 cur_match;
161987da915Sopenharmony_ci	const u8 *matchptr;
162987da915Sopenharmony_ci	int len;
163987da915Sopenharmony_ci
164987da915Sopenharmony_ci	if (max_len < 4)
165987da915Sopenharmony_ci		goto out;
166987da915Sopenharmony_ci
167987da915Sopenharmony_ci	/* Insert the current sequence into the appropriate hash chain.  */
168987da915Sopenharmony_ci	hash = ntfs_hash(strptr);
169987da915Sopenharmony_ci	cur_match = pctx->head[hash];
170987da915Sopenharmony_ci	prev[i] = cur_match;
171987da915Sopenharmony_ci	pctx->head[hash] = i;
172987da915Sopenharmony_ci
173987da915Sopenharmony_ci	if (best_len >= max_len) {
174987da915Sopenharmony_ci		/* Lazy match is being attempted, but there aren't enough length
175987da915Sopenharmony_ci		 * bits remaining to code a longer match.  */
176987da915Sopenharmony_ci		goto out;
177987da915Sopenharmony_ci	}
178987da915Sopenharmony_ci
179987da915Sopenharmony_ci	/* Search the appropriate hash chain for matches.  */
180987da915Sopenharmony_ci
181987da915Sopenharmony_ci	for (; cur_match >= 0 && depth_remaining--;
182987da915Sopenharmony_ci		cur_match = prev[cur_match])
183987da915Sopenharmony_ci	{
184987da915Sopenharmony_ci
185987da915Sopenharmony_ci		matchptr = &inbuf[cur_match];
186987da915Sopenharmony_ci
187987da915Sopenharmony_ci		/* Considering the potential match at 'matchptr':  is it longer
188987da915Sopenharmony_ci		 * than 'best_len'?
189987da915Sopenharmony_ci		 *
190987da915Sopenharmony_ci		 * The bytes at index 'best_len' are the most likely to differ,
191987da915Sopenharmony_ci		 * so check them first.
192987da915Sopenharmony_ci		 *
193987da915Sopenharmony_ci		 * The bytes at indices 'best_len - 1' and '0' are less
194987da915Sopenharmony_ci		 * important to check separately.  But doing so still gives a
195987da915Sopenharmony_ci		 * slight performance improvement, at least on x86_64, probably
196987da915Sopenharmony_ci		 * because they create separate branches for the CPU to predict
197987da915Sopenharmony_ci		 * independently of the branches in the main comparison loops.
198987da915Sopenharmony_ci		 */
199987da915Sopenharmony_ci		if (matchptr[best_len] != strptr[best_len] ||
200987da915Sopenharmony_ci		    matchptr[best_len - 1] != strptr[best_len - 1] ||
201987da915Sopenharmony_ci		    matchptr[0] != strptr[0])
202987da915Sopenharmony_ci			goto next_match;
203987da915Sopenharmony_ci
204987da915Sopenharmony_ci		for (len = 1; len < best_len - 1; len++)
205987da915Sopenharmony_ci			if (matchptr[len] != strptr[len])
206987da915Sopenharmony_ci				goto next_match;
207987da915Sopenharmony_ci
208987da915Sopenharmony_ci		/* The match is the longest found so far ---
209987da915Sopenharmony_ci		 * at least 'best_len' + 1 bytes.  Continue extending it.  */
210987da915Sopenharmony_ci
211987da915Sopenharmony_ci		best_matchptr = matchptr;
212987da915Sopenharmony_ci
213987da915Sopenharmony_ci		do {
214987da915Sopenharmony_ci			if (++best_len >= nice_len) {
215987da915Sopenharmony_ci				/* 'nice_len' reached; don't waste time
216987da915Sopenharmony_ci				 * searching for longer matches.  Extend the
217987da915Sopenharmony_ci				 * match as far as possible and terminate the
218987da915Sopenharmony_ci				 * search.  */
219987da915Sopenharmony_ci				while (best_len < max_len &&
220987da915Sopenharmony_ci					(best_matchptr[best_len] ==
221987da915Sopenharmony_ci						strptr[best_len]))
222987da915Sopenharmony_ci				{
223987da915Sopenharmony_ci					best_len++;
224987da915Sopenharmony_ci				}
225987da915Sopenharmony_ci				goto out;
226987da915Sopenharmony_ci			}
227987da915Sopenharmony_ci		} while (best_matchptr[best_len] == strptr[best_len]);
228987da915Sopenharmony_ci
229987da915Sopenharmony_ci		/* Found a longer match, but 'nice_len' not yet reached.  */
230987da915Sopenharmony_ci
231987da915Sopenharmony_ci	next_match:
232987da915Sopenharmony_ci		/* Continue to next match in the chain.  */
233987da915Sopenharmony_ci		;
234987da915Sopenharmony_ci	}
235987da915Sopenharmony_ci
236987da915Sopenharmony_ci	/* Reached end of chain, or ended early due to reaching the maximum
237987da915Sopenharmony_ci	 * search depth.  */
238987da915Sopenharmony_ci
239987da915Sopenharmony_ciout:
240987da915Sopenharmony_ci	/* Return the longest match we were able to find.  */
241987da915Sopenharmony_ci	pctx->size = best_len;
242987da915Sopenharmony_ci	pctx->rel = best_matchptr - strptr; /* given as a negative number! */
243987da915Sopenharmony_ci}
244987da915Sopenharmony_ci
245987da915Sopenharmony_ci/*
246987da915Sopenharmony_ci *		Advance the match-finder, but don't search for matches.
247987da915Sopenharmony_ci */
248987da915Sopenharmony_cistatic void ntfs_skip_position(struct COMPRESS_CONTEXT *pctx, const int i)
249987da915Sopenharmony_ci{
250987da915Sopenharmony_ci	unsigned int hash;
251987da915Sopenharmony_ci
252987da915Sopenharmony_ci	if (pctx->bufsize - i < 4)
253987da915Sopenharmony_ci		return;
254987da915Sopenharmony_ci
255987da915Sopenharmony_ci	/* Insert the current sequence into the appropriate hash chain.  */
256987da915Sopenharmony_ci	hash = ntfs_hash(pctx->inbuf + i);
257987da915Sopenharmony_ci	pctx->prev[i] = pctx->head[hash];
258987da915Sopenharmony_ci	pctx->head[hash] = i;
259987da915Sopenharmony_ci}
260987da915Sopenharmony_ci
261987da915Sopenharmony_ci/*
262987da915Sopenharmony_ci *		Compress a 4096-byte block
263987da915Sopenharmony_ci *
264987da915Sopenharmony_ci *	Returns a header of two bytes followed by the compressed data.
265987da915Sopenharmony_ci *	If compression is not effective, the header and an uncompressed
266987da915Sopenharmony_ci *	block is returned.
267987da915Sopenharmony_ci *
268987da915Sopenharmony_ci *	Note : two bytes may be output before output buffer overflow
269987da915Sopenharmony_ci *	is detected, so a 4100-bytes output buffer must be reserved.
270987da915Sopenharmony_ci *
271987da915Sopenharmony_ci *	Returns the size of the compressed block, including the
272987da915Sopenharmony_ci *			header (minimal size is 2, maximum size is 4098)
273987da915Sopenharmony_ci *		0 if an error has been met.
274987da915Sopenharmony_ci */
275987da915Sopenharmony_ci
276987da915Sopenharmony_cistatic unsigned int ntfs_compress_block(const char *inbuf, const int bufsize,
277987da915Sopenharmony_ci				char *outbuf)
278987da915Sopenharmony_ci{
279987da915Sopenharmony_ci	struct COMPRESS_CONTEXT *pctx;
280987da915Sopenharmony_ci	int i; /* current position */
281987da915Sopenharmony_ci	int j; /* end of best match from current position */
282987da915Sopenharmony_ci	int k; /* end of best match from next position */
283987da915Sopenharmony_ci	int offs; /* offset to best match */
284987da915Sopenharmony_ci	int bp; /* bits to store offset */
285987da915Sopenharmony_ci	int bp_cur; /* saved bits to store offset at current position */
286987da915Sopenharmony_ci	int mxoff; /* max match offset : 1 << bp */
287987da915Sopenharmony_ci	unsigned int xout;
288987da915Sopenharmony_ci	unsigned int q; /* aggregated offset and size */
289987da915Sopenharmony_ci	int have_match; /* do we have a match at the current position? */
290987da915Sopenharmony_ci	char *ptag; /* location reserved for a tag */
291987da915Sopenharmony_ci	int tag;    /* current value of tag */
292987da915Sopenharmony_ci	int ntag;   /* count of bits still undefined in tag */
293987da915Sopenharmony_ci
294987da915Sopenharmony_ci	pctx = ntfs_malloc(sizeof(struct COMPRESS_CONTEXT));
295987da915Sopenharmony_ci	if (!pctx) {
296987da915Sopenharmony_ci		errno = ENOMEM;
297987da915Sopenharmony_ci		return 0;
298987da915Sopenharmony_ci	}
299987da915Sopenharmony_ci
300987da915Sopenharmony_ci	/* All hash chains start as empty.  The special value '-1' indicates the
301987da915Sopenharmony_ci	 * end of each hash chain.  */
302987da915Sopenharmony_ci	memset(pctx->head, 0xFF, sizeof(pctx->head));
303987da915Sopenharmony_ci
304987da915Sopenharmony_ci	pctx->inbuf = (const unsigned char*)inbuf;
305987da915Sopenharmony_ci	pctx->bufsize = bufsize;
306987da915Sopenharmony_ci	xout = 2;
307987da915Sopenharmony_ci	i = 0;
308987da915Sopenharmony_ci	bp = 4;
309987da915Sopenharmony_ci	mxoff = 1 << bp;
310987da915Sopenharmony_ci	pctx->mxsz = (1 << (16 - bp)) + 2;
311987da915Sopenharmony_ci	have_match = 0;
312987da915Sopenharmony_ci	tag = 0;
313987da915Sopenharmony_ci	ntag = 8;
314987da915Sopenharmony_ci	ptag = &outbuf[xout++];
315987da915Sopenharmony_ci
316987da915Sopenharmony_ci	while ((i < bufsize) && (xout < (NTFS_SB_SIZE + 2))) {
317987da915Sopenharmony_ci
318987da915Sopenharmony_ci		/* This implementation uses "lazy" parsing: it always chooses
319987da915Sopenharmony_ci		 * the longest match, unless the match at the next position is
320987da915Sopenharmony_ci		 * longer.  This is the same strategy used by the high
321987da915Sopenharmony_ci		 * compression modes of zlib.  */
322987da915Sopenharmony_ci
323987da915Sopenharmony_ci		if (!have_match) {
324987da915Sopenharmony_ci			/* Find the longest match at the current position.  But
325987da915Sopenharmony_ci			 * first adjust the maximum match length if needed.
326987da915Sopenharmony_ci			 * (This loop might need to run more than one time in
327987da915Sopenharmony_ci			 * the case that we just output a long match.)  */
328987da915Sopenharmony_ci			while (mxoff < i) {
329987da915Sopenharmony_ci				bp++;
330987da915Sopenharmony_ci				mxoff <<= 1;
331987da915Sopenharmony_ci				pctx->mxsz = (pctx->mxsz + 2) >> 1;
332987da915Sopenharmony_ci			}
333987da915Sopenharmony_ci			ntfs_best_match(pctx, i, 2);
334987da915Sopenharmony_ci		}
335987da915Sopenharmony_ci
336987da915Sopenharmony_ci		if (pctx->size >= 3) {
337987da915Sopenharmony_ci
338987da915Sopenharmony_ci			/* Found a match at the current position.  */
339987da915Sopenharmony_ci
340987da915Sopenharmony_ci			j = i + pctx->size;
341987da915Sopenharmony_ci			bp_cur = bp;
342987da915Sopenharmony_ci			offs = pctx->rel;
343987da915Sopenharmony_ci
344987da915Sopenharmony_ci			if (pctx->size >= NICE_MATCH_LEN) {
345987da915Sopenharmony_ci
346987da915Sopenharmony_ci				/* Choose long matches immediately.  */
347987da915Sopenharmony_ci
348987da915Sopenharmony_ci				q = (~offs << (16 - bp_cur)) + (j - i - 3);
349987da915Sopenharmony_ci				outbuf[xout++] = q & 255;
350987da915Sopenharmony_ci				outbuf[xout++] = (q >> 8) & 255;
351987da915Sopenharmony_ci				tag |= (1 << (8 - ntag));
352987da915Sopenharmony_ci
353987da915Sopenharmony_ci				if (j == bufsize) {
354987da915Sopenharmony_ci					/* Shortcut if the match extends to the
355987da915Sopenharmony_ci					 * end of the buffer.  */
356987da915Sopenharmony_ci					i = j;
357987da915Sopenharmony_ci					--ntag;
358987da915Sopenharmony_ci					break;
359987da915Sopenharmony_ci				}
360987da915Sopenharmony_ci				i += 1;
361987da915Sopenharmony_ci				do {
362987da915Sopenharmony_ci					ntfs_skip_position(pctx, i);
363987da915Sopenharmony_ci				} while (++i != j);
364987da915Sopenharmony_ci				have_match = 0;
365987da915Sopenharmony_ci			} else {
366987da915Sopenharmony_ci				/* Check for a longer match at the next
367987da915Sopenharmony_ci				 * position.  */
368987da915Sopenharmony_ci
369987da915Sopenharmony_ci				/* Doesn't need to be while() since we just
370987da915Sopenharmony_ci				 * adjusted the maximum match length at the
371987da915Sopenharmony_ci				 * previous position.  */
372987da915Sopenharmony_ci				if (mxoff < i + 1) {
373987da915Sopenharmony_ci					bp++;
374987da915Sopenharmony_ci					mxoff <<= 1;
375987da915Sopenharmony_ci					pctx->mxsz = (pctx->mxsz + 2) >> 1;
376987da915Sopenharmony_ci				}
377987da915Sopenharmony_ci				ntfs_best_match(pctx, i + 1, pctx->size);
378987da915Sopenharmony_ci				k = i + 1 + pctx->size;
379987da915Sopenharmony_ci
380987da915Sopenharmony_ci				if (k > (j + 1)) {
381987da915Sopenharmony_ci					/* Next match is longer.
382987da915Sopenharmony_ci					 * Output a literal.  */
383987da915Sopenharmony_ci					outbuf[xout++] = inbuf[i++];
384987da915Sopenharmony_ci					have_match = 1;
385987da915Sopenharmony_ci				} else {
386987da915Sopenharmony_ci					/* Next match isn't longer.
387987da915Sopenharmony_ci					 * Output the current match.  */
388987da915Sopenharmony_ci					q = (~offs << (16 - bp_cur)) +
389987da915Sopenharmony_ci							(j - i - 3);
390987da915Sopenharmony_ci					outbuf[xout++] = q & 255;
391987da915Sopenharmony_ci					outbuf[xout++] = (q >> 8) & 255;
392987da915Sopenharmony_ci					tag |= (1 << (8 - ntag));
393987da915Sopenharmony_ci
394987da915Sopenharmony_ci					/* The minimum match length is 3, and
395987da915Sopenharmony_ci					 * we've run two bytes through the
396987da915Sopenharmony_ci					 * matchfinder already.  So the minimum
397987da915Sopenharmony_ci					 * number of positions we need to skip
398987da915Sopenharmony_ci					 * is 1.  */
399987da915Sopenharmony_ci					i += 2;
400987da915Sopenharmony_ci					do {
401987da915Sopenharmony_ci						ntfs_skip_position(pctx, i);
402987da915Sopenharmony_ci					} while (++i != j);
403987da915Sopenharmony_ci					have_match = 0;
404987da915Sopenharmony_ci				}
405987da915Sopenharmony_ci			}
406987da915Sopenharmony_ci		} else {
407987da915Sopenharmony_ci			/* No match at current position.  Output a literal.  */
408987da915Sopenharmony_ci			outbuf[xout++] = inbuf[i++];
409987da915Sopenharmony_ci			have_match = 0;
410987da915Sopenharmony_ci		}
411987da915Sopenharmony_ci
412987da915Sopenharmony_ci		/* Store the tag if fully used.  */
413987da915Sopenharmony_ci		if (!--ntag) {
414987da915Sopenharmony_ci			*ptag = tag;
415987da915Sopenharmony_ci			ntag = 8;
416987da915Sopenharmony_ci			ptag = &outbuf[xout++];
417987da915Sopenharmony_ci			tag = 0;
418987da915Sopenharmony_ci		}
419987da915Sopenharmony_ci	}
420987da915Sopenharmony_ci
421987da915Sopenharmony_ci	/* Store the last tag if partially used.  */
422987da915Sopenharmony_ci	if (ntag == 8)
423987da915Sopenharmony_ci		xout--;
424987da915Sopenharmony_ci	else
425987da915Sopenharmony_ci		*ptag = tag;
426987da915Sopenharmony_ci
427987da915Sopenharmony_ci	/* Determine whether to store the data compressed or uncompressed.  */
428987da915Sopenharmony_ci
429987da915Sopenharmony_ci	if ((i >= bufsize) && (xout < (NTFS_SB_SIZE + 2))) {
430987da915Sopenharmony_ci		/* Compressed.  */
431987da915Sopenharmony_ci		outbuf[0] = (xout - 3) & 255;
432987da915Sopenharmony_ci		outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15);
433987da915Sopenharmony_ci	} else {
434987da915Sopenharmony_ci		/* Uncompressed.  */
435987da915Sopenharmony_ci		memcpy(&outbuf[2], inbuf, bufsize);
436987da915Sopenharmony_ci		if (bufsize < NTFS_SB_SIZE)
437987da915Sopenharmony_ci			memset(&outbuf[bufsize + 2], 0, NTFS_SB_SIZE - bufsize);
438987da915Sopenharmony_ci		outbuf[0] = 0xff;
439987da915Sopenharmony_ci		outbuf[1] = 0x3f;
440987da915Sopenharmony_ci		xout = NTFS_SB_SIZE + 2;
441987da915Sopenharmony_ci	}
442987da915Sopenharmony_ci
443987da915Sopenharmony_ci	/* Free the compression context and return the total number of bytes
444987da915Sopenharmony_ci	 * written to 'outbuf'.  */
445987da915Sopenharmony_ci	free(pctx);
446987da915Sopenharmony_ci	return (xout);
447987da915Sopenharmony_ci}
448987da915Sopenharmony_ci
449987da915Sopenharmony_ci/**
450987da915Sopenharmony_ci * ntfs_decompress - decompress a compression block into an array of pages
451987da915Sopenharmony_ci * @dest:	buffer to which to write the decompressed data
452987da915Sopenharmony_ci * @dest_size:	size of buffer @dest in bytes
453987da915Sopenharmony_ci * @cb_start:	compression block to decompress
454987da915Sopenharmony_ci * @cb_size:	size of compression block @cb_start in bytes
455987da915Sopenharmony_ci *
456987da915Sopenharmony_ci * This decompresses the compression block @cb_start into the destination
457987da915Sopenharmony_ci * buffer @dest.
458987da915Sopenharmony_ci *
459987da915Sopenharmony_ci * @cb_start is a pointer to the compression block which needs decompressing
460987da915Sopenharmony_ci * and @cb_size is the size of @cb_start in bytes (8-64kiB).
461987da915Sopenharmony_ci *
462987da915Sopenharmony_ci * Return 0 if success or -EOVERFLOW on error in the compressed stream.
463987da915Sopenharmony_ci */
464987da915Sopenharmony_cistatic int ntfs_decompress(u8 *dest, const u32 dest_size,
465987da915Sopenharmony_ci		u8 *const cb_start, const u32 cb_size)
466987da915Sopenharmony_ci{
467987da915Sopenharmony_ci	/*
468987da915Sopenharmony_ci	 * Pointers into the compressed data, i.e. the compression block (cb),
469987da915Sopenharmony_ci	 * and the therein contained sub-blocks (sb).
470987da915Sopenharmony_ci	 */
471987da915Sopenharmony_ci	u8 *cb_end = cb_start + cb_size; /* End of cb. */
472987da915Sopenharmony_ci	u8 *cb = cb_start;	/* Current position in cb. */
473987da915Sopenharmony_ci	u8 *cb_sb_start = cb;	/* Beginning of the current sb in the cb. */
474987da915Sopenharmony_ci	u8 *cb_sb_end;		/* End of current sb / beginning of next sb. */
475987da915Sopenharmony_ci	/* Variables for uncompressed data / destination. */
476987da915Sopenharmony_ci	u8 *dest_end = dest + dest_size;	/* End of dest buffer. */
477987da915Sopenharmony_ci	u8 *dest_sb_start;	/* Start of current sub-block in dest. */
478987da915Sopenharmony_ci	u8 *dest_sb_end;	/* End of current sb in dest. */
479987da915Sopenharmony_ci	/* Variables for tag and token parsing. */
480987da915Sopenharmony_ci	u8 tag;			/* Current tag. */
481987da915Sopenharmony_ci	int token;		/* Loop counter for the eight tokens in tag. */
482987da915Sopenharmony_ci
483987da915Sopenharmony_ci	ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size);
484987da915Sopenharmony_cido_next_sb:
485987da915Sopenharmony_ci	ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n",
486987da915Sopenharmony_ci			(int)(cb - cb_start));
487987da915Sopenharmony_ci	/*
488987da915Sopenharmony_ci	 * Have we reached the end of the compression block or the end of the
489987da915Sopenharmony_ci	 * decompressed data?  The latter can happen for example if the current
490987da915Sopenharmony_ci	 * position in the compression block is one byte before its end so the
491987da915Sopenharmony_ci	 * first two checks do not detect it.
492987da915Sopenharmony_ci	 */
493987da915Sopenharmony_ci	if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) {
494987da915Sopenharmony_ci		if (dest_end > dest)
495987da915Sopenharmony_ci			memset(dest, 0, dest_end - dest);
496987da915Sopenharmony_ci		ntfs_log_debug("Completed. Returning success (0).\n");
497987da915Sopenharmony_ci		return 0;
498987da915Sopenharmony_ci	}
499987da915Sopenharmony_ci	/* Setup offset for the current sub-block destination. */
500987da915Sopenharmony_ci	dest_sb_start = dest;
501987da915Sopenharmony_ci	dest_sb_end = dest + NTFS_SB_SIZE;
502987da915Sopenharmony_ci	/* Check that we are still within allowed boundaries. */
503987da915Sopenharmony_ci	if (dest_sb_end > dest_end)
504987da915Sopenharmony_ci		goto return_overflow;
505987da915Sopenharmony_ci	/* Does the minimum size of a compressed sb overflow valid range? */
506987da915Sopenharmony_ci	if (cb + 6 > cb_end)
507987da915Sopenharmony_ci		goto return_overflow;
508987da915Sopenharmony_ci	/* Setup the current sub-block source pointers and validate range. */
509987da915Sopenharmony_ci	cb_sb_start = cb;
510987da915Sopenharmony_ci	cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK)
511987da915Sopenharmony_ci			+ 3;
512987da915Sopenharmony_ci	if (cb_sb_end > cb_end)
513987da915Sopenharmony_ci		goto return_overflow;
514987da915Sopenharmony_ci	/* Now, we are ready to process the current sub-block (sb). */
515987da915Sopenharmony_ci	if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) {
516987da915Sopenharmony_ci		ntfs_log_debug("Found uncompressed sub-block.\n");
517987da915Sopenharmony_ci		/* This sb is not compressed, just copy it into destination. */
518987da915Sopenharmony_ci		/* Advance source position to first data byte. */
519987da915Sopenharmony_ci		cb += 2;
520987da915Sopenharmony_ci		/* An uncompressed sb must be full size. */
521987da915Sopenharmony_ci		if (cb_sb_end - cb != NTFS_SB_SIZE)
522987da915Sopenharmony_ci			goto return_overflow;
523987da915Sopenharmony_ci		/* Copy the block and advance the source position. */
524987da915Sopenharmony_ci		memcpy(dest, cb, NTFS_SB_SIZE);
525987da915Sopenharmony_ci		cb += NTFS_SB_SIZE;
526987da915Sopenharmony_ci		/* Advance destination position to next sub-block. */
527987da915Sopenharmony_ci		dest += NTFS_SB_SIZE;
528987da915Sopenharmony_ci		goto do_next_sb;
529987da915Sopenharmony_ci	}
530987da915Sopenharmony_ci	ntfs_log_debug("Found compressed sub-block.\n");
531987da915Sopenharmony_ci	/* This sb is compressed, decompress it into destination. */
532987da915Sopenharmony_ci	/* Forward to the first tag in the sub-block. */
533987da915Sopenharmony_ci	cb += 2;
534987da915Sopenharmony_cido_next_tag:
535987da915Sopenharmony_ci	if (cb == cb_sb_end) {
536987da915Sopenharmony_ci		/* Check if the decompressed sub-block was not full-length. */
537987da915Sopenharmony_ci		if (dest < dest_sb_end) {
538987da915Sopenharmony_ci			int nr_bytes = dest_sb_end - dest;
539987da915Sopenharmony_ci
540987da915Sopenharmony_ci			ntfs_log_debug("Filling incomplete sub-block with zeroes.\n");
541987da915Sopenharmony_ci			/* Zero remainder and update destination position. */
542987da915Sopenharmony_ci			memset(dest, 0, nr_bytes);
543987da915Sopenharmony_ci			dest += nr_bytes;
544987da915Sopenharmony_ci		}
545987da915Sopenharmony_ci		/* We have finished the current sub-block. */
546987da915Sopenharmony_ci		goto do_next_sb;
547987da915Sopenharmony_ci	}
548987da915Sopenharmony_ci	/* Check we are still in range. */
549987da915Sopenharmony_ci	if (cb > cb_sb_end || dest > dest_sb_end)
550987da915Sopenharmony_ci		goto return_overflow;
551987da915Sopenharmony_ci	/* Get the next tag and advance to first token. */
552987da915Sopenharmony_ci	tag = *cb++;
553987da915Sopenharmony_ci	/* Parse the eight tokens described by the tag. */
554987da915Sopenharmony_ci	for (token = 0; token < 8; token++, tag >>= 1) {
555987da915Sopenharmony_ci		u16 lg, pt, length, max_non_overlap;
556987da915Sopenharmony_ci		register u16 i;
557987da915Sopenharmony_ci		u8 *dest_back_addr;
558987da915Sopenharmony_ci
559987da915Sopenharmony_ci		/* Check if we are done / still in range. */
560987da915Sopenharmony_ci		if (cb >= cb_sb_end || dest > dest_sb_end)
561987da915Sopenharmony_ci			break;
562987da915Sopenharmony_ci		/* Determine token type and parse appropriately.*/
563987da915Sopenharmony_ci		if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
564987da915Sopenharmony_ci			/*
565987da915Sopenharmony_ci			 * We have a symbol token, copy the symbol across, and
566987da915Sopenharmony_ci			 * advance the source and destination positions.
567987da915Sopenharmony_ci			 */
568987da915Sopenharmony_ci			*dest++ = *cb++;
569987da915Sopenharmony_ci			/* Continue with the next token. */
570987da915Sopenharmony_ci			continue;
571987da915Sopenharmony_ci		}
572987da915Sopenharmony_ci		/*
573987da915Sopenharmony_ci		 * We have a phrase token. Make sure it is not the first tag in
574987da915Sopenharmony_ci		 * the sb as this is illegal and would confuse the code below.
575987da915Sopenharmony_ci		 */
576987da915Sopenharmony_ci		if (dest == dest_sb_start)
577987da915Sopenharmony_ci			goto return_overflow;
578987da915Sopenharmony_ci		/*
579987da915Sopenharmony_ci		 * Determine the number of bytes to go back (p) and the number
580987da915Sopenharmony_ci		 * of bytes to copy (l). We use an optimized algorithm in which
581987da915Sopenharmony_ci		 * we first calculate log2(current destination position in sb),
582987da915Sopenharmony_ci		 * which allows determination of l and p in O(1) rather than
583987da915Sopenharmony_ci		 * O(n). We just need an arch-optimized log2() function now.
584987da915Sopenharmony_ci		 */
585987da915Sopenharmony_ci		lg = 0;
586987da915Sopenharmony_ci		for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1)
587987da915Sopenharmony_ci			lg++;
588987da915Sopenharmony_ci		/* Get the phrase token into i. */
589987da915Sopenharmony_ci		pt = le16_to_cpup((le16*)cb);
590987da915Sopenharmony_ci		/*
591987da915Sopenharmony_ci		 * Calculate starting position of the byte sequence in
592987da915Sopenharmony_ci		 * the destination using the fact that p = (pt >> (12 - lg)) + 1
593987da915Sopenharmony_ci		 * and make sure we don't go too far back.
594987da915Sopenharmony_ci		 */
595987da915Sopenharmony_ci		dest_back_addr = dest - (pt >> (12 - lg)) - 1;
596987da915Sopenharmony_ci		if (dest_back_addr < dest_sb_start)
597987da915Sopenharmony_ci			goto return_overflow;
598987da915Sopenharmony_ci		/* Now calculate the length of the byte sequence. */
599987da915Sopenharmony_ci		length = (pt & (0xfff >> lg)) + 3;
600987da915Sopenharmony_ci		/* Verify destination is in range. */
601987da915Sopenharmony_ci		if (dest + length > dest_sb_end)
602987da915Sopenharmony_ci			goto return_overflow;
603987da915Sopenharmony_ci		/* The number of non-overlapping bytes. */
604987da915Sopenharmony_ci		max_non_overlap = dest - dest_back_addr;
605987da915Sopenharmony_ci		if (length <= max_non_overlap) {
606987da915Sopenharmony_ci			/* The byte sequence doesn't overlap, just copy it. */
607987da915Sopenharmony_ci			memcpy(dest, dest_back_addr, length);
608987da915Sopenharmony_ci			/* Advance destination pointer. */
609987da915Sopenharmony_ci			dest += length;
610987da915Sopenharmony_ci		} else {
611987da915Sopenharmony_ci			/*
612987da915Sopenharmony_ci			 * The byte sequence does overlap, copy non-overlapping
613987da915Sopenharmony_ci			 * part and then do a slow byte by byte copy for the
614987da915Sopenharmony_ci			 * overlapping part. Also, advance the destination
615987da915Sopenharmony_ci			 * pointer.
616987da915Sopenharmony_ci			 */
617987da915Sopenharmony_ci			memcpy(dest, dest_back_addr, max_non_overlap);
618987da915Sopenharmony_ci			dest += max_non_overlap;
619987da915Sopenharmony_ci			dest_back_addr += max_non_overlap;
620987da915Sopenharmony_ci			length -= max_non_overlap;
621987da915Sopenharmony_ci			while (length--)
622987da915Sopenharmony_ci				*dest++ = *dest_back_addr++;
623987da915Sopenharmony_ci		}
624987da915Sopenharmony_ci		/* Advance source position and continue with the next token. */
625987da915Sopenharmony_ci		cb += 2;
626987da915Sopenharmony_ci	}
627987da915Sopenharmony_ci	/* No tokens left in the current tag. Continue with the next tag. */
628987da915Sopenharmony_ci	goto do_next_tag;
629987da915Sopenharmony_cireturn_overflow:
630987da915Sopenharmony_ci	errno = EOVERFLOW;
631987da915Sopenharmony_ci	ntfs_log_perror("Failed to decompress file");
632987da915Sopenharmony_ci	return -1;
633987da915Sopenharmony_ci}
634987da915Sopenharmony_ci
635987da915Sopenharmony_ci/**
636987da915Sopenharmony_ci * ntfs_is_cb_compressed - internal function, do not use
637987da915Sopenharmony_ci *
638987da915Sopenharmony_ci * This is a very specialised function determining if a cb is compressed or
639987da915Sopenharmony_ci * uncompressed.  It is assumed that checking for a sparse cb has already been
640987da915Sopenharmony_ci * performed and that the cb is not sparse.  It makes all sorts of other
641987da915Sopenharmony_ci * assumptions as well and hence it is not useful anywhere other than where it
642987da915Sopenharmony_ci * is used at the moment.  Please, do not make this function available for use
643987da915Sopenharmony_ci * outside of compress.c as it is bound to confuse people and not do what they
644987da915Sopenharmony_ci * want.
645987da915Sopenharmony_ci *
646987da915Sopenharmony_ci * Return TRUE on errors so that the error will be detected later on in the
647987da915Sopenharmony_ci * code.  Might be a bit confusing to debug but there really should never be
648987da915Sopenharmony_ci * errors coming from here.
649987da915Sopenharmony_ci */
650987da915Sopenharmony_cistatic BOOL ntfs_is_cb_compressed(ntfs_attr *na, runlist_element *rl,
651987da915Sopenharmony_ci				  VCN cb_start_vcn, int cb_clusters)
652987da915Sopenharmony_ci{
653987da915Sopenharmony_ci	/*
654987da915Sopenharmony_ci	 * The simplest case: the run starting at @cb_start_vcn contains
655987da915Sopenharmony_ci	 * @cb_clusters clusters which are all not sparse, thus the cb is not
656987da915Sopenharmony_ci	 * compressed.
657987da915Sopenharmony_ci	 */
658987da915Sopenharmony_cirestart:
659987da915Sopenharmony_ci	cb_clusters -= rl->length - (cb_start_vcn - rl->vcn);
660987da915Sopenharmony_ci	while (cb_clusters > 0) {
661987da915Sopenharmony_ci		/* Go to the next run. */
662987da915Sopenharmony_ci		rl++;
663987da915Sopenharmony_ci		/* Map the next runlist fragment if it is not mapped. */
664987da915Sopenharmony_ci		if (rl->lcn < LCN_HOLE || !rl->length) {
665987da915Sopenharmony_ci			cb_start_vcn = rl->vcn;
666987da915Sopenharmony_ci			rl = ntfs_attr_find_vcn(na, rl->vcn);
667987da915Sopenharmony_ci			if (!rl || rl->lcn < LCN_HOLE || !rl->length)
668987da915Sopenharmony_ci				return TRUE;
669987da915Sopenharmony_ci			/*
670987da915Sopenharmony_ci			 * If the runs were merged need to deal with the
671987da915Sopenharmony_ci			 * resulting partial run so simply restart.
672987da915Sopenharmony_ci			 */
673987da915Sopenharmony_ci			if (rl->vcn < cb_start_vcn)
674987da915Sopenharmony_ci				goto restart;
675987da915Sopenharmony_ci		}
676987da915Sopenharmony_ci		/* If the current run is sparse, the cb is compressed. */
677987da915Sopenharmony_ci		if (rl->lcn == LCN_HOLE)
678987da915Sopenharmony_ci			return TRUE;
679987da915Sopenharmony_ci		/* If the whole cb is not sparse, it is not compressed. */
680987da915Sopenharmony_ci		if (rl->length >= cb_clusters)
681987da915Sopenharmony_ci			return FALSE;
682987da915Sopenharmony_ci		cb_clusters -= rl->length;
683987da915Sopenharmony_ci	};
684987da915Sopenharmony_ci	/* All cb_clusters were not sparse thus the cb is not compressed. */
685987da915Sopenharmony_ci	return FALSE;
686987da915Sopenharmony_ci}
687987da915Sopenharmony_ci
688987da915Sopenharmony_ci/**
689987da915Sopenharmony_ci * ntfs_compressed_attr_pread - read from a compressed attribute
690987da915Sopenharmony_ci * @na:		ntfs attribute to read from
691987da915Sopenharmony_ci * @pos:	byte position in the attribute to begin reading from
692987da915Sopenharmony_ci * @count:	number of bytes to read
693987da915Sopenharmony_ci * @b:		output data buffer
694987da915Sopenharmony_ci *
695987da915Sopenharmony_ci * NOTE:  You probably want to be using attrib.c::ntfs_attr_pread() instead.
696987da915Sopenharmony_ci *
697987da915Sopenharmony_ci * This function will read @count bytes starting at offset @pos from the
698987da915Sopenharmony_ci * compressed ntfs attribute @na into the data buffer @b.
699987da915Sopenharmony_ci *
700987da915Sopenharmony_ci * On success, return the number of successfully read bytes.  If this number
701987da915Sopenharmony_ci * is lower than @count this means that the read reached end of file or that
702987da915Sopenharmony_ci * an error was encountered during the read so that the read is partial.
703987da915Sopenharmony_ci * 0 means end of file or nothing was read (also return 0 when @count is 0).
704987da915Sopenharmony_ci *
705987da915Sopenharmony_ci * On error and nothing has been read, return -1 with errno set appropriately
706987da915Sopenharmony_ci * to the return code of ntfs_pread(), or to EINVAL in case of invalid
707987da915Sopenharmony_ci * arguments.
708987da915Sopenharmony_ci */
709987da915Sopenharmony_cis64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
710987da915Sopenharmony_ci{
711987da915Sopenharmony_ci	s64 br, to_read, ofs, total, total2;
712987da915Sopenharmony_ci	u64 cb_size_mask;
713987da915Sopenharmony_ci	VCN start_vcn, vcn, end_vcn;
714987da915Sopenharmony_ci	ntfs_volume *vol;
715987da915Sopenharmony_ci	runlist_element *rl;
716987da915Sopenharmony_ci	u8 *dest, *cb, *cb_pos, *cb_end;
717987da915Sopenharmony_ci	u32 cb_size;
718987da915Sopenharmony_ci	int err;
719987da915Sopenharmony_ci	ATTR_FLAGS data_flags;
720987da915Sopenharmony_ci	FILE_ATTR_FLAGS compression;
721987da915Sopenharmony_ci	unsigned int nr_cbs, cb_clusters;
722987da915Sopenharmony_ci
723987da915Sopenharmony_ci	ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n",
724987da915Sopenharmony_ci			(unsigned long long)na->ni->mft_no, le32_to_cpu(na->type),
725987da915Sopenharmony_ci			(long long)pos, (long long)count);
726987da915Sopenharmony_ci	data_flags = na->data_flags;
727987da915Sopenharmony_ci	compression = na->ni->flags & FILE_ATTR_COMPRESSED;
728987da915Sopenharmony_ci	if (!na || !na->ni || !na->ni->vol || !b
729987da915Sopenharmony_ci			|| ((data_flags & ATTR_COMPRESSION_MASK)
730987da915Sopenharmony_ci				!= ATTR_IS_COMPRESSED)
731987da915Sopenharmony_ci			|| pos < 0 || count < 0) {
732987da915Sopenharmony_ci		errno = EINVAL;
733987da915Sopenharmony_ci		return -1;
734987da915Sopenharmony_ci	}
735987da915Sopenharmony_ci	/*
736987da915Sopenharmony_ci	 * Encrypted attributes are not supported.  We return access denied,
737987da915Sopenharmony_ci	 * which is what Windows NT4 does, too.
738987da915Sopenharmony_ci	 */
739987da915Sopenharmony_ci	if (NAttrEncrypted(na)) {
740987da915Sopenharmony_ci		errno = EACCES;
741987da915Sopenharmony_ci		return -1;
742987da915Sopenharmony_ci	}
743987da915Sopenharmony_ci	if (!count)
744987da915Sopenharmony_ci		return 0;
745987da915Sopenharmony_ci	/* Truncate reads beyond end of attribute. */
746987da915Sopenharmony_ci	if (pos + count > na->data_size) {
747987da915Sopenharmony_ci		if (pos >= na->data_size) {
748987da915Sopenharmony_ci			return 0;
749987da915Sopenharmony_ci		}
750987da915Sopenharmony_ci		count = na->data_size - pos;
751987da915Sopenharmony_ci	}
752987da915Sopenharmony_ci	/* If it is a resident attribute, simply use ntfs_attr_pread(). */
753987da915Sopenharmony_ci	if (!NAttrNonResident(na))
754987da915Sopenharmony_ci		return ntfs_attr_pread(na, pos, count, b);
755987da915Sopenharmony_ci	if (na->compression_block_size < NTFS_SB_SIZE) {
756987da915Sopenharmony_ci		ntfs_log_error("Unsupported compression block size %ld\n",
757987da915Sopenharmony_ci				(long)na->compression_block_size);
758987da915Sopenharmony_ci		errno = EOVERFLOW;
759987da915Sopenharmony_ci		return (-1);
760987da915Sopenharmony_ci	}
761987da915Sopenharmony_ci	total = total2 = 0;
762987da915Sopenharmony_ci	/* Zero out reads beyond initialized size. */
763987da915Sopenharmony_ci	if (pos + count > na->initialized_size) {
764987da915Sopenharmony_ci		if (pos >= na->initialized_size) {
765987da915Sopenharmony_ci			memset(b, 0, count);
766987da915Sopenharmony_ci			return count;
767987da915Sopenharmony_ci		}
768987da915Sopenharmony_ci		total2 = pos + count - na->initialized_size;
769987da915Sopenharmony_ci		count -= total2;
770987da915Sopenharmony_ci		memset((u8*)b + count, 0, total2);
771987da915Sopenharmony_ci	}
772987da915Sopenharmony_ci	vol = na->ni->vol;
773987da915Sopenharmony_ci	cb_size = na->compression_block_size;
774987da915Sopenharmony_ci	cb_size_mask = cb_size - 1UL;
775987da915Sopenharmony_ci	cb_clusters = na->compression_block_clusters;
776987da915Sopenharmony_ci
777987da915Sopenharmony_ci	/* Need a temporary buffer for each loaded compression block. */
778987da915Sopenharmony_ci	cb = (u8*)ntfs_malloc(cb_size);
779987da915Sopenharmony_ci	if (!cb)
780987da915Sopenharmony_ci		return -1;
781987da915Sopenharmony_ci
782987da915Sopenharmony_ci	/* Need a temporary buffer for each uncompressed block. */
783987da915Sopenharmony_ci	dest = (u8*)ntfs_malloc(cb_size);
784987da915Sopenharmony_ci	if (!dest) {
785987da915Sopenharmony_ci		free(cb);
786987da915Sopenharmony_ci		return -1;
787987da915Sopenharmony_ci	}
788987da915Sopenharmony_ci	/*
789987da915Sopenharmony_ci	 * The first vcn in the first compression block (cb) which we need to
790987da915Sopenharmony_ci	 * decompress.
791987da915Sopenharmony_ci	 */
792987da915Sopenharmony_ci	start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits;
793987da915Sopenharmony_ci	/* Offset in the uncompressed cb at which to start reading data. */
794987da915Sopenharmony_ci	ofs = pos & cb_size_mask;
795987da915Sopenharmony_ci	/*
796987da915Sopenharmony_ci	 * The first vcn in the cb after the last cb which we need to
797987da915Sopenharmony_ci	 * decompress.
798987da915Sopenharmony_ci	 */
799987da915Sopenharmony_ci	end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >>
800987da915Sopenharmony_ci			vol->cluster_size_bits;
801987da915Sopenharmony_ci	/* Number of compression blocks (cbs) in the wanted vcn range. */
802987da915Sopenharmony_ci	nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >>
803987da915Sopenharmony_ci			na->compression_block_size_bits;
804987da915Sopenharmony_ci	cb_end = cb + cb_size;
805987da915Sopenharmony_cido_next_cb:
806987da915Sopenharmony_ci	nr_cbs--;
807987da915Sopenharmony_ci	cb_pos = cb;
808987da915Sopenharmony_ci	vcn = start_vcn;
809987da915Sopenharmony_ci	start_vcn += cb_clusters;
810987da915Sopenharmony_ci
811987da915Sopenharmony_ci	/* Check whether the compression block is sparse. */
812987da915Sopenharmony_ci	rl = ntfs_attr_find_vcn(na, vcn);
813987da915Sopenharmony_ci	if (!rl || rl->lcn < LCN_HOLE) {
814987da915Sopenharmony_ci		free(cb);
815987da915Sopenharmony_ci		free(dest);
816987da915Sopenharmony_ci		if (total)
817987da915Sopenharmony_ci			return total;
818987da915Sopenharmony_ci		/* FIXME: Do we want EIO or the error code? (AIA) */
819987da915Sopenharmony_ci		errno = EIO;
820987da915Sopenharmony_ci		return -1;
821987da915Sopenharmony_ci	}
822987da915Sopenharmony_ci	if (rl->lcn == LCN_HOLE) {
823987da915Sopenharmony_ci		/* Sparse cb, zero out destination range overlapping the cb. */
824987da915Sopenharmony_ci		ntfs_log_debug("Found sparse compression block.\n");
825987da915Sopenharmony_ci		to_read = min(count, cb_size - ofs);
826987da915Sopenharmony_ci		memset(b, 0, to_read);
827987da915Sopenharmony_ci		ofs = 0;
828987da915Sopenharmony_ci		total += to_read;
829987da915Sopenharmony_ci		count -= to_read;
830987da915Sopenharmony_ci		b = (u8*)b + to_read;
831987da915Sopenharmony_ci	} else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) {
832987da915Sopenharmony_ci		s64 tdata_size, tinitialized_size;
833987da915Sopenharmony_ci		/*
834987da915Sopenharmony_ci		 * Uncompressed cb, read it straight into the destination range
835987da915Sopenharmony_ci		 * overlapping the cb.
836987da915Sopenharmony_ci		 */
837987da915Sopenharmony_ci		ntfs_log_debug("Found uncompressed compression block.\n");
838987da915Sopenharmony_ci		/*
839987da915Sopenharmony_ci		 * Read the uncompressed data into the destination buffer.
840987da915Sopenharmony_ci		 * NOTE: We cheat a little bit here by marking the attribute as
841987da915Sopenharmony_ci		 * not compressed in the ntfs_attr structure so that we can
842987da915Sopenharmony_ci		 * read the data by simply using ntfs_attr_pread().  (-8
843987da915Sopenharmony_ci		 * NOTE: we have to modify data_size and initialized_size
844987da915Sopenharmony_ci		 * temporarily as well...
845987da915Sopenharmony_ci		 */
846987da915Sopenharmony_ci		to_read = min(count, cb_size - ofs);
847987da915Sopenharmony_ci		ofs += vcn << vol->cluster_size_bits;
848987da915Sopenharmony_ci		NAttrClearCompressed(na);
849987da915Sopenharmony_ci		na->data_flags &= ~ATTR_COMPRESSION_MASK;
850987da915Sopenharmony_ci		tdata_size = na->data_size;
851987da915Sopenharmony_ci		tinitialized_size = na->initialized_size;
852987da915Sopenharmony_ci		na->data_size = na->initialized_size = na->allocated_size;
853987da915Sopenharmony_ci		do {
854987da915Sopenharmony_ci			br = ntfs_attr_pread(na, ofs, to_read, b);
855987da915Sopenharmony_ci			if (br <= 0) {
856987da915Sopenharmony_ci				if (!br) {
857987da915Sopenharmony_ci					ntfs_log_error("Failed to read an"
858987da915Sopenharmony_ci						" uncompressed cluster,"
859987da915Sopenharmony_ci						" inode %lld offs 0x%llx\n",
860987da915Sopenharmony_ci						(long long)na->ni->mft_no,
861987da915Sopenharmony_ci						(long long)ofs);
862987da915Sopenharmony_ci					errno = EIO;
863987da915Sopenharmony_ci				}
864987da915Sopenharmony_ci				err = errno;
865987da915Sopenharmony_ci				na->data_size = tdata_size;
866987da915Sopenharmony_ci				na->initialized_size = tinitialized_size;
867987da915Sopenharmony_ci				na->ni->flags |= compression;
868987da915Sopenharmony_ci				na->data_flags = data_flags;
869987da915Sopenharmony_ci				free(cb);
870987da915Sopenharmony_ci				free(dest);
871987da915Sopenharmony_ci				if (total)
872987da915Sopenharmony_ci					return total;
873987da915Sopenharmony_ci				errno = err;
874987da915Sopenharmony_ci				return br;
875987da915Sopenharmony_ci			}
876987da915Sopenharmony_ci			total += br;
877987da915Sopenharmony_ci			count -= br;
878987da915Sopenharmony_ci			b = (u8*)b + br;
879987da915Sopenharmony_ci			to_read -= br;
880987da915Sopenharmony_ci			ofs += br;
881987da915Sopenharmony_ci		} while (to_read > 0);
882987da915Sopenharmony_ci		na->data_size = tdata_size;
883987da915Sopenharmony_ci		na->initialized_size = tinitialized_size;
884987da915Sopenharmony_ci		na->ni->flags |= compression;
885987da915Sopenharmony_ci		na->data_flags = data_flags;
886987da915Sopenharmony_ci		ofs = 0;
887987da915Sopenharmony_ci	} else {
888987da915Sopenharmony_ci		s64 tdata_size, tinitialized_size;
889987da915Sopenharmony_ci		u32 decompsz;
890987da915Sopenharmony_ci
891987da915Sopenharmony_ci		/*
892987da915Sopenharmony_ci		 * Compressed cb, decompress it into the temporary buffer, then
893987da915Sopenharmony_ci		 * copy the data to the destination range overlapping the cb.
894987da915Sopenharmony_ci		 */
895987da915Sopenharmony_ci		ntfs_log_debug("Found compressed compression block.\n");
896987da915Sopenharmony_ci		/*
897987da915Sopenharmony_ci		 * Read the compressed data into the temporary buffer.
898987da915Sopenharmony_ci		 * NOTE: We cheat a little bit here by marking the attribute as
899987da915Sopenharmony_ci		 * not compressed in the ntfs_attr structure so that we can
900987da915Sopenharmony_ci		 * read the raw, compressed data by simply using
901987da915Sopenharmony_ci		 * ntfs_attr_pread().  (-8
902987da915Sopenharmony_ci		 * NOTE: We have to modify data_size and initialized_size
903987da915Sopenharmony_ci		 * temporarily as well...
904987da915Sopenharmony_ci		 */
905987da915Sopenharmony_ci		to_read = cb_size;
906987da915Sopenharmony_ci		NAttrClearCompressed(na);
907987da915Sopenharmony_ci		na->data_flags &= ~ATTR_COMPRESSION_MASK;
908987da915Sopenharmony_ci		tdata_size = na->data_size;
909987da915Sopenharmony_ci		tinitialized_size = na->initialized_size;
910987da915Sopenharmony_ci		na->data_size = na->initialized_size = na->allocated_size;
911987da915Sopenharmony_ci		do {
912987da915Sopenharmony_ci			br = ntfs_attr_pread(na,
913987da915Sopenharmony_ci					(vcn << vol->cluster_size_bits) +
914987da915Sopenharmony_ci					(cb_pos - cb), to_read, cb_pos);
915987da915Sopenharmony_ci			if (br <= 0) {
916987da915Sopenharmony_ci				if (!br) {
917987da915Sopenharmony_ci					ntfs_log_error("Failed to read a"
918987da915Sopenharmony_ci						" compressed cluster, "
919987da915Sopenharmony_ci						" inode %lld offs 0x%llx\n",
920987da915Sopenharmony_ci						(long long)na->ni->mft_no,
921987da915Sopenharmony_ci						(long long)(vcn << vol->cluster_size_bits));
922987da915Sopenharmony_ci					errno = EIO;
923987da915Sopenharmony_ci				}
924987da915Sopenharmony_ci				err = errno;
925987da915Sopenharmony_ci				na->data_size = tdata_size;
926987da915Sopenharmony_ci				na->initialized_size = tinitialized_size;
927987da915Sopenharmony_ci				na->ni->flags |= compression;
928987da915Sopenharmony_ci				na->data_flags = data_flags;
929987da915Sopenharmony_ci				free(cb);
930987da915Sopenharmony_ci				free(dest);
931987da915Sopenharmony_ci				if (total)
932987da915Sopenharmony_ci					return total;
933987da915Sopenharmony_ci				errno = err;
934987da915Sopenharmony_ci				return br;
935987da915Sopenharmony_ci			}
936987da915Sopenharmony_ci			cb_pos += br;
937987da915Sopenharmony_ci			to_read -= br;
938987da915Sopenharmony_ci		} while (to_read > 0);
939987da915Sopenharmony_ci		na->data_size = tdata_size;
940987da915Sopenharmony_ci		na->initialized_size = tinitialized_size;
941987da915Sopenharmony_ci		na->ni->flags |= compression;
942987da915Sopenharmony_ci		na->data_flags = data_flags;
943987da915Sopenharmony_ci		/* Just a precaution. */
944987da915Sopenharmony_ci		if (cb_pos + 2 <= cb_end)
945987da915Sopenharmony_ci			*(u16*)cb_pos = 0;
946987da915Sopenharmony_ci		ntfs_log_debug("Successfully read the compression block.\n");
947987da915Sopenharmony_ci		/* Do not decompress beyond the requested block */
948987da915Sopenharmony_ci		to_read = min(count, cb_size - ofs);
949987da915Sopenharmony_ci		decompsz = ((ofs + to_read - 1) | (NTFS_SB_SIZE - 1)) + 1;
950987da915Sopenharmony_ci		if (ntfs_decompress(dest, decompsz, cb, cb_size) < 0) {
951987da915Sopenharmony_ci			err = errno;
952987da915Sopenharmony_ci			free(cb);
953987da915Sopenharmony_ci			free(dest);
954987da915Sopenharmony_ci			if (total)
955987da915Sopenharmony_ci				return total;
956987da915Sopenharmony_ci			errno = err;
957987da915Sopenharmony_ci			return -1;
958987da915Sopenharmony_ci		}
959987da915Sopenharmony_ci		memcpy(b, dest + ofs, to_read);
960987da915Sopenharmony_ci		total += to_read;
961987da915Sopenharmony_ci		count -= to_read;
962987da915Sopenharmony_ci		b = (u8*)b + to_read;
963987da915Sopenharmony_ci		ofs = 0;
964987da915Sopenharmony_ci	}
965987da915Sopenharmony_ci	/* Do we have more work to do? */
966987da915Sopenharmony_ci	if (nr_cbs)
967987da915Sopenharmony_ci		goto do_next_cb;
968987da915Sopenharmony_ci	/* We no longer need the buffers. */
969987da915Sopenharmony_ci	free(cb);
970987da915Sopenharmony_ci	free(dest);
971987da915Sopenharmony_ci	/* Return number of bytes read. */
972987da915Sopenharmony_ci	return total + total2;
973987da915Sopenharmony_ci}
974987da915Sopenharmony_ci
975987da915Sopenharmony_ci/*
976987da915Sopenharmony_ci *		Read data from a set of clusters
977987da915Sopenharmony_ci *
978987da915Sopenharmony_ci *	Returns the amount of data read
979987da915Sopenharmony_ci */
980987da915Sopenharmony_ci
981987da915Sopenharmony_cistatic u32 read_clusters(ntfs_volume *vol, const runlist_element *rl,
982987da915Sopenharmony_ci			s64 offs, u32 to_read, char *inbuf)
983987da915Sopenharmony_ci{
984987da915Sopenharmony_ci	u32 count;
985987da915Sopenharmony_ci	int xgot;
986987da915Sopenharmony_ci	u32 got;
987987da915Sopenharmony_ci	s64 xpos;
988987da915Sopenharmony_ci	BOOL first;
989987da915Sopenharmony_ci	char *xinbuf;
990987da915Sopenharmony_ci	const runlist_element *xrl;
991987da915Sopenharmony_ci
992987da915Sopenharmony_ci	got = 0;
993987da915Sopenharmony_ci	xrl = rl;
994987da915Sopenharmony_ci	xinbuf = inbuf;
995987da915Sopenharmony_ci	first = TRUE;
996987da915Sopenharmony_ci	do {
997987da915Sopenharmony_ci		count = xrl->length << vol->cluster_size_bits;
998987da915Sopenharmony_ci		xpos = xrl->lcn << vol->cluster_size_bits;
999987da915Sopenharmony_ci		if (first) {
1000987da915Sopenharmony_ci			count -= offs;
1001987da915Sopenharmony_ci			xpos += offs;
1002987da915Sopenharmony_ci		}
1003987da915Sopenharmony_ci		if ((to_read - got) < count)
1004987da915Sopenharmony_ci			count = to_read - got;
1005987da915Sopenharmony_ci		xgot = ntfs_pread(vol->dev, xpos, count, xinbuf);
1006987da915Sopenharmony_ci		if (xgot == (int)count) {
1007987da915Sopenharmony_ci			got += count;
1008987da915Sopenharmony_ci			xpos += count;
1009987da915Sopenharmony_ci			xinbuf += count;
1010987da915Sopenharmony_ci			xrl++;
1011987da915Sopenharmony_ci		}
1012987da915Sopenharmony_ci		first = FALSE;
1013987da915Sopenharmony_ci	} while ((xgot == (int)count) && (got < to_read));
1014987da915Sopenharmony_ci	return (got);
1015987da915Sopenharmony_ci}
1016987da915Sopenharmony_ci
1017987da915Sopenharmony_ci/*
1018987da915Sopenharmony_ci *		Write data to a set of clusters
1019987da915Sopenharmony_ci *
1020987da915Sopenharmony_ci *	Returns the amount of data written
1021987da915Sopenharmony_ci */
1022987da915Sopenharmony_ci
1023987da915Sopenharmony_cistatic s32 write_clusters(ntfs_volume *vol, const runlist_element *rl,
1024987da915Sopenharmony_ci			s64 offs, s32 to_write, const char *outbuf)
1025987da915Sopenharmony_ci{
1026987da915Sopenharmony_ci	s32 count;
1027987da915Sopenharmony_ci	s32 put, xput;
1028987da915Sopenharmony_ci	s64 xpos;
1029987da915Sopenharmony_ci	BOOL first;
1030987da915Sopenharmony_ci	const char *xoutbuf;
1031987da915Sopenharmony_ci	const runlist_element *xrl;
1032987da915Sopenharmony_ci
1033987da915Sopenharmony_ci	put = 0;
1034987da915Sopenharmony_ci	xrl = rl;
1035987da915Sopenharmony_ci	xoutbuf = outbuf;
1036987da915Sopenharmony_ci	first = TRUE;
1037987da915Sopenharmony_ci	do {
1038987da915Sopenharmony_ci		count = xrl->length << vol->cluster_size_bits;
1039987da915Sopenharmony_ci		xpos = xrl->lcn << vol->cluster_size_bits;
1040987da915Sopenharmony_ci		if (first) {
1041987da915Sopenharmony_ci			count -= offs;
1042987da915Sopenharmony_ci			xpos += offs;
1043987da915Sopenharmony_ci		}
1044987da915Sopenharmony_ci		if ((to_write - put) < count)
1045987da915Sopenharmony_ci			count = to_write - put;
1046987da915Sopenharmony_ci		xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf);
1047987da915Sopenharmony_ci		if (xput == count) {
1048987da915Sopenharmony_ci			put += count;
1049987da915Sopenharmony_ci			xpos += count;
1050987da915Sopenharmony_ci			xoutbuf += count;
1051987da915Sopenharmony_ci			xrl++;
1052987da915Sopenharmony_ci		}
1053987da915Sopenharmony_ci		first = FALSE;
1054987da915Sopenharmony_ci	} while ((xput == count) && (put < to_write));
1055987da915Sopenharmony_ci	return (put);
1056987da915Sopenharmony_ci}
1057987da915Sopenharmony_ci
1058987da915Sopenharmony_ci
1059987da915Sopenharmony_ci/*
1060987da915Sopenharmony_ci *		Compress and write a set of blocks
1061987da915Sopenharmony_ci *
1062987da915Sopenharmony_ci *	returns the size actually written (rounded to a full cluster)
1063987da915Sopenharmony_ci *		or 0 if all zeroes (nothing is written)
1064987da915Sopenharmony_ci *		or -1 if could not compress (nothing is written)
1065987da915Sopenharmony_ci *		or -2 if there were an irrecoverable error (errno set)
1066987da915Sopenharmony_ci */
1067987da915Sopenharmony_ci
1068987da915Sopenharmony_cistatic s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
1069987da915Sopenharmony_ci			s64 offs, u32 insz, const char *inbuf)
1070987da915Sopenharmony_ci{
1071987da915Sopenharmony_ci	ntfs_volume *vol;
1072987da915Sopenharmony_ci	char *outbuf;
1073987da915Sopenharmony_ci	char *pbuf;
1074987da915Sopenharmony_ci	u32 compsz;
1075987da915Sopenharmony_ci	s32 written;
1076987da915Sopenharmony_ci	s32 rounded;
1077987da915Sopenharmony_ci	unsigned int clsz;
1078987da915Sopenharmony_ci	u32 p;
1079987da915Sopenharmony_ci	unsigned int sz;
1080987da915Sopenharmony_ci	unsigned int bsz;
1081987da915Sopenharmony_ci	BOOL fail;
1082987da915Sopenharmony_ci	BOOL allzeroes;
1083987da915Sopenharmony_ci		/* a single compressed zero */
1084987da915Sopenharmony_ci	static char onezero[] = { 0x01, 0xb0, 0x00, 0x00 } ;
1085987da915Sopenharmony_ci		/* a couple of compressed zeroes */
1086987da915Sopenharmony_ci	static char twozeroes[] = { 0x02, 0xb0, 0x00, 0x00, 0x00 } ;
1087987da915Sopenharmony_ci		/* more compressed zeroes, to be followed by some count */
1088987da915Sopenharmony_ci	static char morezeroes[] = { 0x03, 0xb0, 0x02, 0x00 } ;
1089987da915Sopenharmony_ci
1090987da915Sopenharmony_ci	vol = na->ni->vol;
1091987da915Sopenharmony_ci	written = -1; /* default return */
1092987da915Sopenharmony_ci	clsz = 1 << vol->cluster_size_bits;
1093987da915Sopenharmony_ci		/* may need 2 extra bytes per block and 2 more bytes */
1094987da915Sopenharmony_ci	outbuf = (char*)ntfs_malloc(na->compression_block_size
1095987da915Sopenharmony_ci			+ 2*(na->compression_block_size/NTFS_SB_SIZE)
1096987da915Sopenharmony_ci			+ 2);
1097987da915Sopenharmony_ci	if (outbuf) {
1098987da915Sopenharmony_ci		fail = FALSE;
1099987da915Sopenharmony_ci		compsz = 0;
1100987da915Sopenharmony_ci		allzeroes = TRUE;
1101987da915Sopenharmony_ci		for (p=0; (p<insz) && !fail; p+=NTFS_SB_SIZE) {
1102987da915Sopenharmony_ci			if ((p + NTFS_SB_SIZE) < insz)
1103987da915Sopenharmony_ci				bsz = NTFS_SB_SIZE;
1104987da915Sopenharmony_ci			else
1105987da915Sopenharmony_ci				bsz = insz - p;
1106987da915Sopenharmony_ci			pbuf = &outbuf[compsz];
1107987da915Sopenharmony_ci			sz = ntfs_compress_block(&inbuf[p],bsz,pbuf);
1108987da915Sopenharmony_ci			/* fail if all the clusters (or more) are needed */
1109987da915Sopenharmony_ci			if (!sz || ((compsz + sz + clsz + 2)
1110987da915Sopenharmony_ci					 > na->compression_block_size))
1111987da915Sopenharmony_ci				fail = TRUE;
1112987da915Sopenharmony_ci			else {
1113987da915Sopenharmony_ci				if (allzeroes) {
1114987da915Sopenharmony_ci				/* check whether this is all zeroes */
1115987da915Sopenharmony_ci					switch (sz) {
1116987da915Sopenharmony_ci					case 4 :
1117987da915Sopenharmony_ci						allzeroes = !memcmp(
1118987da915Sopenharmony_ci							pbuf,onezero,4);
1119987da915Sopenharmony_ci						break;
1120987da915Sopenharmony_ci					case 5 :
1121987da915Sopenharmony_ci						allzeroes = !memcmp(
1122987da915Sopenharmony_ci							pbuf,twozeroes,5);
1123987da915Sopenharmony_ci						break;
1124987da915Sopenharmony_ci					case 6 :
1125987da915Sopenharmony_ci						allzeroes = !memcmp(
1126987da915Sopenharmony_ci							pbuf,morezeroes,4);
1127987da915Sopenharmony_ci						break;
1128987da915Sopenharmony_ci					default :
1129987da915Sopenharmony_ci						allzeroes = FALSE;
1130987da915Sopenharmony_ci						break;
1131987da915Sopenharmony_ci					}
1132987da915Sopenharmony_ci				}
1133987da915Sopenharmony_ci			compsz += sz;
1134987da915Sopenharmony_ci			}
1135987da915Sopenharmony_ci		}
1136987da915Sopenharmony_ci		if (!fail && !allzeroes) {
1137987da915Sopenharmony_ci			/* add a couple of null bytes, space has been checked */
1138987da915Sopenharmony_ci			outbuf[compsz++] = 0;
1139987da915Sopenharmony_ci			outbuf[compsz++] = 0;
1140987da915Sopenharmony_ci			/* write a full cluster, to avoid partial reading */
1141987da915Sopenharmony_ci			rounded = ((compsz - 1) | (clsz - 1)) + 1;
1142987da915Sopenharmony_ci			memset(&outbuf[compsz], 0, rounded - compsz);
1143987da915Sopenharmony_ci			written = write_clusters(vol, rl, offs, rounded, outbuf);
1144987da915Sopenharmony_ci			if (written != rounded) {
1145987da915Sopenharmony_ci				/*
1146987da915Sopenharmony_ci				 * TODO : previously written text has been
1147987da915Sopenharmony_ci				 * spoilt, should return a specific error
1148987da915Sopenharmony_ci				 */
1149987da915Sopenharmony_ci				ntfs_log_error("error writing compressed data\n");
1150987da915Sopenharmony_ci				errno = EIO;
1151987da915Sopenharmony_ci				written = -2;
1152987da915Sopenharmony_ci			}
1153987da915Sopenharmony_ci		} else
1154987da915Sopenharmony_ci			if (!fail)
1155987da915Sopenharmony_ci				written = 0;
1156987da915Sopenharmony_ci		free(outbuf);
1157987da915Sopenharmony_ci	}
1158987da915Sopenharmony_ci	return (written);
1159987da915Sopenharmony_ci}
1160987da915Sopenharmony_ci
1161987da915Sopenharmony_ci/*
1162987da915Sopenharmony_ci *		Check the validity of a compressed runlist
1163987da915Sopenharmony_ci *	The check starts at the beginning of current run and ends
1164987da915Sopenharmony_ci *	at the end of runlist
1165987da915Sopenharmony_ci *	errno is set if the runlist is not valid
1166987da915Sopenharmony_ci */
1167987da915Sopenharmony_ci
1168987da915Sopenharmony_cistatic BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl,
1169987da915Sopenharmony_ci			BOOL fullcheck, const char *text)
1170987da915Sopenharmony_ci{
1171987da915Sopenharmony_ci	runlist_element *xrl;
1172987da915Sopenharmony_ci	const char *err;
1173987da915Sopenharmony_ci	BOOL ok = TRUE;
1174987da915Sopenharmony_ci
1175987da915Sopenharmony_ci	xrl = rl;
1176987da915Sopenharmony_ci	while (xrl->vcn & (na->compression_block_clusters - 1))
1177987da915Sopenharmony_ci		xrl--;
1178987da915Sopenharmony_ci	err = (const char*)NULL;
1179987da915Sopenharmony_ci	while (xrl->length) {
1180987da915Sopenharmony_ci		if ((xrl->vcn + xrl->length) != xrl[1].vcn)
1181987da915Sopenharmony_ci			err = "Runs not adjacent";
1182987da915Sopenharmony_ci		if (xrl->lcn == LCN_HOLE) {
1183987da915Sopenharmony_ci			if ((xrl->vcn + xrl->length)
1184987da915Sopenharmony_ci			    & (na->compression_block_clusters - 1)) {
1185987da915Sopenharmony_ci				err = "Invalid hole";
1186987da915Sopenharmony_ci			}
1187987da915Sopenharmony_ci			if (fullcheck && (xrl[1].lcn == LCN_HOLE)) {
1188987da915Sopenharmony_ci				err = "Adjacent holes";
1189987da915Sopenharmony_ci			}
1190987da915Sopenharmony_ci		}
1191987da915Sopenharmony_ci		if (err) {
1192987da915Sopenharmony_ci			ntfs_log_error("%s at %s index %ld inode %lld\n",
1193987da915Sopenharmony_ci				err, text, (long)(xrl - na->rl),
1194987da915Sopenharmony_ci				(long long)na->ni->mft_no);
1195987da915Sopenharmony_ci			errno = EIO;
1196987da915Sopenharmony_ci			ok = FALSE;
1197987da915Sopenharmony_ci			err = (const char*)NULL;
1198987da915Sopenharmony_ci		}
1199987da915Sopenharmony_ci		xrl++;
1200987da915Sopenharmony_ci	}
1201987da915Sopenharmony_ci	return (ok);
1202987da915Sopenharmony_ci}
1203987da915Sopenharmony_ci
1204987da915Sopenharmony_ci/*
1205987da915Sopenharmony_ci *		Free unneeded clusters after overwriting compressed data
1206987da915Sopenharmony_ci *
1207987da915Sopenharmony_ci *	This generally requires one or two empty slots at the end of runlist,
1208987da915Sopenharmony_ci *	but we do not want to reallocate the runlist here because
1209987da915Sopenharmony_ci *	there are many pointers to it.
1210987da915Sopenharmony_ci *	So the empty slots have to be reserved beforehand
1211987da915Sopenharmony_ci *
1212987da915Sopenharmony_ci *	Returns zero unless some error occurred (described by errno)
1213987da915Sopenharmony_ci *
1214987da915Sopenharmony_ci *         +======= start of block =====+
1215987da915Sopenharmony_ci *      0  |A     chunk may overflow    | <-- rl         usedcnt : A + B
1216987da915Sopenharmony_ci *         |A     on previous block     |                        then B
1217987da915Sopenharmony_ci *         |A                           |
1218987da915Sopenharmony_ci *         +-- end of allocated chunk --+                freelength : C
1219987da915Sopenharmony_ci *         |B                           |                      (incl overflow)
1220987da915Sopenharmony_ci *         +== end of compressed data ==+
1221987da915Sopenharmony_ci *         |C                           | <-- freerl     freecnt : C + D
1222987da915Sopenharmony_ci *         |C     chunk may overflow    |
1223987da915Sopenharmony_ci *         |C     on next block         |
1224987da915Sopenharmony_ci *         +-- end of allocated chunk --+
1225987da915Sopenharmony_ci *         |D                           |
1226987da915Sopenharmony_ci *         |D     chunk may overflow    |
1227987da915Sopenharmony_ci *     15  |D     on next block         |
1228987da915Sopenharmony_ci *         +======== end of block ======+
1229987da915Sopenharmony_ci *
1230987da915Sopenharmony_ci */
1231987da915Sopenharmony_ci
1232987da915Sopenharmony_cistatic int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl,
1233987da915Sopenharmony_ci			s32 usedcnt, s32 freecnt, VCN *update_from)
1234987da915Sopenharmony_ci{
1235987da915Sopenharmony_ci	BOOL beginhole;
1236987da915Sopenharmony_ci	BOOL mergeholes;
1237987da915Sopenharmony_ci	s32 oldlength;
1238987da915Sopenharmony_ci	s32 freelength;
1239987da915Sopenharmony_ci	s64 freelcn;
1240987da915Sopenharmony_ci	s64 freevcn;
1241987da915Sopenharmony_ci	runlist_element *freerl;
1242987da915Sopenharmony_ci	ntfs_volume *vol;
1243987da915Sopenharmony_ci	s32 carry;
1244987da915Sopenharmony_ci	int res;
1245987da915Sopenharmony_ci
1246987da915Sopenharmony_ci	vol = na->ni->vol;
1247987da915Sopenharmony_ci	res = 0;
1248987da915Sopenharmony_ci	freelcn = rl->lcn + usedcnt;
1249987da915Sopenharmony_ci	freevcn = rl->vcn + usedcnt;
1250987da915Sopenharmony_ci	freelength = rl->length - usedcnt;
1251987da915Sopenharmony_ci	beginhole = !usedcnt && !rl->vcn;
1252987da915Sopenharmony_ci		/* can merge with hole before ? */
1253987da915Sopenharmony_ci	mergeholes = !usedcnt
1254987da915Sopenharmony_ci			&& rl[0].vcn
1255987da915Sopenharmony_ci			&& (rl[-1].lcn == LCN_HOLE);
1256987da915Sopenharmony_ci		/* truncate current run, carry to subsequent hole */
1257987da915Sopenharmony_ci	carry = freelength;
1258987da915Sopenharmony_ci	oldlength = rl->length;
1259987da915Sopenharmony_ci	if (mergeholes) {
1260987da915Sopenharmony_ci			/* merging with a hole before */
1261987da915Sopenharmony_ci		freerl = rl;
1262987da915Sopenharmony_ci	} else {
1263987da915Sopenharmony_ci		rl->length -= freelength; /* warning : can be zero */
1264987da915Sopenharmony_ci		freerl = ++rl;
1265987da915Sopenharmony_ci	}
1266987da915Sopenharmony_ci	if (!mergeholes && (usedcnt || beginhole)) {
1267987da915Sopenharmony_ci		s32 freed;
1268987da915Sopenharmony_ci		runlist_element *frl;
1269987da915Sopenharmony_ci		runlist_element *erl;
1270987da915Sopenharmony_ci		int holes = 0;
1271987da915Sopenharmony_ci		BOOL threeparts;
1272987da915Sopenharmony_ci
1273987da915Sopenharmony_ci		/* free the unneeded clusters from initial run, then freerl */
1274987da915Sopenharmony_ci		threeparts = (freelength > freecnt);
1275987da915Sopenharmony_ci		freed = 0;
1276987da915Sopenharmony_ci		frl = freerl;
1277987da915Sopenharmony_ci		if (freelength) {
1278987da915Sopenharmony_ci      			res = ntfs_cluster_free_basic(vol,freelcn,
1279987da915Sopenharmony_ci				(threeparts ? freecnt : freelength));
1280987da915Sopenharmony_ci			if (!res)
1281987da915Sopenharmony_ci				freed += (threeparts ? freecnt : freelength);
1282987da915Sopenharmony_ci			if (!usedcnt) {
1283987da915Sopenharmony_ci				holes++;
1284987da915Sopenharmony_ci				freerl--;
1285987da915Sopenharmony_ci				freerl->length += (threeparts
1286987da915Sopenharmony_ci						? freecnt : freelength);
1287987da915Sopenharmony_ci				if (freerl->vcn < *update_from)
1288987da915Sopenharmony_ci					*update_from = freerl->vcn;
1289987da915Sopenharmony_ci			}
1290987da915Sopenharmony_ci   		}
1291987da915Sopenharmony_ci   		while (!res && frl->length && (freed < freecnt)) {
1292987da915Sopenharmony_ci      			if (frl->length <= (freecnt - freed)) {
1293987da915Sopenharmony_ci         			res = ntfs_cluster_free_basic(vol, frl->lcn,
1294987da915Sopenharmony_ci						frl->length);
1295987da915Sopenharmony_ci				if (!res) {
1296987da915Sopenharmony_ci         				freed += frl->length;
1297987da915Sopenharmony_ci         				frl->lcn = LCN_HOLE;
1298987da915Sopenharmony_ci					frl->length += carry;
1299987da915Sopenharmony_ci					carry = 0;
1300987da915Sopenharmony_ci         				holes++;
1301987da915Sopenharmony_ci				}
1302987da915Sopenharmony_ci      			} else {
1303987da915Sopenharmony_ci         			res = ntfs_cluster_free_basic(vol, frl->lcn,
1304987da915Sopenharmony_ci						freecnt - freed);
1305987da915Sopenharmony_ci				if (!res) {
1306987da915Sopenharmony_ci         				frl->lcn += freecnt - freed;
1307987da915Sopenharmony_ci         				frl->vcn += freecnt - freed;
1308987da915Sopenharmony_ci         				frl->length -= freecnt - freed;
1309987da915Sopenharmony_ci         				freed = freecnt;
1310987da915Sopenharmony_ci				}
1311987da915Sopenharmony_ci      			}
1312987da915Sopenharmony_ci      			frl++;
1313987da915Sopenharmony_ci   		}
1314987da915Sopenharmony_ci		na->compressed_size -= freed << vol->cluster_size_bits;
1315987da915Sopenharmony_ci		switch (holes) {
1316987da915Sopenharmony_ci		case 0 :
1317987da915Sopenharmony_ci			/* there are no hole, must insert one */
1318987da915Sopenharmony_ci			/* space for hole has been prereserved */
1319987da915Sopenharmony_ci			if (freerl->lcn == LCN_HOLE) {
1320987da915Sopenharmony_ci				if (threeparts) {
1321987da915Sopenharmony_ci					erl = freerl;
1322987da915Sopenharmony_ci					while (erl->length)
1323987da915Sopenharmony_ci						erl++;
1324987da915Sopenharmony_ci					do {
1325987da915Sopenharmony_ci						erl[2] = *erl;
1326987da915Sopenharmony_ci					} while (erl-- != freerl);
1327987da915Sopenharmony_ci
1328987da915Sopenharmony_ci					freerl[1].length = freelength - freecnt;
1329987da915Sopenharmony_ci					freerl->length = freecnt;
1330987da915Sopenharmony_ci					freerl[1].lcn = freelcn + freecnt;
1331987da915Sopenharmony_ci					freerl[1].vcn = freevcn + freecnt;
1332987da915Sopenharmony_ci					freerl[2].lcn = LCN_HOLE;
1333987da915Sopenharmony_ci					freerl[2].vcn = freerl[1].vcn
1334987da915Sopenharmony_ci							+ freerl[1].length;
1335987da915Sopenharmony_ci					freerl->vcn = freevcn;
1336987da915Sopenharmony_ci				} else {
1337987da915Sopenharmony_ci					freerl->vcn = freevcn;
1338987da915Sopenharmony_ci					freerl->length += freelength;
1339987da915Sopenharmony_ci				}
1340987da915Sopenharmony_ci			} else {
1341987da915Sopenharmony_ci				erl = freerl;
1342987da915Sopenharmony_ci				while (erl->length)
1343987da915Sopenharmony_ci					erl++;
1344987da915Sopenharmony_ci				if (threeparts) {
1345987da915Sopenharmony_ci					do {
1346987da915Sopenharmony_ci						erl[2] = *erl;
1347987da915Sopenharmony_ci					} while (erl-- != freerl);
1348987da915Sopenharmony_ci					freerl[1].lcn = freelcn + freecnt;
1349987da915Sopenharmony_ci					freerl[1].vcn = freevcn + freecnt;
1350987da915Sopenharmony_ci					freerl[1].length = oldlength - usedcnt - freecnt;
1351987da915Sopenharmony_ci				} else {
1352987da915Sopenharmony_ci					do {
1353987da915Sopenharmony_ci						erl[1] = *erl;
1354987da915Sopenharmony_ci					} while (erl-- != freerl);
1355987da915Sopenharmony_ci				}
1356987da915Sopenharmony_ci				freerl->lcn = LCN_HOLE;
1357987da915Sopenharmony_ci				freerl->vcn = freevcn;
1358987da915Sopenharmony_ci				freerl->length = freecnt;
1359987da915Sopenharmony_ci			}
1360987da915Sopenharmony_ci			break;
1361987da915Sopenharmony_ci		case 1 :
1362987da915Sopenharmony_ci			/* there is a single hole, may have to merge */
1363987da915Sopenharmony_ci			freerl->vcn = freevcn;
1364987da915Sopenharmony_ci			freerl->length = freecnt;
1365987da915Sopenharmony_ci			if (freerl[1].lcn == LCN_HOLE) {
1366987da915Sopenharmony_ci				freerl->length += freerl[1].length;
1367987da915Sopenharmony_ci				erl = freerl;
1368987da915Sopenharmony_ci				do {
1369987da915Sopenharmony_ci					erl++;
1370987da915Sopenharmony_ci					*erl = erl[1];
1371987da915Sopenharmony_ci				} while (erl->length);
1372987da915Sopenharmony_ci			}
1373987da915Sopenharmony_ci			break;
1374987da915Sopenharmony_ci		default :
1375987da915Sopenharmony_ci			/* there were several holes, must merge them */
1376987da915Sopenharmony_ci			freerl->lcn = LCN_HOLE;
1377987da915Sopenharmony_ci			freerl->vcn = freevcn;
1378987da915Sopenharmony_ci			freerl->length = freecnt;
1379987da915Sopenharmony_ci			if (freerl[holes].lcn == LCN_HOLE) {
1380987da915Sopenharmony_ci				freerl->length += freerl[holes].length;
1381987da915Sopenharmony_ci				holes++;
1382987da915Sopenharmony_ci			}
1383987da915Sopenharmony_ci			erl = freerl;
1384987da915Sopenharmony_ci			do {
1385987da915Sopenharmony_ci				erl++;
1386987da915Sopenharmony_ci				*erl = erl[holes - 1];
1387987da915Sopenharmony_ci			} while (erl->length);
1388987da915Sopenharmony_ci			break;
1389987da915Sopenharmony_ci		}
1390987da915Sopenharmony_ci	} else {
1391987da915Sopenharmony_ci		s32 freed;
1392987da915Sopenharmony_ci		runlist_element *frl;
1393987da915Sopenharmony_ci		runlist_element *xrl;
1394987da915Sopenharmony_ci
1395987da915Sopenharmony_ci		freed = 0;
1396987da915Sopenharmony_ci		frl = freerl--;
1397987da915Sopenharmony_ci		if (freerl->vcn < *update_from)
1398987da915Sopenharmony_ci			*update_from = freerl->vcn;
1399987da915Sopenharmony_ci		while (!res && frl->length && (freed < freecnt)) {
1400987da915Sopenharmony_ci			if (frl->length <= (freecnt - freed)) {
1401987da915Sopenharmony_ci				freerl->length += frl->length;
1402987da915Sopenharmony_ci				freed += frl->length;
1403987da915Sopenharmony_ci				res = ntfs_cluster_free_basic(vol, frl->lcn,
1404987da915Sopenharmony_ci						frl->length);
1405987da915Sopenharmony_ci				frl++;
1406987da915Sopenharmony_ci			} else {
1407987da915Sopenharmony_ci				freerl->length += freecnt - freed;
1408987da915Sopenharmony_ci				res = ntfs_cluster_free_basic(vol, frl->lcn,
1409987da915Sopenharmony_ci						freecnt - freed);
1410987da915Sopenharmony_ci				frl->lcn += freecnt - freed;
1411987da915Sopenharmony_ci				frl->vcn += freecnt - freed;
1412987da915Sopenharmony_ci				frl->length -= freecnt - freed;
1413987da915Sopenharmony_ci				freed = freecnt;
1414987da915Sopenharmony_ci			}
1415987da915Sopenharmony_ci		}
1416987da915Sopenharmony_ci			/* remove unneded runlist entries */
1417987da915Sopenharmony_ci		xrl = freerl;
1418987da915Sopenharmony_ci			/* group with next run if also a hole */
1419987da915Sopenharmony_ci		if (frl->length && (frl->lcn == LCN_HOLE)) {
1420987da915Sopenharmony_ci			xrl->length += frl->length;
1421987da915Sopenharmony_ci			frl++;
1422987da915Sopenharmony_ci		}
1423987da915Sopenharmony_ci		while (frl->length) {
1424987da915Sopenharmony_ci			*++xrl = *frl++;
1425987da915Sopenharmony_ci		}
1426987da915Sopenharmony_ci		*++xrl = *frl; /* terminator */
1427987da915Sopenharmony_ci	na->compressed_size -= freed << vol->cluster_size_bits;
1428987da915Sopenharmony_ci	}
1429987da915Sopenharmony_ci	return (res);
1430987da915Sopenharmony_ci}
1431987da915Sopenharmony_ci
1432987da915Sopenharmony_ci
1433987da915Sopenharmony_ci/*
1434987da915Sopenharmony_ci *		Free unneeded clusters after compression
1435987da915Sopenharmony_ci *
1436987da915Sopenharmony_ci *	This generally requires one or two empty slots at the end of runlist,
1437987da915Sopenharmony_ci *	but we do not want to reallocate the runlist here because
1438987da915Sopenharmony_ci *	there are many pointers to it.
1439987da915Sopenharmony_ci *	So the empty slots have to be reserved beforehand
1440987da915Sopenharmony_ci *
1441987da915Sopenharmony_ci *	Returns zero unless some error occurred (described by errno)
1442987da915Sopenharmony_ci */
1443987da915Sopenharmony_ci
1444987da915Sopenharmony_cistatic int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
1445987da915Sopenharmony_ci				s64 used, s64 reserved, BOOL appending,
1446987da915Sopenharmony_ci				VCN *update_from)
1447987da915Sopenharmony_ci{
1448987da915Sopenharmony_ci	s32 freecnt;
1449987da915Sopenharmony_ci	s32 usedcnt;
1450987da915Sopenharmony_ci	int res;
1451987da915Sopenharmony_ci	s64 freelcn;
1452987da915Sopenharmony_ci	s64 freevcn;
1453987da915Sopenharmony_ci	s32 freelength;
1454987da915Sopenharmony_ci	BOOL mergeholes;
1455987da915Sopenharmony_ci	BOOL beginhole;
1456987da915Sopenharmony_ci	ntfs_volume *vol;
1457987da915Sopenharmony_ci	runlist_element *freerl;
1458987da915Sopenharmony_ci
1459987da915Sopenharmony_ci	res = -1; /* default return */
1460987da915Sopenharmony_ci	vol = na->ni->vol;
1461987da915Sopenharmony_ci	freecnt = (reserved - used) >> vol->cluster_size_bits;
1462987da915Sopenharmony_ci	usedcnt = (reserved >> vol->cluster_size_bits) - freecnt;
1463987da915Sopenharmony_ci	if (rl->vcn < *update_from)
1464987da915Sopenharmony_ci		*update_from = rl->vcn;
1465987da915Sopenharmony_ci		/* skip entries fully used, if any */
1466987da915Sopenharmony_ci	while (rl->length && (rl->length < usedcnt)) {
1467987da915Sopenharmony_ci		usedcnt -= rl->length; /* must be > 0 */
1468987da915Sopenharmony_ci		rl++;
1469987da915Sopenharmony_ci	}
1470987da915Sopenharmony_ci	if (rl->length) {
1471987da915Sopenharmony_ci		/*
1472987da915Sopenharmony_ci		 * Splitting the current allocation block requires
1473987da915Sopenharmony_ci		 * an extra runlist element to create the hole.
1474987da915Sopenharmony_ci		 * The required entry has been prereserved when
1475987da915Sopenharmony_ci		 * mapping the runlist.
1476987da915Sopenharmony_ci		 */
1477987da915Sopenharmony_ci			/* get the free part in initial run */
1478987da915Sopenharmony_ci		freelcn = rl->lcn + usedcnt;
1479987da915Sopenharmony_ci		freevcn = rl->vcn + usedcnt;
1480987da915Sopenharmony_ci			/* new count of allocated clusters */
1481987da915Sopenharmony_ci		if (!((freevcn + freecnt)
1482987da915Sopenharmony_ci			    & (na->compression_block_clusters - 1))) {
1483987da915Sopenharmony_ci			if (!appending)
1484987da915Sopenharmony_ci				res = ntfs_compress_overwr_free(na,rl,
1485987da915Sopenharmony_ci						usedcnt,freecnt,update_from);
1486987da915Sopenharmony_ci			else {
1487987da915Sopenharmony_ci				freelength = rl->length - usedcnt;
1488987da915Sopenharmony_ci				beginhole = !usedcnt && !rl->vcn;
1489987da915Sopenharmony_ci				mergeholes = !usedcnt
1490987da915Sopenharmony_ci						&& rl[0].vcn
1491987da915Sopenharmony_ci						&& (rl[-1].lcn == LCN_HOLE);
1492987da915Sopenharmony_ci				if (mergeholes) {
1493987da915Sopenharmony_ci					s32 carry;
1494987da915Sopenharmony_ci
1495987da915Sopenharmony_ci				/* shorten the runs which have free space */
1496987da915Sopenharmony_ci					carry = freecnt;
1497987da915Sopenharmony_ci					freerl = rl;
1498987da915Sopenharmony_ci					while (freerl->length < carry) {
1499987da915Sopenharmony_ci						carry -= freerl->length;
1500987da915Sopenharmony_ci						freerl++;
1501987da915Sopenharmony_ci					}
1502987da915Sopenharmony_ci					freerl->length = carry;
1503987da915Sopenharmony_ci					freerl = rl;
1504987da915Sopenharmony_ci				} else {
1505987da915Sopenharmony_ci					rl->length = usedcnt; /* can be zero ? */
1506987da915Sopenharmony_ci					freerl = ++rl;
1507987da915Sopenharmony_ci				}
1508987da915Sopenharmony_ci				if ((freelength > 0)
1509987da915Sopenharmony_ci				    && !mergeholes
1510987da915Sopenharmony_ci				    && (usedcnt || beginhole)) {
1511987da915Sopenharmony_ci				/*
1512987da915Sopenharmony_ci				 * move the unused part to the end. Doing so,
1513987da915Sopenharmony_ci				 * the vcn will be out of order. This does
1514987da915Sopenharmony_ci				 * not harm, the vcn are meaningless now, and
1515987da915Sopenharmony_ci				 * only the lcn are meaningful for freeing.
1516987da915Sopenharmony_ci				 */
1517987da915Sopenharmony_ci					/* locate current end */
1518987da915Sopenharmony_ci					while (rl->length)
1519987da915Sopenharmony_ci						rl++;
1520987da915Sopenharmony_ci					/* new terminator relocated */
1521987da915Sopenharmony_ci					rl[1].vcn = rl->vcn;
1522987da915Sopenharmony_ci					rl[1].lcn = LCN_ENOENT;
1523987da915Sopenharmony_ci					rl[1].length = 0;
1524987da915Sopenharmony_ci					/* hole, currently allocated */
1525987da915Sopenharmony_ci					rl->vcn = freevcn;
1526987da915Sopenharmony_ci					rl->lcn = freelcn;
1527987da915Sopenharmony_ci					rl->length = freelength;
1528987da915Sopenharmony_ci				} else {
1529987da915Sopenharmony_ci	/* why is this different from the begin hole case ? */
1530987da915Sopenharmony_ci					if ((freelength > 0)
1531987da915Sopenharmony_ci					    && !mergeholes
1532987da915Sopenharmony_ci					    && !usedcnt) {
1533987da915Sopenharmony_ci						freerl--;
1534987da915Sopenharmony_ci						freerl->length = freelength;
1535987da915Sopenharmony_ci						if (freerl->vcn < *update_from)
1536987da915Sopenharmony_ci							*update_from
1537987da915Sopenharmony_ci								= freerl->vcn;
1538987da915Sopenharmony_ci					}
1539987da915Sopenharmony_ci				}
1540987da915Sopenharmony_ci				/* free the hole */
1541987da915Sopenharmony_ci				res = ntfs_cluster_free_from_rl(vol,freerl);
1542987da915Sopenharmony_ci				if (!res) {
1543987da915Sopenharmony_ci					na->compressed_size -= freecnt
1544987da915Sopenharmony_ci						<< vol->cluster_size_bits;
1545987da915Sopenharmony_ci					if (mergeholes) {
1546987da915Sopenharmony_ci						/* merge with adjacent hole */
1547987da915Sopenharmony_ci						freerl--;
1548987da915Sopenharmony_ci						freerl->length += freecnt;
1549987da915Sopenharmony_ci					} else {
1550987da915Sopenharmony_ci						if (beginhole)
1551987da915Sopenharmony_ci							freerl--;
1552987da915Sopenharmony_ci						/* mark hole as free */
1553987da915Sopenharmony_ci						freerl->lcn = LCN_HOLE;
1554987da915Sopenharmony_ci						freerl->vcn = freevcn;
1555987da915Sopenharmony_ci						freerl->length = freecnt;
1556987da915Sopenharmony_ci					}
1557987da915Sopenharmony_ci					if (freerl->vcn < *update_from)
1558987da915Sopenharmony_ci						*update_from = freerl->vcn;
1559987da915Sopenharmony_ci						/* and set up the new end */
1560987da915Sopenharmony_ci					freerl[1].lcn = LCN_ENOENT;
1561987da915Sopenharmony_ci					freerl[1].vcn = freevcn + freecnt;
1562987da915Sopenharmony_ci					freerl[1].length = 0;
1563987da915Sopenharmony_ci				}
1564987da915Sopenharmony_ci			}
1565987da915Sopenharmony_ci		} else {
1566987da915Sopenharmony_ci			ntfs_log_error("Bad end of a compression block set\n");
1567987da915Sopenharmony_ci			errno = EIO;
1568987da915Sopenharmony_ci		}
1569987da915Sopenharmony_ci	} else {
1570987da915Sopenharmony_ci		ntfs_log_error("No cluster to free after compression\n");
1571987da915Sopenharmony_ci		errno = EIO;
1572987da915Sopenharmony_ci	}
1573987da915Sopenharmony_ci	NAttrSetRunlistDirty(na);
1574987da915Sopenharmony_ci	return (res);
1575987da915Sopenharmony_ci}
1576987da915Sopenharmony_ci
1577987da915Sopenharmony_ci/*
1578987da915Sopenharmony_ci *		Read existing data, decompress and append buffer
1579987da915Sopenharmony_ci *	Do nothing if something fails
1580987da915Sopenharmony_ci */
1581987da915Sopenharmony_ci
1582987da915Sopenharmony_cistatic int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
1583987da915Sopenharmony_ci			s64 offs, u32 compsz, s32 pos, BOOL appending,
1584987da915Sopenharmony_ci			char *outbuf, s64 to_write, const void *b)
1585987da915Sopenharmony_ci{
1586987da915Sopenharmony_ci	int fail = 1;
1587987da915Sopenharmony_ci	char *compbuf;
1588987da915Sopenharmony_ci	u32 decompsz;
1589987da915Sopenharmony_ci	u32 got;
1590987da915Sopenharmony_ci
1591987da915Sopenharmony_ci	if (compsz == na->compression_block_size) {
1592987da915Sopenharmony_ci			/* if the full block was requested, it was a hole */
1593987da915Sopenharmony_ci		memset(outbuf,0,compsz);
1594987da915Sopenharmony_ci		memcpy(&outbuf[pos],b,to_write);
1595987da915Sopenharmony_ci		fail = 0;
1596987da915Sopenharmony_ci	} else {
1597987da915Sopenharmony_ci		compbuf = (char*)ntfs_malloc(compsz);
1598987da915Sopenharmony_ci		if (compbuf) {
1599987da915Sopenharmony_ci			/* must align to full block for decompression */
1600987da915Sopenharmony_ci			if (appending)
1601987da915Sopenharmony_ci				decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1;
1602987da915Sopenharmony_ci			else
1603987da915Sopenharmony_ci				decompsz = na->compression_block_size;
1604987da915Sopenharmony_ci			got = read_clusters(na->ni->vol, rl, offs,
1605987da915Sopenharmony_ci					compsz, compbuf);
1606987da915Sopenharmony_ci			if ((got == compsz)
1607987da915Sopenharmony_ci			    && !ntfs_decompress((u8*)outbuf,decompsz,
1608987da915Sopenharmony_ci					(u8*)compbuf,compsz)) {
1609987da915Sopenharmony_ci				memcpy(&outbuf[pos],b,to_write);
1610987da915Sopenharmony_ci				fail = 0;
1611987da915Sopenharmony_ci			}
1612987da915Sopenharmony_ci			free(compbuf);
1613987da915Sopenharmony_ci		}
1614987da915Sopenharmony_ci	}
1615987da915Sopenharmony_ci	return (fail);
1616987da915Sopenharmony_ci}
1617987da915Sopenharmony_ci
1618987da915Sopenharmony_ci/*
1619987da915Sopenharmony_ci *		Flush a full compression block
1620987da915Sopenharmony_ci *
1621987da915Sopenharmony_ci *	returns the size actually written (rounded to a full cluster)
1622987da915Sopenharmony_ci *		or 0 if could not compress (and written uncompressed)
1623987da915Sopenharmony_ci *		or -1 if there were an irrecoverable error (errno set)
1624987da915Sopenharmony_ci */
1625987da915Sopenharmony_ci
1626987da915Sopenharmony_cistatic s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
1627987da915Sopenharmony_ci			char *outbuf, s32 count, BOOL compress,
1628987da915Sopenharmony_ci			BOOL appending, VCN *update_from)
1629987da915Sopenharmony_ci{
1630987da915Sopenharmony_ci	s32 rounded;
1631987da915Sopenharmony_ci	s32 written;
1632987da915Sopenharmony_ci	int clsz;
1633987da915Sopenharmony_ci
1634987da915Sopenharmony_ci	if (compress) {
1635987da915Sopenharmony_ci		written = ntfs_comp_set(na, rl, offs, count, outbuf);
1636987da915Sopenharmony_ci		if (written == -1)
1637987da915Sopenharmony_ci			compress = FALSE;
1638987da915Sopenharmony_ci		if ((written >= 0)
1639987da915Sopenharmony_ci		   && ntfs_compress_free(na,rl,offs + written,
1640987da915Sopenharmony_ci				offs + na->compression_block_size, appending,
1641987da915Sopenharmony_ci				update_from))
1642987da915Sopenharmony_ci			written = -1;
1643987da915Sopenharmony_ci	} else
1644987da915Sopenharmony_ci		written = 0;
1645987da915Sopenharmony_ci	if (!compress) {
1646987da915Sopenharmony_ci		clsz = 1 << na->ni->vol->cluster_size_bits;
1647987da915Sopenharmony_ci		rounded = ((count - 1) | (clsz - 1)) + 1;
1648987da915Sopenharmony_ci		if (rounded > count)
1649987da915Sopenharmony_ci			memset(&outbuf[count], 0, rounded - count);
1650987da915Sopenharmony_ci		written = write_clusters(na->ni->vol, rl,
1651987da915Sopenharmony_ci				offs, rounded, outbuf);
1652987da915Sopenharmony_ci		if (written != rounded)
1653987da915Sopenharmony_ci			written = -1;
1654987da915Sopenharmony_ci	}
1655987da915Sopenharmony_ci	return (written);
1656987da915Sopenharmony_ci}
1657987da915Sopenharmony_ci
1658987da915Sopenharmony_ci/*
1659987da915Sopenharmony_ci *		Write some data to be compressed.
1660987da915Sopenharmony_ci *	Compression only occurs when a few clusters (usually 16) are
1661987da915Sopenharmony_ci *	full. When this occurs an extra runlist slot may be needed, so
1662987da915Sopenharmony_ci *	it has to be reserved beforehand.
1663987da915Sopenharmony_ci *
1664987da915Sopenharmony_ci *	Returns the size of uncompressed data written,
1665987da915Sopenharmony_ci *		or negative if an error occurred.
1666987da915Sopenharmony_ci *	When the returned size is less than requested, new clusters have
1667987da915Sopenharmony_ci *	to be allocated before the function is called again.
1668987da915Sopenharmony_ci */
1669987da915Sopenharmony_ci
1670987da915Sopenharmony_cis64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
1671987da915Sopenharmony_ci				s64 offs, s64 to_write, s64 rounded,
1672987da915Sopenharmony_ci				const void *b, int compressed_part,
1673987da915Sopenharmony_ci				VCN *update_from)
1674987da915Sopenharmony_ci{
1675987da915Sopenharmony_ci	ntfs_volume *vol;
1676987da915Sopenharmony_ci	runlist_element *brl; /* entry containing the beginning of block */
1677987da915Sopenharmony_ci	int compression_length;
1678987da915Sopenharmony_ci	s64 written;
1679987da915Sopenharmony_ci	s64 to_read;
1680987da915Sopenharmony_ci	s64 to_flush;
1681987da915Sopenharmony_ci	s64 roffs;
1682987da915Sopenharmony_ci	s64 got;
1683987da915Sopenharmony_ci	s64 start_vcn;
1684987da915Sopenharmony_ci	s64 nextblock;
1685987da915Sopenharmony_ci	s64 endwrite;
1686987da915Sopenharmony_ci	u32 compsz;
1687987da915Sopenharmony_ci	char *inbuf;
1688987da915Sopenharmony_ci	char *outbuf;
1689987da915Sopenharmony_ci	BOOL fail;
1690987da915Sopenharmony_ci	BOOL done;
1691987da915Sopenharmony_ci	BOOL compress;
1692987da915Sopenharmony_ci	BOOL appending;
1693987da915Sopenharmony_ci
1694987da915Sopenharmony_ci	if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) {
1695987da915Sopenharmony_ci		return (-1);
1696987da915Sopenharmony_ci	}
1697987da915Sopenharmony_ci	if ((*update_from < 0)
1698987da915Sopenharmony_ci	    || (compressed_part < 0)
1699987da915Sopenharmony_ci	    || (compressed_part > (int)na->compression_block_clusters)) {
1700987da915Sopenharmony_ci		ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n",
1701987da915Sopenharmony_ci			compressed_part);
1702987da915Sopenharmony_ci		errno = EIO;
1703987da915Sopenharmony_ci		return (-1);
1704987da915Sopenharmony_ci	}
1705987da915Sopenharmony_ci		/* make sure there are two unused entries in runlist */
1706987da915Sopenharmony_ci	if (na->unused_runs < 2) {
1707987da915Sopenharmony_ci		ntfs_log_error("No unused runs for compressed write\n");
1708987da915Sopenharmony_ci		errno = EIO;
1709987da915Sopenharmony_ci		return (-1);
1710987da915Sopenharmony_ci	}
1711987da915Sopenharmony_ci	if (na->compression_block_size < NTFS_SB_SIZE) {
1712987da915Sopenharmony_ci		ntfs_log_error("Unsupported compression block size %ld\n",
1713987da915Sopenharmony_ci				(long)na->compression_block_size);
1714987da915Sopenharmony_ci		errno = EOVERFLOW;
1715987da915Sopenharmony_ci		return (-1);
1716987da915Sopenharmony_ci	}
1717987da915Sopenharmony_ci	if (wrl->vcn < *update_from)
1718987da915Sopenharmony_ci		*update_from = wrl->vcn;
1719987da915Sopenharmony_ci	written = -1; /* default return */
1720987da915Sopenharmony_ci	vol = na->ni->vol;
1721987da915Sopenharmony_ci	compression_length = na->compression_block_clusters;
1722987da915Sopenharmony_ci	compress = FALSE;
1723987da915Sopenharmony_ci	done = FALSE;
1724987da915Sopenharmony_ci		/*
1725987da915Sopenharmony_ci		 * Cannot accept writing beyond the current compression set
1726987da915Sopenharmony_ci		 * because when compression occurs, clusters are freed
1727987da915Sopenharmony_ci		 * and have to be reallocated.
1728987da915Sopenharmony_ci		 * (cannot happen with standard fuse 4K buffers)
1729987da915Sopenharmony_ci		 * Caller has to avoid this situation, or face consequences.
1730987da915Sopenharmony_ci		 */
1731987da915Sopenharmony_ci	nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits))
1732987da915Sopenharmony_ci			| (na->compression_block_size - 1)) + 1;
1733987da915Sopenharmony_ci		/* determine whether we are appending to file */
1734987da915Sopenharmony_ci	endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits);
1735987da915Sopenharmony_ci	appending = endwrite >= na->initialized_size;
1736987da915Sopenharmony_ci	if (endwrite >= nextblock) {
1737987da915Sopenharmony_ci			/* it is time to compress */
1738987da915Sopenharmony_ci		compress = TRUE;
1739987da915Sopenharmony_ci			/* only process what we can */
1740987da915Sopenharmony_ci		to_write = rounded = nextblock
1741987da915Sopenharmony_ci			- (offs + (wrl->vcn << vol->cluster_size_bits));
1742987da915Sopenharmony_ci	}
1743987da915Sopenharmony_ci	start_vcn = 0;
1744987da915Sopenharmony_ci	fail = FALSE;
1745987da915Sopenharmony_ci	brl = wrl;
1746987da915Sopenharmony_ci	roffs = 0;
1747987da915Sopenharmony_ci		/*
1748987da915Sopenharmony_ci		 * If we are about to compress or we need to decompress
1749987da915Sopenharmony_ci		 * existing data, we have to process a full set of blocks.
1750987da915Sopenharmony_ci		 * So relocate the parameters to the beginning of allocation
1751987da915Sopenharmony_ci		 * containing the first byte of the set of blocks.
1752987da915Sopenharmony_ci		 */
1753987da915Sopenharmony_ci	if (compress || compressed_part) {
1754987da915Sopenharmony_ci		/* find the beginning of block */
1755987da915Sopenharmony_ci		start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits))
1756987da915Sopenharmony_ci				& -compression_length;
1757987da915Sopenharmony_ci		if (start_vcn < *update_from)
1758987da915Sopenharmony_ci			*update_from = start_vcn;
1759987da915Sopenharmony_ci		while (brl->vcn && (brl->vcn > start_vcn)) {
1760987da915Sopenharmony_ci			/* jumping back a hole means big trouble */
1761987da915Sopenharmony_ci			if (brl->lcn == (LCN)LCN_HOLE) {
1762987da915Sopenharmony_ci				ntfs_log_error("jump back over a hole when appending\n");
1763987da915Sopenharmony_ci				fail = TRUE;
1764987da915Sopenharmony_ci				errno = EIO;
1765987da915Sopenharmony_ci			}
1766987da915Sopenharmony_ci			brl--;
1767987da915Sopenharmony_ci			offs += brl->length << vol->cluster_size_bits;
1768987da915Sopenharmony_ci		}
1769987da915Sopenharmony_ci		roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits;
1770987da915Sopenharmony_ci	}
1771987da915Sopenharmony_ci	if (compressed_part && !fail) {
1772987da915Sopenharmony_ci		/*
1773987da915Sopenharmony_ci		 * The set of compression blocks contains compressed data
1774987da915Sopenharmony_ci		 * (we are reopening an existing file to append to it)
1775987da915Sopenharmony_ci		 * Decompress the data and append
1776987da915Sopenharmony_ci		 */
1777987da915Sopenharmony_ci		compsz = (s32)compressed_part << vol->cluster_size_bits;
1778987da915Sopenharmony_ci		outbuf = (char*)ntfs_malloc(na->compression_block_size);
1779987da915Sopenharmony_ci		if (outbuf) {
1780987da915Sopenharmony_ci			if (appending) {
1781987da915Sopenharmony_ci				to_read = offs - roffs;
1782987da915Sopenharmony_ci				to_flush = to_read + to_write;
1783987da915Sopenharmony_ci			} else {
1784987da915Sopenharmony_ci				to_read = na->data_size
1785987da915Sopenharmony_ci					- (brl->vcn << vol->cluster_size_bits);
1786987da915Sopenharmony_ci				if (to_read > na->compression_block_size)
1787987da915Sopenharmony_ci					to_read = na->compression_block_size;
1788987da915Sopenharmony_ci				to_flush = to_read;
1789987da915Sopenharmony_ci			}
1790987da915Sopenharmony_ci			if (!ntfs_read_append(na, brl, roffs, compsz,
1791987da915Sopenharmony_ci					(s32)(offs - roffs), appending,
1792987da915Sopenharmony_ci					outbuf, to_write, b)) {
1793987da915Sopenharmony_ci				written = ntfs_flush(na, brl, roffs,
1794987da915Sopenharmony_ci					outbuf, to_flush, compress, appending,
1795987da915Sopenharmony_ci					update_from);
1796987da915Sopenharmony_ci				if (written >= 0) {
1797987da915Sopenharmony_ci					written = to_write;
1798987da915Sopenharmony_ci					done = TRUE;
1799987da915Sopenharmony_ci				}
1800987da915Sopenharmony_ci			}
1801987da915Sopenharmony_ci		free(outbuf);
1802987da915Sopenharmony_ci		}
1803987da915Sopenharmony_ci	} else {
1804987da915Sopenharmony_ci		if (compress && !fail) {
1805987da915Sopenharmony_ci			/*
1806987da915Sopenharmony_ci			 * we are filling up a block, read the full set
1807987da915Sopenharmony_ci			 * of blocks and compress it
1808987da915Sopenharmony_ci		 	 */
1809987da915Sopenharmony_ci			inbuf = (char*)ntfs_malloc(na->compression_block_size);
1810987da915Sopenharmony_ci			if (inbuf) {
1811987da915Sopenharmony_ci				to_read = offs - roffs;
1812987da915Sopenharmony_ci				if (to_read)
1813987da915Sopenharmony_ci					got = read_clusters(vol, brl, roffs,
1814987da915Sopenharmony_ci							to_read, inbuf);
1815987da915Sopenharmony_ci				else
1816987da915Sopenharmony_ci					got = 0;
1817987da915Sopenharmony_ci				if (got == to_read) {
1818987da915Sopenharmony_ci					memcpy(&inbuf[to_read],b,to_write);
1819987da915Sopenharmony_ci					written = ntfs_comp_set(na, brl, roffs,
1820987da915Sopenharmony_ci						to_read + to_write, inbuf);
1821987da915Sopenharmony_ci				/*
1822987da915Sopenharmony_ci				 * if compression was not successful,
1823987da915Sopenharmony_ci				 * only write the part which was requested
1824987da915Sopenharmony_ci				 */
1825987da915Sopenharmony_ci					if ((written >= 0)
1826987da915Sopenharmony_ci						/* free the unused clusters */
1827987da915Sopenharmony_ci				  	  && !ntfs_compress_free(na,brl,
1828987da915Sopenharmony_ci						    written + roffs,
1829987da915Sopenharmony_ci						    na->compression_block_size
1830987da915Sopenharmony_ci						         + roffs,
1831987da915Sopenharmony_ci						    appending, update_from)) {
1832987da915Sopenharmony_ci						done = TRUE;
1833987da915Sopenharmony_ci						written = to_write;
1834987da915Sopenharmony_ci					}
1835987da915Sopenharmony_ci				}
1836987da915Sopenharmony_ci				free(inbuf);
1837987da915Sopenharmony_ci			}
1838987da915Sopenharmony_ci		}
1839987da915Sopenharmony_ci		if (!done) {
1840987da915Sopenharmony_ci			/*
1841987da915Sopenharmony_ci			 * if the compression block is not full, or
1842987da915Sopenharmony_ci			 * if compression failed for whatever reason,
1843987da915Sopenharmony_ci		 	 * write uncompressed
1844987da915Sopenharmony_ci			 */
1845987da915Sopenharmony_ci			/* check we are not overflowing current allocation */
1846987da915Sopenharmony_ci			if ((wpos + rounded)
1847987da915Sopenharmony_ci			    > ((wrl->lcn + wrl->length)
1848987da915Sopenharmony_ci				 << vol->cluster_size_bits)) {
1849987da915Sopenharmony_ci				ntfs_log_error("writing on unallocated clusters\n");
1850987da915Sopenharmony_ci				errno = EIO;
1851987da915Sopenharmony_ci			} else {
1852987da915Sopenharmony_ci				written = ntfs_pwrite(vol->dev, wpos,
1853987da915Sopenharmony_ci					rounded, b);
1854987da915Sopenharmony_ci				if (written == rounded)
1855987da915Sopenharmony_ci					written = to_write;
1856987da915Sopenharmony_ci			}
1857987da915Sopenharmony_ci		}
1858987da915Sopenharmony_ci	}
1859987da915Sopenharmony_ci	if ((written >= 0)
1860987da915Sopenharmony_ci	    && !valid_compressed_run(na,wrl,TRUE,"end compressed write"))
1861987da915Sopenharmony_ci		written = -1;
1862987da915Sopenharmony_ci	return (written);
1863987da915Sopenharmony_ci}
1864987da915Sopenharmony_ci
1865987da915Sopenharmony_ci/*
1866987da915Sopenharmony_ci *		Close a file written compressed.
1867987da915Sopenharmony_ci *	This compresses the last partial compression block of the file.
1868987da915Sopenharmony_ci *	Two empty runlist slots have to be reserved beforehand.
1869987da915Sopenharmony_ci *
1870987da915Sopenharmony_ci *	Returns zero if closing is successful.
1871987da915Sopenharmony_ci */
1872987da915Sopenharmony_ci
1873987da915Sopenharmony_ciint ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs,
1874987da915Sopenharmony_ci			VCN *update_from)
1875987da915Sopenharmony_ci{
1876987da915Sopenharmony_ci	ntfs_volume *vol;
1877987da915Sopenharmony_ci	runlist_element *brl; /* entry containing the beginning of block */
1878987da915Sopenharmony_ci	int compression_length;
1879987da915Sopenharmony_ci	s64 written;
1880987da915Sopenharmony_ci	s64 to_read;
1881987da915Sopenharmony_ci	s64 roffs;
1882987da915Sopenharmony_ci	s64 got;
1883987da915Sopenharmony_ci	s64 start_vcn;
1884987da915Sopenharmony_ci	char *inbuf;
1885987da915Sopenharmony_ci	BOOL fail;
1886987da915Sopenharmony_ci	BOOL done;
1887987da915Sopenharmony_ci
1888987da915Sopenharmony_ci	if (na->unused_runs < 2) {
1889987da915Sopenharmony_ci		ntfs_log_error("No unused runs for compressed close\n");
1890987da915Sopenharmony_ci		errno = EIO;
1891987da915Sopenharmony_ci		return (-1);
1892987da915Sopenharmony_ci	}
1893987da915Sopenharmony_ci	if (*update_from < 0) {
1894987da915Sopenharmony_ci		ntfs_log_error("Bad update vcn for compressed close\n");
1895987da915Sopenharmony_ci		errno = EIO;
1896987da915Sopenharmony_ci		return (-1);
1897987da915Sopenharmony_ci	}
1898987da915Sopenharmony_ci	if (na->compression_block_size < NTFS_SB_SIZE) {
1899987da915Sopenharmony_ci		ntfs_log_error("Unsupported compression block size %ld\n",
1900987da915Sopenharmony_ci				(long)na->compression_block_size);
1901987da915Sopenharmony_ci		errno = EOVERFLOW;
1902987da915Sopenharmony_ci		return (-1);
1903987da915Sopenharmony_ci	}
1904987da915Sopenharmony_ci	if (wrl->vcn < *update_from)
1905987da915Sopenharmony_ci		*update_from = wrl->vcn;
1906987da915Sopenharmony_ci	vol = na->ni->vol;
1907987da915Sopenharmony_ci	compression_length = na->compression_block_clusters;
1908987da915Sopenharmony_ci	done = FALSE;
1909987da915Sopenharmony_ci		/*
1910987da915Sopenharmony_ci		 * There generally is an uncompressed block at end of file,
1911987da915Sopenharmony_ci		 * read the full block and compress it
1912987da915Sopenharmony_ci		 */
1913987da915Sopenharmony_ci	inbuf = (char*)ntfs_malloc(na->compression_block_size);
1914987da915Sopenharmony_ci	if (inbuf) {
1915987da915Sopenharmony_ci		start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits))
1916987da915Sopenharmony_ci				& -compression_length;
1917987da915Sopenharmony_ci		if (start_vcn < *update_from)
1918987da915Sopenharmony_ci			*update_from = start_vcn;
1919987da915Sopenharmony_ci		to_read = offs + ((wrl->vcn - start_vcn)
1920987da915Sopenharmony_ci					<< vol->cluster_size_bits);
1921987da915Sopenharmony_ci		brl = wrl;
1922987da915Sopenharmony_ci		fail = FALSE;
1923987da915Sopenharmony_ci		while (brl->vcn && (brl->vcn > start_vcn)) {
1924987da915Sopenharmony_ci			if (brl->lcn == (LCN)LCN_HOLE) {
1925987da915Sopenharmony_ci				ntfs_log_error("jump back over a hole when closing\n");
1926987da915Sopenharmony_ci				fail = TRUE;
1927987da915Sopenharmony_ci				errno = EIO;
1928987da915Sopenharmony_ci			}
1929987da915Sopenharmony_ci			brl--;
1930987da915Sopenharmony_ci		}
1931987da915Sopenharmony_ci		if (!fail) {
1932987da915Sopenharmony_ci			/* roffs can be an offset from another uncomp block */
1933987da915Sopenharmony_ci			roffs = (start_vcn - brl->vcn)
1934987da915Sopenharmony_ci						<< vol->cluster_size_bits;
1935987da915Sopenharmony_ci			if (to_read) {
1936987da915Sopenharmony_ci				got = read_clusters(vol, brl, roffs, to_read,
1937987da915Sopenharmony_ci						 inbuf);
1938987da915Sopenharmony_ci				if (got == to_read) {
1939987da915Sopenharmony_ci					written = ntfs_comp_set(na, brl, roffs,
1940987da915Sopenharmony_ci							to_read, inbuf);
1941987da915Sopenharmony_ci					if ((written >= 0)
1942987da915Sopenharmony_ci					/* free the unused clusters */
1943987da915Sopenharmony_ci					    && !ntfs_compress_free(na,brl,
1944987da915Sopenharmony_ci							written + roffs,
1945987da915Sopenharmony_ci							na->compression_block_size + roffs,
1946987da915Sopenharmony_ci							TRUE, update_from)) {
1947987da915Sopenharmony_ci						done = TRUE;
1948987da915Sopenharmony_ci					} else
1949987da915Sopenharmony_ci				/* if compression failed, leave uncompressed */
1950987da915Sopenharmony_ci						if (written == -1)
1951987da915Sopenharmony_ci							done = TRUE;
1952987da915Sopenharmony_ci				}
1953987da915Sopenharmony_ci			} else
1954987da915Sopenharmony_ci				done = TRUE;
1955987da915Sopenharmony_ci			free(inbuf);
1956987da915Sopenharmony_ci		}
1957987da915Sopenharmony_ci	}
1958987da915Sopenharmony_ci	if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close"))
1959987da915Sopenharmony_ci		done = FALSE;
1960987da915Sopenharmony_ci	return (!done);
1961987da915Sopenharmony_ci}
1962