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