1987da915Sopenharmony_ci/** 2987da915Sopenharmony_ci * runlist.c - Run list handling code. Originated from the Linux-NTFS project. 3987da915Sopenharmony_ci * 4987da915Sopenharmony_ci * Copyright (c) 2002-2005 Anton Altaparmakov 5987da915Sopenharmony_ci * Copyright (c) 2002-2005 Richard Russon 6987da915Sopenharmony_ci * Copyright (c) 2002-2008 Szabolcs Szakacsits 7987da915Sopenharmony_ci * Copyright (c) 2004 Yura Pakhuchiy 8987da915Sopenharmony_ci * Copyright (c) 2007-2022 Jean-Pierre Andre 9987da915Sopenharmony_ci * 10987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or 11987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published 12987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or 13987da915Sopenharmony_ci * (at your option) any later version. 14987da915Sopenharmony_ci * 15987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be 16987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 17987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18987da915Sopenharmony_ci * GNU General Public License for more details. 19987da915Sopenharmony_ci * 20987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License 21987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G 22987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software 23987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24987da915Sopenharmony_ci */ 25987da915Sopenharmony_ci 26987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H 27987da915Sopenharmony_ci#include "config.h" 28987da915Sopenharmony_ci#endif 29987da915Sopenharmony_ci 30987da915Sopenharmony_ci#ifdef HAVE_STDIO_H 31987da915Sopenharmony_ci#include <stdio.h> 32987da915Sopenharmony_ci#endif 33987da915Sopenharmony_ci#ifdef HAVE_STRING_H 34987da915Sopenharmony_ci#include <string.h> 35987da915Sopenharmony_ci#endif 36987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H 37987da915Sopenharmony_ci#include <stdlib.h> 38987da915Sopenharmony_ci#endif 39987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H 40987da915Sopenharmony_ci#include <errno.h> 41987da915Sopenharmony_ci#endif 42987da915Sopenharmony_ci 43987da915Sopenharmony_ci#include "compat.h" 44987da915Sopenharmony_ci#include "types.h" 45987da915Sopenharmony_ci#include "volume.h" 46987da915Sopenharmony_ci#include "layout.h" 47987da915Sopenharmony_ci#include "debug.h" 48987da915Sopenharmony_ci#include "device.h" 49987da915Sopenharmony_ci#include "logging.h" 50987da915Sopenharmony_ci#include "misc.h" 51987da915Sopenharmony_ci 52987da915Sopenharmony_ci/** 53987da915Sopenharmony_ci * ntfs_rl_mm - runlist memmove 54987da915Sopenharmony_ci * @base: 55987da915Sopenharmony_ci * @dst: 56987da915Sopenharmony_ci * @src: 57987da915Sopenharmony_ci * @size: 58987da915Sopenharmony_ci * 59987da915Sopenharmony_ci * Description... 60987da915Sopenharmony_ci * 61987da915Sopenharmony_ci * Returns: 62987da915Sopenharmony_ci */ 63987da915Sopenharmony_cistatic void ntfs_rl_mm(runlist_element *base, int dst, int src, int size) 64987da915Sopenharmony_ci{ 65987da915Sopenharmony_ci if ((dst != src) && (size > 0)) 66987da915Sopenharmony_ci memmove(base + dst, base + src, size * sizeof(*base)); 67987da915Sopenharmony_ci} 68987da915Sopenharmony_ci 69987da915Sopenharmony_ci/** 70987da915Sopenharmony_ci * ntfs_rl_mc - runlist memory copy 71987da915Sopenharmony_ci * @dstbase: 72987da915Sopenharmony_ci * @dst: 73987da915Sopenharmony_ci * @srcbase: 74987da915Sopenharmony_ci * @src: 75987da915Sopenharmony_ci * @size: 76987da915Sopenharmony_ci * 77987da915Sopenharmony_ci * Description... 78987da915Sopenharmony_ci * 79987da915Sopenharmony_ci * Returns: 80987da915Sopenharmony_ci */ 81987da915Sopenharmony_cistatic void ntfs_rl_mc(runlist_element *dstbase, int dst, 82987da915Sopenharmony_ci runlist_element *srcbase, int src, int size) 83987da915Sopenharmony_ci{ 84987da915Sopenharmony_ci if (size > 0) 85987da915Sopenharmony_ci memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase)); 86987da915Sopenharmony_ci} 87987da915Sopenharmony_ci 88987da915Sopenharmony_ci/** 89987da915Sopenharmony_ci * ntfs_rl_realloc - Reallocate memory for runlists 90987da915Sopenharmony_ci * @rl: original runlist 91987da915Sopenharmony_ci * @old_size: number of runlist elements in the original runlist @rl 92987da915Sopenharmony_ci * @new_size: number of runlist elements we need space for 93987da915Sopenharmony_ci * 94987da915Sopenharmony_ci * As the runlists grow, more memory will be required. To prevent large 95987da915Sopenharmony_ci * numbers of small reallocations of memory, this function returns a 4kiB block 96987da915Sopenharmony_ci * of memory. 97987da915Sopenharmony_ci * 98987da915Sopenharmony_ci * N.B. If the new allocation doesn't require a different number of 4kiB 99987da915Sopenharmony_ci * blocks in memory, the function will return the original pointer. 100987da915Sopenharmony_ci * 101987da915Sopenharmony_ci * On success, return a pointer to the newly allocated, or recycled, memory. 102987da915Sopenharmony_ci * On error, return NULL with errno set to the error code. 103987da915Sopenharmony_ci */ 104987da915Sopenharmony_cistatic runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, 105987da915Sopenharmony_ci int new_size) 106987da915Sopenharmony_ci{ 107987da915Sopenharmony_ci old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff; 108987da915Sopenharmony_ci new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff; 109987da915Sopenharmony_ci if (old_size == new_size) 110987da915Sopenharmony_ci return rl; 111987da915Sopenharmony_ci return realloc(rl, new_size); 112987da915Sopenharmony_ci} 113987da915Sopenharmony_ci 114987da915Sopenharmony_ci/* 115987da915Sopenharmony_ci * Extend a runlist by some entry count 116987da915Sopenharmony_ci * The runlist may have to be reallocated 117987da915Sopenharmony_ci * 118987da915Sopenharmony_ci * Returns the reallocated runlist 119987da915Sopenharmony_ci * or NULL if reallocation was not possible (with errno set) 120987da915Sopenharmony_ci * the runlist is left unchanged if the reallocation fails 121987da915Sopenharmony_ci */ 122987da915Sopenharmony_ci 123987da915Sopenharmony_cirunlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, 124987da915Sopenharmony_ci int more_entries) 125987da915Sopenharmony_ci{ 126987da915Sopenharmony_ci runlist_element *newrl; 127987da915Sopenharmony_ci int last; 128987da915Sopenharmony_ci int irl; 129987da915Sopenharmony_ci 130987da915Sopenharmony_ci if (na->rl && rl) { 131987da915Sopenharmony_ci irl = (int)(rl - na->rl); 132987da915Sopenharmony_ci last = irl; 133987da915Sopenharmony_ci while (na->rl[last].length) 134987da915Sopenharmony_ci last++; 135987da915Sopenharmony_ci newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); 136987da915Sopenharmony_ci if (!newrl) { 137987da915Sopenharmony_ci errno = ENOMEM; 138987da915Sopenharmony_ci rl = (runlist_element*)NULL; 139987da915Sopenharmony_ci } else { 140987da915Sopenharmony_ci na->rl = newrl; 141987da915Sopenharmony_ci rl = &newrl[irl]; 142987da915Sopenharmony_ci } 143987da915Sopenharmony_ci } else { 144987da915Sopenharmony_ci ntfs_log_error("Cannot extend unmapped runlist"); 145987da915Sopenharmony_ci errno = EIO; 146987da915Sopenharmony_ci rl = (runlist_element*)NULL; 147987da915Sopenharmony_ci } 148987da915Sopenharmony_ci return (rl); 149987da915Sopenharmony_ci} 150987da915Sopenharmony_ci 151987da915Sopenharmony_ci/** 152987da915Sopenharmony_ci * ntfs_rl_are_mergeable - test if two runlists can be joined together 153987da915Sopenharmony_ci * @dst: original runlist 154987da915Sopenharmony_ci * @src: new runlist to test for mergeability with @dst 155987da915Sopenharmony_ci * 156987da915Sopenharmony_ci * Test if two runlists can be joined together. For this, their VCNs and LCNs 157987da915Sopenharmony_ci * must be adjacent. 158987da915Sopenharmony_ci * 159987da915Sopenharmony_ci * Return: TRUE Success, the runlists can be merged. 160987da915Sopenharmony_ci * FALSE Failure, the runlists cannot be merged. 161987da915Sopenharmony_ci */ 162987da915Sopenharmony_cistatic BOOL ntfs_rl_are_mergeable(runlist_element *dst, runlist_element *src) 163987da915Sopenharmony_ci{ 164987da915Sopenharmony_ci if (!dst || !src) { 165987da915Sopenharmony_ci ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL " 166987da915Sopenharmony_ci "pointer!\n"); 167987da915Sopenharmony_ci return FALSE; 168987da915Sopenharmony_ci } 169987da915Sopenharmony_ci 170987da915Sopenharmony_ci /* We can merge unmapped regions even if they are misaligned. */ 171987da915Sopenharmony_ci if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED)) 172987da915Sopenharmony_ci return TRUE; 173987da915Sopenharmony_ci /* If the runs are misaligned, we cannot merge them. */ 174987da915Sopenharmony_ci if ((dst->vcn + dst->length) != src->vcn) 175987da915Sopenharmony_ci return FALSE; 176987da915Sopenharmony_ci /* If both runs are non-sparse and contiguous, we can merge them. */ 177987da915Sopenharmony_ci if ((dst->lcn >= 0) && (src->lcn >= 0) && 178987da915Sopenharmony_ci ((dst->lcn + dst->length) == src->lcn)) 179987da915Sopenharmony_ci return TRUE; 180987da915Sopenharmony_ci /* If we are merging two holes, we can merge them. */ 181987da915Sopenharmony_ci if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE)) 182987da915Sopenharmony_ci return TRUE; 183987da915Sopenharmony_ci /* Cannot merge. */ 184987da915Sopenharmony_ci return FALSE; 185987da915Sopenharmony_ci} 186987da915Sopenharmony_ci 187987da915Sopenharmony_ci/** 188987da915Sopenharmony_ci * __ntfs_rl_merge - merge two runlists without testing if they can be merged 189987da915Sopenharmony_ci * @dst: original, destination runlist 190987da915Sopenharmony_ci * @src: new runlist to merge with @dst 191987da915Sopenharmony_ci * 192987da915Sopenharmony_ci * Merge the two runlists, writing into the destination runlist @dst. The 193987da915Sopenharmony_ci * caller must make sure the runlists can be merged or this will corrupt the 194987da915Sopenharmony_ci * destination runlist. 195987da915Sopenharmony_ci */ 196987da915Sopenharmony_cistatic void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) 197987da915Sopenharmony_ci{ 198987da915Sopenharmony_ci dst->length += src->length; 199987da915Sopenharmony_ci} 200987da915Sopenharmony_ci 201987da915Sopenharmony_ci/** 202987da915Sopenharmony_ci * ntfs_rl_append - append a runlist after a given element 203987da915Sopenharmony_ci * @dst: original runlist to be worked on 204987da915Sopenharmony_ci * @dsize: number of elements in @dst (including end marker) 205987da915Sopenharmony_ci * @src: runlist to be inserted into @dst 206987da915Sopenharmony_ci * @ssize: number of elements in @src (excluding end marker) 207987da915Sopenharmony_ci * @loc: append the new runlist @src after this element in @dst 208987da915Sopenharmony_ci * 209987da915Sopenharmony_ci * Append the runlist @src after element @loc in @dst. Merge the right end of 210987da915Sopenharmony_ci * the new runlist, if necessary. Adjust the size of the hole before the 211987da915Sopenharmony_ci * appended runlist. 212987da915Sopenharmony_ci * 213987da915Sopenharmony_ci * On success, return a pointer to the new, combined, runlist. Note, both 214987da915Sopenharmony_ci * runlists @dst and @src are deallocated before returning so you cannot use 215987da915Sopenharmony_ci * the pointers for anything any more. (Strictly speaking the returned runlist 216987da915Sopenharmony_ci * may be the same as @dst but this is irrelevant.) 217987da915Sopenharmony_ci * 218987da915Sopenharmony_ci * On error, return NULL, with errno set to the error code. Both runlists are 219987da915Sopenharmony_ci * left unmodified. 220987da915Sopenharmony_ci */ 221987da915Sopenharmony_cistatic runlist_element *ntfs_rl_append(runlist_element *dst, int dsize, 222987da915Sopenharmony_ci runlist_element *src, int ssize, int loc) 223987da915Sopenharmony_ci{ 224987da915Sopenharmony_ci BOOL right = FALSE; /* Right end of @src needs merging */ 225987da915Sopenharmony_ci int marker; /* End of the inserted runs */ 226987da915Sopenharmony_ci 227987da915Sopenharmony_ci if (!dst || !src) { 228987da915Sopenharmony_ci ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL " 229987da915Sopenharmony_ci "pointer!\n"); 230987da915Sopenharmony_ci errno = EINVAL; 231987da915Sopenharmony_ci return NULL; 232987da915Sopenharmony_ci } 233987da915Sopenharmony_ci 234987da915Sopenharmony_ci /* First, check if the right hand end needs merging. */ 235987da915Sopenharmony_ci if ((loc + 1) < dsize) 236987da915Sopenharmony_ci right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); 237987da915Sopenharmony_ci 238987da915Sopenharmony_ci /* Space required: @dst size + @src size, less one if we merged. */ 239987da915Sopenharmony_ci dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right); 240987da915Sopenharmony_ci if (!dst) 241987da915Sopenharmony_ci return NULL; 242987da915Sopenharmony_ci /* 243987da915Sopenharmony_ci * We are guaranteed to succeed from here so can start modifying the 244987da915Sopenharmony_ci * original runlists. 245987da915Sopenharmony_ci */ 246987da915Sopenharmony_ci 247987da915Sopenharmony_ci /* First, merge the right hand end, if necessary. */ 248987da915Sopenharmony_ci if (right) 249987da915Sopenharmony_ci __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); 250987da915Sopenharmony_ci 251987da915Sopenharmony_ci /* marker - First run after the @src runs that have been inserted */ 252987da915Sopenharmony_ci marker = loc + ssize + 1; 253987da915Sopenharmony_ci 254987da915Sopenharmony_ci /* Move the tail of @dst out of the way, then copy in @src. */ 255987da915Sopenharmony_ci ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right); 256987da915Sopenharmony_ci ntfs_rl_mc(dst, loc + 1, src, 0, ssize); 257987da915Sopenharmony_ci 258987da915Sopenharmony_ci /* Adjust the size of the preceding hole. */ 259987da915Sopenharmony_ci dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; 260987da915Sopenharmony_ci 261987da915Sopenharmony_ci /* We may have changed the length of the file, so fix the end marker */ 262987da915Sopenharmony_ci if (dst[marker].lcn == LCN_ENOENT) 263987da915Sopenharmony_ci dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length; 264987da915Sopenharmony_ci 265987da915Sopenharmony_ci return dst; 266987da915Sopenharmony_ci} 267987da915Sopenharmony_ci 268987da915Sopenharmony_ci/** 269987da915Sopenharmony_ci * ntfs_rl_insert - insert a runlist into another 270987da915Sopenharmony_ci * @dst: original runlist to be worked on 271987da915Sopenharmony_ci * @dsize: number of elements in @dst (including end marker) 272987da915Sopenharmony_ci * @src: new runlist to be inserted 273987da915Sopenharmony_ci * @ssize: number of elements in @src (excluding end marker) 274987da915Sopenharmony_ci * @loc: insert the new runlist @src before this element in @dst 275987da915Sopenharmony_ci * 276987da915Sopenharmony_ci * Insert the runlist @src before element @loc in the runlist @dst. Merge the 277987da915Sopenharmony_ci * left end of the new runlist, if necessary. Adjust the size of the hole 278987da915Sopenharmony_ci * after the inserted runlist. 279987da915Sopenharmony_ci * 280987da915Sopenharmony_ci * On success, return a pointer to the new, combined, runlist. Note, both 281987da915Sopenharmony_ci * runlists @dst and @src are deallocated before returning so you cannot use 282987da915Sopenharmony_ci * the pointers for anything any more. (Strictly speaking the returned runlist 283987da915Sopenharmony_ci * may be the same as @dst but this is irrelevant.) 284987da915Sopenharmony_ci * 285987da915Sopenharmony_ci * On error, return NULL, with errno set to the error code. Both runlists are 286987da915Sopenharmony_ci * left unmodified. 287987da915Sopenharmony_ci */ 288987da915Sopenharmony_cistatic runlist_element *ntfs_rl_insert(runlist_element *dst, int dsize, 289987da915Sopenharmony_ci runlist_element *src, int ssize, int loc) 290987da915Sopenharmony_ci{ 291987da915Sopenharmony_ci BOOL left = FALSE; /* Left end of @src needs merging */ 292987da915Sopenharmony_ci BOOL disc = FALSE; /* Discontinuity between @dst and @src */ 293987da915Sopenharmony_ci int marker; /* End of the inserted runs */ 294987da915Sopenharmony_ci 295987da915Sopenharmony_ci if (!dst || !src) { 296987da915Sopenharmony_ci ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL " 297987da915Sopenharmony_ci "pointer!\n"); 298987da915Sopenharmony_ci errno = EINVAL; 299987da915Sopenharmony_ci return NULL; 300987da915Sopenharmony_ci } 301987da915Sopenharmony_ci 302987da915Sopenharmony_ci /* disc => Discontinuity between the end of @dst and the start of @src. 303987da915Sopenharmony_ci * This means we might need to insert a "notmapped" run. 304987da915Sopenharmony_ci */ 305987da915Sopenharmony_ci if (loc == 0) 306987da915Sopenharmony_ci disc = (src[0].vcn > 0); 307987da915Sopenharmony_ci else { 308987da915Sopenharmony_ci s64 merged_length; 309987da915Sopenharmony_ci 310987da915Sopenharmony_ci left = ntfs_rl_are_mergeable(dst + loc - 1, src); 311987da915Sopenharmony_ci 312987da915Sopenharmony_ci merged_length = dst[loc - 1].length; 313987da915Sopenharmony_ci if (left) 314987da915Sopenharmony_ci merged_length += src->length; 315987da915Sopenharmony_ci 316987da915Sopenharmony_ci disc = (src[0].vcn > dst[loc - 1].vcn + merged_length); 317987da915Sopenharmony_ci } 318987da915Sopenharmony_ci 319987da915Sopenharmony_ci /* Space required: @dst size + @src size, less one if we merged, plus 320987da915Sopenharmony_ci * one if there was a discontinuity. 321987da915Sopenharmony_ci */ 322987da915Sopenharmony_ci dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc); 323987da915Sopenharmony_ci if (!dst) 324987da915Sopenharmony_ci return NULL; 325987da915Sopenharmony_ci /* 326987da915Sopenharmony_ci * We are guaranteed to succeed from here so can start modifying the 327987da915Sopenharmony_ci * original runlist. 328987da915Sopenharmony_ci */ 329987da915Sopenharmony_ci 330987da915Sopenharmony_ci if (left) 331987da915Sopenharmony_ci __ntfs_rl_merge(dst + loc - 1, src); 332987da915Sopenharmony_ci 333987da915Sopenharmony_ci /* 334987da915Sopenharmony_ci * marker - First run after the @src runs that have been inserted 335987da915Sopenharmony_ci * Nominally: marker = @loc + @ssize (location + number of runs in @src) 336987da915Sopenharmony_ci * If "left", then the first run in @src has been merged with one in @dst. 337987da915Sopenharmony_ci * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap. 338987da915Sopenharmony_ci */ 339987da915Sopenharmony_ci marker = loc + ssize - left + disc; 340987da915Sopenharmony_ci 341987da915Sopenharmony_ci /* Move the tail of @dst out of the way, then copy in @src. */ 342987da915Sopenharmony_ci ntfs_rl_mm(dst, marker, loc, dsize - loc); 343987da915Sopenharmony_ci ntfs_rl_mc(dst, loc + disc, src, left, ssize - left); 344987da915Sopenharmony_ci 345987da915Sopenharmony_ci /* Adjust the VCN of the first run after the insertion ... */ 346987da915Sopenharmony_ci dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; 347987da915Sopenharmony_ci /* ... and the length. */ 348987da915Sopenharmony_ci if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED) 349987da915Sopenharmony_ci dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn; 350987da915Sopenharmony_ci 351987da915Sopenharmony_ci /* Writing beyond the end of the file and there's a discontinuity. */ 352987da915Sopenharmony_ci if (disc) { 353987da915Sopenharmony_ci if (loc > 0) { 354987da915Sopenharmony_ci dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length; 355987da915Sopenharmony_ci dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn; 356987da915Sopenharmony_ci } else { 357987da915Sopenharmony_ci dst[loc].vcn = 0; 358987da915Sopenharmony_ci dst[loc].length = dst[loc + 1].vcn; 359987da915Sopenharmony_ci } 360987da915Sopenharmony_ci dst[loc].lcn = LCN_RL_NOT_MAPPED; 361987da915Sopenharmony_ci } 362987da915Sopenharmony_ci return dst; 363987da915Sopenharmony_ci} 364987da915Sopenharmony_ci 365987da915Sopenharmony_ci/** 366987da915Sopenharmony_ci * ntfs_rl_replace - overwrite a runlist element with another runlist 367987da915Sopenharmony_ci * @dst: original runlist to be worked on 368987da915Sopenharmony_ci * @dsize: number of elements in @dst (including end marker) 369987da915Sopenharmony_ci * @src: new runlist to be inserted 370987da915Sopenharmony_ci * @ssize: number of elements in @src (excluding end marker) 371987da915Sopenharmony_ci * @loc: index in runlist @dst to overwrite with @src 372987da915Sopenharmony_ci * 373987da915Sopenharmony_ci * Replace the runlist element @dst at @loc with @src. Merge the left and 374987da915Sopenharmony_ci * right ends of the inserted runlist, if necessary. 375987da915Sopenharmony_ci * 376987da915Sopenharmony_ci * On success, return a pointer to the new, combined, runlist. Note, both 377987da915Sopenharmony_ci * runlists @dst and @src are deallocated before returning so you cannot use 378987da915Sopenharmony_ci * the pointers for anything any more. (Strictly speaking the returned runlist 379987da915Sopenharmony_ci * may be the same as @dst but this is irrelevant.) 380987da915Sopenharmony_ci * 381987da915Sopenharmony_ci * On error, return NULL, with errno set to the error code. Both runlists are 382987da915Sopenharmony_ci * left unmodified. 383987da915Sopenharmony_ci */ 384987da915Sopenharmony_cistatic runlist_element *ntfs_rl_replace(runlist_element *dst, int dsize, 385987da915Sopenharmony_ci runlist_element *src, int ssize, 386987da915Sopenharmony_ci int loc) 387987da915Sopenharmony_ci{ 388987da915Sopenharmony_ci signed delta; 389987da915Sopenharmony_ci BOOL left = FALSE; /* Left end of @src needs merging */ 390987da915Sopenharmony_ci BOOL right = FALSE; /* Right end of @src needs merging */ 391987da915Sopenharmony_ci int tail; /* Start of tail of @dst */ 392987da915Sopenharmony_ci int marker; /* End of the inserted runs */ 393987da915Sopenharmony_ci 394987da915Sopenharmony_ci if (!dst || !src) { 395987da915Sopenharmony_ci ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL " 396987da915Sopenharmony_ci "pointer!\n"); 397987da915Sopenharmony_ci errno = EINVAL; 398987da915Sopenharmony_ci return NULL; 399987da915Sopenharmony_ci } 400987da915Sopenharmony_ci 401987da915Sopenharmony_ci /* First, see if the left and right ends need merging. */ 402987da915Sopenharmony_ci if ((loc + 1) < dsize) 403987da915Sopenharmony_ci right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1); 404987da915Sopenharmony_ci if (loc > 0) 405987da915Sopenharmony_ci left = ntfs_rl_are_mergeable(dst + loc - 1, src); 406987da915Sopenharmony_ci 407987da915Sopenharmony_ci /* Allocate some space. We'll need less if the left, right, or both 408987da915Sopenharmony_ci * ends get merged. The -1 accounts for the run being replaced. 409987da915Sopenharmony_ci */ 410987da915Sopenharmony_ci delta = ssize - 1 - left - right; 411987da915Sopenharmony_ci if (delta > 0) { 412987da915Sopenharmony_ci dst = ntfs_rl_realloc(dst, dsize, dsize + delta); 413987da915Sopenharmony_ci if (!dst) 414987da915Sopenharmony_ci return NULL; 415987da915Sopenharmony_ci } 416987da915Sopenharmony_ci /* 417987da915Sopenharmony_ci * We are guaranteed to succeed from here so can start modifying the 418987da915Sopenharmony_ci * original runlists. 419987da915Sopenharmony_ci */ 420987da915Sopenharmony_ci 421987da915Sopenharmony_ci /* First, merge the left and right ends, if necessary. */ 422987da915Sopenharmony_ci if (right) 423987da915Sopenharmony_ci __ntfs_rl_merge(src + ssize - 1, dst + loc + 1); 424987da915Sopenharmony_ci if (left) 425987da915Sopenharmony_ci __ntfs_rl_merge(dst + loc - 1, src); 426987da915Sopenharmony_ci 427987da915Sopenharmony_ci /* 428987da915Sopenharmony_ci * tail - Offset of the tail of @dst 429987da915Sopenharmony_ci * Nominally: @tail = @loc + 1 (location, skipping the replaced run) 430987da915Sopenharmony_ci * If "right", then one of @dst's runs is already merged into @src. 431987da915Sopenharmony_ci */ 432987da915Sopenharmony_ci tail = loc + right + 1; 433987da915Sopenharmony_ci 434987da915Sopenharmony_ci /* 435987da915Sopenharmony_ci * marker - First run after the @src runs that have been inserted 436987da915Sopenharmony_ci * Nominally: @marker = @loc + @ssize (location + number of runs in @src) 437987da915Sopenharmony_ci * If "left", then the first run in @src has been merged with one in @dst. 438987da915Sopenharmony_ci */ 439987da915Sopenharmony_ci marker = loc + ssize - left; 440987da915Sopenharmony_ci 441987da915Sopenharmony_ci /* Move the tail of @dst out of the way, then copy in @src. */ 442987da915Sopenharmony_ci ntfs_rl_mm(dst, marker, tail, dsize - tail); 443987da915Sopenharmony_ci ntfs_rl_mc(dst, loc, src, left, ssize - left); 444987da915Sopenharmony_ci 445987da915Sopenharmony_ci /* We may have changed the length of the file, so fix the end marker */ 446987da915Sopenharmony_ci if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT)) 447987da915Sopenharmony_ci dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length; 448987da915Sopenharmony_ci 449987da915Sopenharmony_ci return dst; 450987da915Sopenharmony_ci} 451987da915Sopenharmony_ci 452987da915Sopenharmony_ci/** 453987da915Sopenharmony_ci * ntfs_rl_split - insert a runlist into the centre of a hole 454987da915Sopenharmony_ci * @dst: original runlist to be worked on 455987da915Sopenharmony_ci * @dsize: number of elements in @dst (including end marker) 456987da915Sopenharmony_ci * @src: new runlist to be inserted 457987da915Sopenharmony_ci * @ssize: number of elements in @src (excluding end marker) 458987da915Sopenharmony_ci * @loc: index in runlist @dst at which to split and insert @src 459987da915Sopenharmony_ci * 460987da915Sopenharmony_ci * Split the runlist @dst at @loc into two and insert @new in between the two 461987da915Sopenharmony_ci * fragments. No merging of runlists is necessary. Adjust the size of the 462987da915Sopenharmony_ci * holes either side. 463987da915Sopenharmony_ci * 464987da915Sopenharmony_ci * On success, return a pointer to the new, combined, runlist. Note, both 465987da915Sopenharmony_ci * runlists @dst and @src are deallocated before returning so you cannot use 466987da915Sopenharmony_ci * the pointers for anything any more. (Strictly speaking the returned runlist 467987da915Sopenharmony_ci * may be the same as @dst but this is irrelevant.) 468987da915Sopenharmony_ci * 469987da915Sopenharmony_ci * On error, return NULL, with errno set to the error code. Both runlists are 470987da915Sopenharmony_ci * left unmodified. 471987da915Sopenharmony_ci */ 472987da915Sopenharmony_cistatic runlist_element *ntfs_rl_split(runlist_element *dst, int dsize, 473987da915Sopenharmony_ci runlist_element *src, int ssize, int loc) 474987da915Sopenharmony_ci{ 475987da915Sopenharmony_ci if (!dst || !src) { 476987da915Sopenharmony_ci ntfs_log_debug("Eeek. ntfs_rl_split() invoked with NULL pointer!\n"); 477987da915Sopenharmony_ci errno = EINVAL; 478987da915Sopenharmony_ci return NULL; 479987da915Sopenharmony_ci } 480987da915Sopenharmony_ci 481987da915Sopenharmony_ci /* Space required: @dst size + @src size + one new hole. */ 482987da915Sopenharmony_ci dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1); 483987da915Sopenharmony_ci if (!dst) 484987da915Sopenharmony_ci return dst; 485987da915Sopenharmony_ci /* 486987da915Sopenharmony_ci * We are guaranteed to succeed from here so can start modifying the 487987da915Sopenharmony_ci * original runlists. 488987da915Sopenharmony_ci */ 489987da915Sopenharmony_ci 490987da915Sopenharmony_ci /* Move the tail of @dst out of the way, then copy in @src. */ 491987da915Sopenharmony_ci ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc); 492987da915Sopenharmony_ci ntfs_rl_mc(dst, loc + 1, src, 0, ssize); 493987da915Sopenharmony_ci 494987da915Sopenharmony_ci /* Adjust the size of the holes either size of @src. */ 495987da915Sopenharmony_ci dst[loc].length = dst[loc+1].vcn - dst[loc].vcn; 496987da915Sopenharmony_ci dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length; 497987da915Sopenharmony_ci dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn; 498987da915Sopenharmony_ci 499987da915Sopenharmony_ci return dst; 500987da915Sopenharmony_ci} 501987da915Sopenharmony_ci 502987da915Sopenharmony_ci 503987da915Sopenharmony_ci/** 504987da915Sopenharmony_ci * ntfs_runlists_merge_i - see ntfs_runlists_merge 505987da915Sopenharmony_ci */ 506987da915Sopenharmony_cistatic runlist_element *ntfs_runlists_merge_i(runlist_element *drl, 507987da915Sopenharmony_ci runlist_element *srl) 508987da915Sopenharmony_ci{ 509987da915Sopenharmony_ci int di, si; /* Current index into @[ds]rl. */ 510987da915Sopenharmony_ci int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */ 511987da915Sopenharmony_ci int dins; /* Index into @drl at which to insert @srl. */ 512987da915Sopenharmony_ci int dend, send; /* Last index into @[ds]rl. */ 513987da915Sopenharmony_ci int dfinal, sfinal; /* The last index into @[ds]rl with 514987da915Sopenharmony_ci lcn >= LCN_HOLE. */ 515987da915Sopenharmony_ci int marker = 0; 516987da915Sopenharmony_ci VCN marker_vcn = 0; 517987da915Sopenharmony_ci 518987da915Sopenharmony_ci ntfs_log_debug("dst:\n"); 519987da915Sopenharmony_ci ntfs_debug_runlist_dump(drl); 520987da915Sopenharmony_ci ntfs_log_debug("src:\n"); 521987da915Sopenharmony_ci ntfs_debug_runlist_dump(srl); 522987da915Sopenharmony_ci 523987da915Sopenharmony_ci /* Check for silly calling... */ 524987da915Sopenharmony_ci if (!srl) 525987da915Sopenharmony_ci return drl; 526987da915Sopenharmony_ci 527987da915Sopenharmony_ci /* Check for the case where the first mapping is being done now. */ 528987da915Sopenharmony_ci if (!drl) { 529987da915Sopenharmony_ci drl = srl; 530987da915Sopenharmony_ci /* Complete the source runlist if necessary. */ 531987da915Sopenharmony_ci if (drl[0].vcn) { 532987da915Sopenharmony_ci /* Scan to the end of the source runlist. */ 533987da915Sopenharmony_ci for (dend = 0; drl[dend].length; dend++) 534987da915Sopenharmony_ci ; 535987da915Sopenharmony_ci dend++; 536987da915Sopenharmony_ci drl = ntfs_rl_realloc(drl, dend, dend + 1); 537987da915Sopenharmony_ci if (!drl) 538987da915Sopenharmony_ci return drl; 539987da915Sopenharmony_ci /* Insert start element at the front of the runlist. */ 540987da915Sopenharmony_ci ntfs_rl_mm(drl, 1, 0, dend); 541987da915Sopenharmony_ci drl[0].vcn = 0; 542987da915Sopenharmony_ci drl[0].lcn = LCN_RL_NOT_MAPPED; 543987da915Sopenharmony_ci drl[0].length = drl[1].vcn; 544987da915Sopenharmony_ci } 545987da915Sopenharmony_ci goto finished; 546987da915Sopenharmony_ci } 547987da915Sopenharmony_ci 548987da915Sopenharmony_ci si = di = 0; 549987da915Sopenharmony_ci 550987da915Sopenharmony_ci /* Skip any unmapped start element(s) in the source runlist. */ 551987da915Sopenharmony_ci while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE) 552987da915Sopenharmony_ci si++; 553987da915Sopenharmony_ci 554987da915Sopenharmony_ci /* Can't have an entirely unmapped source runlist. */ 555987da915Sopenharmony_ci if (!srl[si].length) { 556987da915Sopenharmony_ci errno = EINVAL; 557987da915Sopenharmony_ci ntfs_log_perror("%s: unmapped source runlist", __FUNCTION__); 558987da915Sopenharmony_ci return NULL; 559987da915Sopenharmony_ci } 560987da915Sopenharmony_ci 561987da915Sopenharmony_ci /* Record the starting points. */ 562987da915Sopenharmony_ci sstart = si; 563987da915Sopenharmony_ci 564987da915Sopenharmony_ci /* 565987da915Sopenharmony_ci * Skip forward in @drl until we reach the position where @srl needs to 566987da915Sopenharmony_ci * be inserted. If we reach the end of @drl, @srl just needs to be 567987da915Sopenharmony_ci * appended to @drl. 568987da915Sopenharmony_ci */ 569987da915Sopenharmony_ci for (; drl[di].length; di++) { 570987da915Sopenharmony_ci if (drl[di].vcn + drl[di].length > srl[sstart].vcn) 571987da915Sopenharmony_ci break; 572987da915Sopenharmony_ci } 573987da915Sopenharmony_ci dins = di; 574987da915Sopenharmony_ci 575987da915Sopenharmony_ci /* Sanity check for illegal overlaps. */ 576987da915Sopenharmony_ci if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) && 577987da915Sopenharmony_ci (srl[si].lcn >= 0)) { 578987da915Sopenharmony_ci errno = ERANGE; 579987da915Sopenharmony_ci ntfs_log_perror("Run lists overlap. Cannot merge"); 580987da915Sopenharmony_ci return NULL; 581987da915Sopenharmony_ci } 582987da915Sopenharmony_ci 583987da915Sopenharmony_ci /* Scan to the end of both runlists in order to know their sizes. */ 584987da915Sopenharmony_ci for (send = si; srl[send].length; send++) 585987da915Sopenharmony_ci ; 586987da915Sopenharmony_ci for (dend = di; drl[dend].length; dend++) 587987da915Sopenharmony_ci ; 588987da915Sopenharmony_ci 589987da915Sopenharmony_ci if (srl[send].lcn == (LCN)LCN_ENOENT) 590987da915Sopenharmony_ci marker_vcn = srl[marker = send].vcn; 591987da915Sopenharmony_ci 592987da915Sopenharmony_ci /* Scan to the last element with lcn >= LCN_HOLE. */ 593987da915Sopenharmony_ci for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--) 594987da915Sopenharmony_ci ; 595987da915Sopenharmony_ci for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--) 596987da915Sopenharmony_ci ; 597987da915Sopenharmony_ci 598987da915Sopenharmony_ci { 599987da915Sopenharmony_ci BOOL start; 600987da915Sopenharmony_ci BOOL finish; 601987da915Sopenharmony_ci int ds = dend + 1; /* Number of elements in drl & srl */ 602987da915Sopenharmony_ci int ss = sfinal - sstart + 1; 603987da915Sopenharmony_ci 604987da915Sopenharmony_ci start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */ 605987da915Sopenharmony_ci (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */ 606987da915Sopenharmony_ci finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */ 607987da915Sopenharmony_ci ((drl[dins].vcn + drl[dins].length) <= /* End of hole */ 608987da915Sopenharmony_ci (srl[send - 1].vcn + srl[send - 1].length))); 609987da915Sopenharmony_ci 610987da915Sopenharmony_ci /* Or we'll lose an end marker */ 611987da915Sopenharmony_ci if (finish && !drl[dins].length) 612987da915Sopenharmony_ci ss++; 613987da915Sopenharmony_ci if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn)) 614987da915Sopenharmony_ci finish = FALSE; 615987da915Sopenharmony_ci 616987da915Sopenharmony_ci ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend); 617987da915Sopenharmony_ci ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send); 618987da915Sopenharmony_ci ntfs_log_debug("start = %i, finish = %i\n", start, finish); 619987da915Sopenharmony_ci ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins); 620987da915Sopenharmony_ci 621987da915Sopenharmony_ci if (start) { 622987da915Sopenharmony_ci if (finish) 623987da915Sopenharmony_ci drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins); 624987da915Sopenharmony_ci else 625987da915Sopenharmony_ci drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins); 626987da915Sopenharmony_ci } else { 627987da915Sopenharmony_ci if (finish) 628987da915Sopenharmony_ci drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins); 629987da915Sopenharmony_ci else 630987da915Sopenharmony_ci drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins); 631987da915Sopenharmony_ci } 632987da915Sopenharmony_ci if (!drl) { 633987da915Sopenharmony_ci ntfs_log_perror("Merge failed"); 634987da915Sopenharmony_ci return drl; 635987da915Sopenharmony_ci } 636987da915Sopenharmony_ci free(srl); 637987da915Sopenharmony_ci if (marker) { 638987da915Sopenharmony_ci ntfs_log_debug("Triggering marker code.\n"); 639987da915Sopenharmony_ci for (ds = dend; drl[ds].length; ds++) 640987da915Sopenharmony_ci ; 641987da915Sopenharmony_ci /* We only need to care if @srl ended after @drl. */ 642987da915Sopenharmony_ci if (drl[ds].vcn <= marker_vcn) { 643987da915Sopenharmony_ci int slots = 0; 644987da915Sopenharmony_ci 645987da915Sopenharmony_ci if (drl[ds].vcn == marker_vcn) { 646987da915Sopenharmony_ci ntfs_log_debug("Old marker = %lli, replacing with " 647987da915Sopenharmony_ci "LCN_ENOENT.\n", 648987da915Sopenharmony_ci (long long)drl[ds].lcn); 649987da915Sopenharmony_ci drl[ds].lcn = (LCN)LCN_ENOENT; 650987da915Sopenharmony_ci goto finished; 651987da915Sopenharmony_ci } 652987da915Sopenharmony_ci /* 653987da915Sopenharmony_ci * We need to create an unmapped runlist element in 654987da915Sopenharmony_ci * @drl or extend an existing one before adding the 655987da915Sopenharmony_ci * ENOENT terminator. 656987da915Sopenharmony_ci */ 657987da915Sopenharmony_ci if (drl[ds].lcn == (LCN)LCN_ENOENT) { 658987da915Sopenharmony_ci ds--; 659987da915Sopenharmony_ci slots = 1; 660987da915Sopenharmony_ci } 661987da915Sopenharmony_ci if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) { 662987da915Sopenharmony_ci /* Add an unmapped runlist element. */ 663987da915Sopenharmony_ci if (!slots) { 664987da915Sopenharmony_ci /* FIXME/TODO: We need to have the 665987da915Sopenharmony_ci * extra memory already! (AIA) 666987da915Sopenharmony_ci */ 667987da915Sopenharmony_ci drl = ntfs_rl_realloc(drl, ds, ds + 2); 668987da915Sopenharmony_ci if (!drl) 669987da915Sopenharmony_ci goto critical_error; 670987da915Sopenharmony_ci slots = 2; 671987da915Sopenharmony_ci } 672987da915Sopenharmony_ci ds++; 673987da915Sopenharmony_ci /* Need to set vcn if it isn't set already. */ 674987da915Sopenharmony_ci if (slots != 1) 675987da915Sopenharmony_ci drl[ds].vcn = drl[ds - 1].vcn + 676987da915Sopenharmony_ci drl[ds - 1].length; 677987da915Sopenharmony_ci drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED; 678987da915Sopenharmony_ci /* We now used up a slot. */ 679987da915Sopenharmony_ci slots--; 680987da915Sopenharmony_ci } 681987da915Sopenharmony_ci drl[ds].length = marker_vcn - drl[ds].vcn; 682987da915Sopenharmony_ci /* Finally add the ENOENT terminator. */ 683987da915Sopenharmony_ci ds++; 684987da915Sopenharmony_ci if (!slots) { 685987da915Sopenharmony_ci /* FIXME/TODO: We need to have the extra 686987da915Sopenharmony_ci * memory already! (AIA) 687987da915Sopenharmony_ci */ 688987da915Sopenharmony_ci drl = ntfs_rl_realloc(drl, ds, ds + 1); 689987da915Sopenharmony_ci if (!drl) 690987da915Sopenharmony_ci goto critical_error; 691987da915Sopenharmony_ci } 692987da915Sopenharmony_ci drl[ds].vcn = marker_vcn; 693987da915Sopenharmony_ci drl[ds].lcn = (LCN)LCN_ENOENT; 694987da915Sopenharmony_ci drl[ds].length = (s64)0; 695987da915Sopenharmony_ci } 696987da915Sopenharmony_ci } 697987da915Sopenharmony_ci } 698987da915Sopenharmony_ci 699987da915Sopenharmony_cifinished: 700987da915Sopenharmony_ci /* The merge was completed successfully. */ 701987da915Sopenharmony_ci ntfs_log_debug("Merged runlist:\n"); 702987da915Sopenharmony_ci ntfs_debug_runlist_dump(drl); 703987da915Sopenharmony_ci return drl; 704987da915Sopenharmony_ci 705987da915Sopenharmony_cicritical_error: 706987da915Sopenharmony_ci /* Critical error! We cannot afford to fail here. */ 707987da915Sopenharmony_ci ntfs_log_perror("libntfs: Critical error"); 708987da915Sopenharmony_ci ntfs_log_debug("Forcing segmentation fault!\n"); 709987da915Sopenharmony_ci marker_vcn = ((runlist*)NULL)->lcn; 710987da915Sopenharmony_ci return drl; 711987da915Sopenharmony_ci} 712987da915Sopenharmony_ci 713987da915Sopenharmony_ci/** 714987da915Sopenharmony_ci * ntfs_runlists_merge - merge two runlists into one 715987da915Sopenharmony_ci * @drl: original runlist to be worked on 716987da915Sopenharmony_ci * @srl: new runlist to be merged into @drl 717987da915Sopenharmony_ci * 718987da915Sopenharmony_ci * First we sanity check the two runlists @srl and @drl to make sure that they 719987da915Sopenharmony_ci * are sensible and can be merged. The runlist @srl must be either after the 720987da915Sopenharmony_ci * runlist @drl or completely within a hole (or unmapped region) in @drl. 721987da915Sopenharmony_ci * 722987da915Sopenharmony_ci * Merging of runlists is necessary in two cases: 723987da915Sopenharmony_ci * 1. When attribute lists are used and a further extent is being mapped. 724987da915Sopenharmony_ci * 2. When new clusters are allocated to fill a hole or extend a file. 725987da915Sopenharmony_ci * 726987da915Sopenharmony_ci * There are four possible ways @srl can be merged. It can: 727987da915Sopenharmony_ci * - be inserted at the beginning of a hole, 728987da915Sopenharmony_ci * - split the hole in two and be inserted between the two fragments, 729987da915Sopenharmony_ci * - be appended at the end of a hole, or it can 730987da915Sopenharmony_ci * - replace the whole hole. 731987da915Sopenharmony_ci * It can also be appended to the end of the runlist, which is just a variant 732987da915Sopenharmony_ci * of the insert case. 733987da915Sopenharmony_ci * 734987da915Sopenharmony_ci * On success, return a pointer to the new, combined, runlist. Note, both 735987da915Sopenharmony_ci * runlists @drl and @srl are deallocated before returning so you cannot use 736987da915Sopenharmony_ci * the pointers for anything any more. (Strictly speaking the returned runlist 737987da915Sopenharmony_ci * may be the same as @dst but this is irrelevant.) 738987da915Sopenharmony_ci * 739987da915Sopenharmony_ci * On error, return NULL, with errno set to the error code. Both runlists are 740987da915Sopenharmony_ci * left unmodified. The following error codes are defined: 741987da915Sopenharmony_ci * ENOMEM Not enough memory to allocate runlist array. 742987da915Sopenharmony_ci * EINVAL Invalid parameters were passed in. 743987da915Sopenharmony_ci * ERANGE The runlists overlap and cannot be merged. 744987da915Sopenharmony_ci */ 745987da915Sopenharmony_cirunlist_element *ntfs_runlists_merge(runlist_element *drl, 746987da915Sopenharmony_ci runlist_element *srl) 747987da915Sopenharmony_ci{ 748987da915Sopenharmony_ci runlist_element *rl; 749987da915Sopenharmony_ci 750987da915Sopenharmony_ci ntfs_log_enter("Entering\n"); 751987da915Sopenharmony_ci rl = ntfs_runlists_merge_i(drl, srl); 752987da915Sopenharmony_ci ntfs_log_leave("\n"); 753987da915Sopenharmony_ci return rl; 754987da915Sopenharmony_ci} 755987da915Sopenharmony_ci 756987da915Sopenharmony_ci/** 757987da915Sopenharmony_ci * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist 758987da915Sopenharmony_ci * @vol: ntfs volume on which the attribute resides 759987da915Sopenharmony_ci * @attr: attribute record whose mapping pairs array to decompress 760987da915Sopenharmony_ci * @old_rl: optional runlist in which to insert @attr's runlist 761987da915Sopenharmony_ci * 762987da915Sopenharmony_ci * Decompress the attribute @attr's mapping pairs array into a runlist. On 763987da915Sopenharmony_ci * success, return the decompressed runlist. 764987da915Sopenharmony_ci * 765987da915Sopenharmony_ci * If @old_rl is not NULL, decompressed runlist is inserted into the 766987da915Sopenharmony_ci * appropriate place in @old_rl and the resultant, combined runlist is 767987da915Sopenharmony_ci * returned. The original @old_rl is deallocated. 768987da915Sopenharmony_ci * 769987da915Sopenharmony_ci * On error, return NULL with errno set to the error code. @old_rl is left 770987da915Sopenharmony_ci * unmodified in that case. 771987da915Sopenharmony_ci * 772987da915Sopenharmony_ci * The following error codes are defined: 773987da915Sopenharmony_ci * ENOMEM Not enough memory to allocate runlist array. 774987da915Sopenharmony_ci * EIO Corrupt runlist. 775987da915Sopenharmony_ci * EINVAL Invalid parameters were passed in. 776987da915Sopenharmony_ci * ERANGE The two runlists overlap. 777987da915Sopenharmony_ci * 778987da915Sopenharmony_ci * FIXME: For now we take the conceptionally simplest approach of creating the 779987da915Sopenharmony_ci * new runlist disregarding the already existing one and then splicing the 780987da915Sopenharmony_ci * two into one, if that is possible (we check for overlap and discard the new 781987da915Sopenharmony_ci * runlist if overlap present before returning NULL, with errno = ERANGE). 782987da915Sopenharmony_ci */ 783987da915Sopenharmony_cistatic runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, 784987da915Sopenharmony_ci const ATTR_RECORD *attr, runlist_element *old_rl) 785987da915Sopenharmony_ci{ 786987da915Sopenharmony_ci VCN vcn; /* Current vcn. */ 787987da915Sopenharmony_ci LCN lcn; /* Current lcn. */ 788987da915Sopenharmony_ci s64 deltaxcn; /* Change in [vl]cn. */ 789987da915Sopenharmony_ci runlist_element *rl; /* The output runlist. */ 790987da915Sopenharmony_ci const u8 *buf; /* Current position in mapping pairs array. */ 791987da915Sopenharmony_ci const u8 *attr_end; /* End of attribute. */ 792987da915Sopenharmony_ci int err, rlsize; /* Size of runlist buffer. */ 793987da915Sopenharmony_ci u16 rlpos; /* Current runlist position in units of 794987da915Sopenharmony_ci runlist_elements. */ 795987da915Sopenharmony_ci u8 b; /* Current byte offset in buf. */ 796987da915Sopenharmony_ci 797987da915Sopenharmony_ci ntfs_log_trace("Entering for attr 0x%x.\n", 798987da915Sopenharmony_ci (unsigned)le32_to_cpu(attr->type)); 799987da915Sopenharmony_ci /* Make sure attr exists and is non-resident. */ 800987da915Sopenharmony_ci if (!attr || !attr->non_resident || 801987da915Sopenharmony_ci sle64_to_cpu(attr->lowest_vcn) < (VCN)0) { 802987da915Sopenharmony_ci errno = EINVAL; 803987da915Sopenharmony_ci return NULL; 804987da915Sopenharmony_ci } 805987da915Sopenharmony_ci /* Start at vcn = lowest_vcn and lcn 0. */ 806987da915Sopenharmony_ci vcn = sle64_to_cpu(attr->lowest_vcn); 807987da915Sopenharmony_ci lcn = 0; 808987da915Sopenharmony_ci /* Get start of the mapping pairs array. */ 809987da915Sopenharmony_ci buf = (const u8*)attr + le16_to_cpu(attr->mapping_pairs_offset); 810987da915Sopenharmony_ci attr_end = (const u8*)attr + le32_to_cpu(attr->length); 811987da915Sopenharmony_ci if (buf < (const u8*)attr || buf > attr_end) { 812987da915Sopenharmony_ci ntfs_log_debug("Corrupt attribute.\n"); 813987da915Sopenharmony_ci errno = EIO; 814987da915Sopenharmony_ci return NULL; 815987da915Sopenharmony_ci } 816987da915Sopenharmony_ci /* Current position in runlist array. */ 817987da915Sopenharmony_ci rlpos = 0; 818987da915Sopenharmony_ci /* Allocate first 4kiB block and set current runlist size to 4kiB. */ 819987da915Sopenharmony_ci rlsize = 0x1000; 820987da915Sopenharmony_ci rl = ntfs_malloc(rlsize); 821987da915Sopenharmony_ci if (!rl) 822987da915Sopenharmony_ci return NULL; 823987da915Sopenharmony_ci /* Insert unmapped starting element if necessary. */ 824987da915Sopenharmony_ci if (vcn) { 825987da915Sopenharmony_ci rl->vcn = (VCN)0; 826987da915Sopenharmony_ci rl->lcn = (LCN)LCN_RL_NOT_MAPPED; 827987da915Sopenharmony_ci rl->length = vcn; 828987da915Sopenharmony_ci rlpos++; 829987da915Sopenharmony_ci } 830987da915Sopenharmony_ci while (buf < attr_end && *buf) { 831987da915Sopenharmony_ci /* 832987da915Sopenharmony_ci * Allocate more memory if needed, including space for the 833987da915Sopenharmony_ci * not-mapped and terminator elements. 834987da915Sopenharmony_ci */ 835987da915Sopenharmony_ci if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { 836987da915Sopenharmony_ci runlist_element *rl2; 837987da915Sopenharmony_ci 838987da915Sopenharmony_ci rlsize += 0x1000; 839987da915Sopenharmony_ci rl2 = realloc(rl, rlsize); 840987da915Sopenharmony_ci if (!rl2) { 841987da915Sopenharmony_ci int eo = errno; 842987da915Sopenharmony_ci free(rl); 843987da915Sopenharmony_ci errno = eo; 844987da915Sopenharmony_ci return NULL; 845987da915Sopenharmony_ci } 846987da915Sopenharmony_ci rl = rl2; 847987da915Sopenharmony_ci } 848987da915Sopenharmony_ci /* Enter the current vcn into the current runlist element. */ 849987da915Sopenharmony_ci rl[rlpos].vcn = vcn; 850987da915Sopenharmony_ci /* 851987da915Sopenharmony_ci * Get the change in vcn, i.e. the run length in clusters. 852987da915Sopenharmony_ci * Doing it this way ensures that we signextend negative values. 853987da915Sopenharmony_ci * A negative run length doesn't make any sense, but hey, I 854987da915Sopenharmony_ci * didn't make up the NTFS specs and Windows NT4 treats the run 855987da915Sopenharmony_ci * length as a signed value so that's how it is... 856987da915Sopenharmony_ci */ 857987da915Sopenharmony_ci b = *buf & 0xf; 858987da915Sopenharmony_ci if (b) { 859987da915Sopenharmony_ci if (buf + b > attr_end) 860987da915Sopenharmony_ci goto io_error; 861987da915Sopenharmony_ci for (deltaxcn = (s8)buf[b--]; b; b--) 862987da915Sopenharmony_ci deltaxcn = (deltaxcn << 8) + buf[b]; 863987da915Sopenharmony_ci } else { /* The length entry is compulsory. */ 864987da915Sopenharmony_ci ntfs_log_debug("Missing length entry in mapping pairs " 865987da915Sopenharmony_ci "array.\n"); 866987da915Sopenharmony_ci deltaxcn = (s64)-1; 867987da915Sopenharmony_ci } 868987da915Sopenharmony_ci /* 869987da915Sopenharmony_ci * Assume a negative length to indicate data corruption and 870987da915Sopenharmony_ci * hence clean-up and return NULL. 871987da915Sopenharmony_ci */ 872987da915Sopenharmony_ci if (deltaxcn < 0) { 873987da915Sopenharmony_ci ntfs_log_debug("Invalid length in mapping pairs array.\n"); 874987da915Sopenharmony_ci goto err_out; 875987da915Sopenharmony_ci } 876987da915Sopenharmony_ci /* 877987da915Sopenharmony_ci * Enter the current run length into the current runlist 878987da915Sopenharmony_ci * element. 879987da915Sopenharmony_ci */ 880987da915Sopenharmony_ci rl[rlpos].length = deltaxcn; 881987da915Sopenharmony_ci /* Increment the current vcn by the current run length. */ 882987da915Sopenharmony_ci vcn += deltaxcn; 883987da915Sopenharmony_ci /* 884987da915Sopenharmony_ci * There might be no lcn change at all, as is the case for 885987da915Sopenharmony_ci * sparse clusters on NTFS 3.0+, in which case we set the lcn 886987da915Sopenharmony_ci * to LCN_HOLE. 887987da915Sopenharmony_ci */ 888987da915Sopenharmony_ci if (!(*buf & 0xf0)) 889987da915Sopenharmony_ci rl[rlpos].lcn = (LCN)LCN_HOLE; 890987da915Sopenharmony_ci else { 891987da915Sopenharmony_ci /* Get the lcn change which really can be negative. */ 892987da915Sopenharmony_ci u8 b2 = *buf & 0xf; 893987da915Sopenharmony_ci b = b2 + ((*buf >> 4) & 0xf); 894987da915Sopenharmony_ci if (buf + b > attr_end) 895987da915Sopenharmony_ci goto io_error; 896987da915Sopenharmony_ci for (deltaxcn = (s8)buf[b--]; b > b2; b--) 897987da915Sopenharmony_ci deltaxcn = (deltaxcn << 8) + buf[b]; 898987da915Sopenharmony_ci /* Change the current lcn to it's new value. */ 899987da915Sopenharmony_ci lcn += deltaxcn; 900987da915Sopenharmony_ci#ifdef DEBUG 901987da915Sopenharmony_ci /* 902987da915Sopenharmony_ci * On NTFS 1.2-, apparently can have lcn == -1 to 903987da915Sopenharmony_ci * indicate a hole. But we haven't verified ourselves 904987da915Sopenharmony_ci * whether it is really the lcn or the deltaxcn that is 905987da915Sopenharmony_ci * -1. So if either is found give us a message so we 906987da915Sopenharmony_ci * can investigate it further! 907987da915Sopenharmony_ci */ 908987da915Sopenharmony_ci if (vol->major_ver < 3) { 909987da915Sopenharmony_ci if (deltaxcn == (LCN)-1) 910987da915Sopenharmony_ci ntfs_log_debug("lcn delta == -1\n"); 911987da915Sopenharmony_ci if (lcn == (LCN)-1) 912987da915Sopenharmony_ci ntfs_log_debug("lcn == -1\n"); 913987da915Sopenharmony_ci } 914987da915Sopenharmony_ci#endif 915987da915Sopenharmony_ci /* Check lcn is not below -1. */ 916987da915Sopenharmony_ci if (lcn < (LCN)-1) { 917987da915Sopenharmony_ci ntfs_log_debug("Invalid LCN < -1 in mapping pairs " 918987da915Sopenharmony_ci "array.\n"); 919987da915Sopenharmony_ci goto err_out; 920987da915Sopenharmony_ci } 921987da915Sopenharmony_ci /* chkdsk accepts zero-sized runs only for holes */ 922987da915Sopenharmony_ci if ((lcn != (LCN)-1) && !rl[rlpos].length) { 923987da915Sopenharmony_ci ntfs_log_debug( 924987da915Sopenharmony_ci "Invalid zero-sized data run.\n"); 925987da915Sopenharmony_ci goto err_out; 926987da915Sopenharmony_ci } 927987da915Sopenharmony_ci /* Enter the current lcn into the runlist element. */ 928987da915Sopenharmony_ci rl[rlpos].lcn = lcn; 929987da915Sopenharmony_ci } 930987da915Sopenharmony_ci /* Get to the next runlist element, skipping zero-sized holes */ 931987da915Sopenharmony_ci if (rl[rlpos].length) 932987da915Sopenharmony_ci rlpos++; 933987da915Sopenharmony_ci /* Increment the buffer position to the next mapping pair. */ 934987da915Sopenharmony_ci buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; 935987da915Sopenharmony_ci } 936987da915Sopenharmony_ci if (buf >= attr_end) 937987da915Sopenharmony_ci goto io_error; 938987da915Sopenharmony_ci /* 939987da915Sopenharmony_ci * If there is a highest_vcn specified, it must be equal to the final 940987da915Sopenharmony_ci * vcn in the runlist - 1, or something has gone badly wrong. 941987da915Sopenharmony_ci */ 942987da915Sopenharmony_ci deltaxcn = sle64_to_cpu(attr->highest_vcn); 943987da915Sopenharmony_ci if (deltaxcn && vcn - 1 != deltaxcn) { 944987da915Sopenharmony_cimpa_err: 945987da915Sopenharmony_ci ntfs_log_debug("Corrupt mapping pairs array in non-resident " 946987da915Sopenharmony_ci "attribute.\n"); 947987da915Sopenharmony_ci goto err_out; 948987da915Sopenharmony_ci } 949987da915Sopenharmony_ci 950987da915Sopenharmony_ci /* 951987da915Sopenharmony_ci * If this is the base of runlist (if 'lowest_vcn' is 0), then 952987da915Sopenharmony_ci * 'allocated_size' is valid, and we can use it to compute the total 953987da915Sopenharmony_ci * number of clusters across all extents. If the runlist covers all 954987da915Sopenharmony_ci * clusters, then it fits into a single extent and we can terminate 955987da915Sopenharmony_ci * the runlist with LCN_NOENT. Otherwise, we must terminate the runlist 956987da915Sopenharmony_ci * with LCN_RL_NOT_MAPPED and let the caller look for more extents. 957987da915Sopenharmony_ci */ 958987da915Sopenharmony_ci if (!attr->lowest_vcn) { 959987da915Sopenharmony_ci VCN num_clusters; 960987da915Sopenharmony_ci 961987da915Sopenharmony_ci num_clusters = ((sle64_to_cpu(attr->allocated_size) + 962987da915Sopenharmony_ci vol->cluster_size - 1) >> 963987da915Sopenharmony_ci vol->cluster_size_bits); 964987da915Sopenharmony_ci 965987da915Sopenharmony_ci if (num_clusters > vcn) { 966987da915Sopenharmony_ci /* 967987da915Sopenharmony_ci * The runlist doesn't cover all the clusters, so there 968987da915Sopenharmony_ci * must be more extents. 969987da915Sopenharmony_ci */ 970987da915Sopenharmony_ci ntfs_log_debug("More extents to follow; vcn = 0x%llx, " 971987da915Sopenharmony_ci "num_clusters = 0x%llx\n", 972987da915Sopenharmony_ci (long long)vcn, 973987da915Sopenharmony_ci (long long)num_clusters); 974987da915Sopenharmony_ci rl[rlpos].vcn = vcn; 975987da915Sopenharmony_ci vcn += rl[rlpos].length = num_clusters - vcn; 976987da915Sopenharmony_ci rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; 977987da915Sopenharmony_ci rlpos++; 978987da915Sopenharmony_ci } else if (vcn > num_clusters) { 979987da915Sopenharmony_ci /* 980987da915Sopenharmony_ci * There are more VCNs in the runlist than expected, so 981987da915Sopenharmony_ci * the runlist is corrupt. 982987da915Sopenharmony_ci */ 983987da915Sopenharmony_ci ntfs_log_error("Corrupt attribute. vcn = 0x%llx, " 984987da915Sopenharmony_ci "num_clusters = 0x%llx\n", 985987da915Sopenharmony_ci (long long)vcn, 986987da915Sopenharmony_ci (long long)num_clusters); 987987da915Sopenharmony_ci goto mpa_err; 988987da915Sopenharmony_ci } 989987da915Sopenharmony_ci rl[rlpos].lcn = (LCN)LCN_ENOENT; 990987da915Sopenharmony_ci } else /* Not the base extent. There may be more extents to follow. */ 991987da915Sopenharmony_ci rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; 992987da915Sopenharmony_ci 993987da915Sopenharmony_ci /* Setup terminating runlist element. */ 994987da915Sopenharmony_ci rl[rlpos].vcn = vcn; 995987da915Sopenharmony_ci rl[rlpos].length = (s64)0; 996987da915Sopenharmony_ci /* If no existing runlist was specified, we are done. */ 997987da915Sopenharmony_ci if (!old_rl || !old_rl[0].length) { 998987da915Sopenharmony_ci ntfs_log_debug("Mapping pairs array successfully decompressed:\n"); 999987da915Sopenharmony_ci ntfs_debug_runlist_dump(rl); 1000987da915Sopenharmony_ci if (old_rl) 1001987da915Sopenharmony_ci free(old_rl); 1002987da915Sopenharmony_ci return rl; 1003987da915Sopenharmony_ci } 1004987da915Sopenharmony_ci /* Now combine the new and old runlists checking for overlaps. */ 1005987da915Sopenharmony_ci if (rl[0].length) 1006987da915Sopenharmony_ci old_rl = ntfs_runlists_merge(old_rl, rl); 1007987da915Sopenharmony_ci else 1008987da915Sopenharmony_ci free(rl); 1009987da915Sopenharmony_ci if (old_rl) 1010987da915Sopenharmony_ci return old_rl; 1011987da915Sopenharmony_ci err = errno; 1012987da915Sopenharmony_ci free(rl); 1013987da915Sopenharmony_ci ntfs_log_debug("Failed to merge runlists.\n"); 1014987da915Sopenharmony_ci errno = err; 1015987da915Sopenharmony_ci return NULL; 1016987da915Sopenharmony_ciio_error: 1017987da915Sopenharmony_ci ntfs_log_debug("Corrupt attribute.\n"); 1018987da915Sopenharmony_cierr_out: 1019987da915Sopenharmony_ci free(rl); 1020987da915Sopenharmony_ci errno = EIO; 1021987da915Sopenharmony_ci return NULL; 1022987da915Sopenharmony_ci} 1023987da915Sopenharmony_ci 1024987da915Sopenharmony_cirunlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol, 1025987da915Sopenharmony_ci const ATTR_RECORD *attr, runlist_element *old_rl) 1026987da915Sopenharmony_ci{ 1027987da915Sopenharmony_ci runlist_element *rle; 1028987da915Sopenharmony_ci 1029987da915Sopenharmony_ci ntfs_log_enter("Entering\n"); 1030987da915Sopenharmony_ci rle = ntfs_mapping_pairs_decompress_i(vol, attr, old_rl); 1031987da915Sopenharmony_ci ntfs_log_leave("\n"); 1032987da915Sopenharmony_ci return rle; 1033987da915Sopenharmony_ci} 1034987da915Sopenharmony_ci 1035987da915Sopenharmony_ci/** 1036987da915Sopenharmony_ci * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist 1037987da915Sopenharmony_ci * @rl: runlist to use for conversion 1038987da915Sopenharmony_ci * @vcn: vcn to convert 1039987da915Sopenharmony_ci * 1040987da915Sopenharmony_ci * Convert the virtual cluster number @vcn of an attribute into a logical 1041987da915Sopenharmony_ci * cluster number (lcn) of a device using the runlist @rl to map vcns to their 1042987da915Sopenharmony_ci * corresponding lcns. 1043987da915Sopenharmony_ci * 1044987da915Sopenharmony_ci * Since lcns must be >= 0, we use negative return values with special meaning: 1045987da915Sopenharmony_ci * 1046987da915Sopenharmony_ci * Return value Meaning / Description 1047987da915Sopenharmony_ci * ================================================== 1048987da915Sopenharmony_ci * -1 = LCN_HOLE Hole / not allocated on disk. 1049987da915Sopenharmony_ci * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been 1050987da915Sopenharmony_ci * inserted into the runlist yet. 1051987da915Sopenharmony_ci * -3 = LCN_ENOENT There is no such vcn in the attribute. 1052987da915Sopenharmony_ci * -4 = LCN_EINVAL Input parameter error. 1053987da915Sopenharmony_ci */ 1054987da915Sopenharmony_ciLCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn) 1055987da915Sopenharmony_ci{ 1056987da915Sopenharmony_ci int i; 1057987da915Sopenharmony_ci 1058987da915Sopenharmony_ci if (vcn < (VCN)0) 1059987da915Sopenharmony_ci return (LCN)LCN_EINVAL; 1060987da915Sopenharmony_ci /* 1061987da915Sopenharmony_ci * If rl is NULL, assume that we have found an unmapped runlist. The 1062987da915Sopenharmony_ci * caller can then attempt to map it and fail appropriately if 1063987da915Sopenharmony_ci * necessary. 1064987da915Sopenharmony_ci */ 1065987da915Sopenharmony_ci if (!rl) 1066987da915Sopenharmony_ci return (LCN)LCN_RL_NOT_MAPPED; 1067987da915Sopenharmony_ci 1068987da915Sopenharmony_ci /* Catch out of lower bounds vcn. */ 1069987da915Sopenharmony_ci if (vcn < rl[0].vcn) 1070987da915Sopenharmony_ci return (LCN)LCN_ENOENT; 1071987da915Sopenharmony_ci 1072987da915Sopenharmony_ci for (i = 0; rl[i].length; i++) { 1073987da915Sopenharmony_ci if (vcn < rl[i+1].vcn) { 1074987da915Sopenharmony_ci if (rl[i].lcn >= (LCN)0) 1075987da915Sopenharmony_ci return rl[i].lcn + (vcn - rl[i].vcn); 1076987da915Sopenharmony_ci return rl[i].lcn; 1077987da915Sopenharmony_ci } 1078987da915Sopenharmony_ci } 1079987da915Sopenharmony_ci /* 1080987da915Sopenharmony_ci * The terminator element is setup to the correct value, i.e. one of 1081987da915Sopenharmony_ci * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. 1082987da915Sopenharmony_ci */ 1083987da915Sopenharmony_ci if (rl[i].lcn < (LCN)0) 1084987da915Sopenharmony_ci return rl[i].lcn; 1085987da915Sopenharmony_ci /* Just in case... We could replace this with BUG() some day. */ 1086987da915Sopenharmony_ci return (LCN)LCN_ENOENT; 1087987da915Sopenharmony_ci} 1088987da915Sopenharmony_ci 1089987da915Sopenharmony_ci/** 1090987da915Sopenharmony_ci * ntfs_rl_pread - gather read from disk 1091987da915Sopenharmony_ci * @vol: ntfs volume to read from 1092987da915Sopenharmony_ci * @rl: runlist specifying where to read the data from 1093987da915Sopenharmony_ci * @pos: byte position within runlist @rl at which to begin the read 1094987da915Sopenharmony_ci * @count: number of bytes to read 1095987da915Sopenharmony_ci * @b: data buffer into which to read from disk 1096987da915Sopenharmony_ci * 1097987da915Sopenharmony_ci * This function will read @count bytes from the volume @vol to the data buffer 1098987da915Sopenharmony_ci * @b gathering the data as specified by the runlist @rl. The read begins at 1099987da915Sopenharmony_ci * offset @pos into the runlist @rl. 1100987da915Sopenharmony_ci * 1101987da915Sopenharmony_ci * On success, return the number of successfully read bytes. If this number is 1102987da915Sopenharmony_ci * lower than @count this means that the read reached end of file or that an 1103987da915Sopenharmony_ci * error was encountered during the read so that the read is partial. 0 means 1104987da915Sopenharmony_ci * nothing was read (also return 0 when @count is 0). 1105987da915Sopenharmony_ci * 1106987da915Sopenharmony_ci * On error and nothing has been read, return -1 with errno set appropriately 1107987da915Sopenharmony_ci * to the return code of ntfs_pread(), or to EINVAL in case of invalid 1108987da915Sopenharmony_ci * arguments. 1109987da915Sopenharmony_ci * 1110987da915Sopenharmony_ci * NOTE: If we encounter EOF while reading we return EIO because we assume that 1111987da915Sopenharmony_ci * the run list must point to valid locations within the ntfs volume. 1112987da915Sopenharmony_ci */ 1113987da915Sopenharmony_cis64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, 1114987da915Sopenharmony_ci const s64 pos, s64 count, void *b) 1115987da915Sopenharmony_ci{ 1116987da915Sopenharmony_ci s64 bytes_read, to_read, ofs, total; 1117987da915Sopenharmony_ci int err = EIO; 1118987da915Sopenharmony_ci 1119987da915Sopenharmony_ci if (!vol || !rl || pos < 0 || count < 0) { 1120987da915Sopenharmony_ci errno = EINVAL; 1121987da915Sopenharmony_ci ntfs_log_perror("Failed to read runlist [vol: %p rl: %p " 1122987da915Sopenharmony_ci "pos: %lld count: %lld]", vol, rl, 1123987da915Sopenharmony_ci (long long)pos, (long long)count); 1124987da915Sopenharmony_ci return -1; 1125987da915Sopenharmony_ci } 1126987da915Sopenharmony_ci if (!count) 1127987da915Sopenharmony_ci return count; 1128987da915Sopenharmony_ci /* Seek in @rl to the run containing @pos. */ 1129987da915Sopenharmony_ci for (ofs = 0; rl->length && (ofs + (rl->length << 1130987da915Sopenharmony_ci vol->cluster_size_bits) <= pos); rl++) 1131987da915Sopenharmony_ci ofs += (rl->length << vol->cluster_size_bits); 1132987da915Sopenharmony_ci /* Offset in the run at which to begin reading. */ 1133987da915Sopenharmony_ci ofs = pos - ofs; 1134987da915Sopenharmony_ci for (total = 0LL; count; rl++, ofs = 0) { 1135987da915Sopenharmony_ci if (!rl->length) 1136987da915Sopenharmony_ci goto rl_err_out; 1137987da915Sopenharmony_ci if (rl->lcn < (LCN)0) { 1138987da915Sopenharmony_ci if (rl->lcn != (LCN)LCN_HOLE) 1139987da915Sopenharmony_ci goto rl_err_out; 1140987da915Sopenharmony_ci /* It is a hole. Just fill buffer @b with zeroes. */ 1141987da915Sopenharmony_ci to_read = min(count, (rl->length << 1142987da915Sopenharmony_ci vol->cluster_size_bits) - ofs); 1143987da915Sopenharmony_ci memset(b, 0, to_read); 1144987da915Sopenharmony_ci /* Update counters and proceed with next run. */ 1145987da915Sopenharmony_ci total += to_read; 1146987da915Sopenharmony_ci count -= to_read; 1147987da915Sopenharmony_ci b = (u8*)b + to_read; 1148987da915Sopenharmony_ci continue; 1149987da915Sopenharmony_ci } 1150987da915Sopenharmony_ci /* It is a real lcn, read it from the volume. */ 1151987da915Sopenharmony_ci to_read = min(count, (rl->length << vol->cluster_size_bits) - 1152987da915Sopenharmony_ci ofs); 1153987da915Sopenharmony_ciretry: 1154987da915Sopenharmony_ci bytes_read = ntfs_pread(vol->dev, (rl->lcn << 1155987da915Sopenharmony_ci vol->cluster_size_bits) + ofs, to_read, b); 1156987da915Sopenharmony_ci /* If everything ok, update progress counters and continue. */ 1157987da915Sopenharmony_ci if (bytes_read > 0) { 1158987da915Sopenharmony_ci total += bytes_read; 1159987da915Sopenharmony_ci count -= bytes_read; 1160987da915Sopenharmony_ci b = (u8*)b + bytes_read; 1161987da915Sopenharmony_ci continue; 1162987da915Sopenharmony_ci } 1163987da915Sopenharmony_ci /* If the syscall was interrupted, try again. */ 1164987da915Sopenharmony_ci if (bytes_read == (s64)-1 && errno == EINTR) 1165987da915Sopenharmony_ci goto retry; 1166987da915Sopenharmony_ci if (bytes_read == (s64)-1) 1167987da915Sopenharmony_ci err = errno; 1168987da915Sopenharmony_ci goto rl_err_out; 1169987da915Sopenharmony_ci } 1170987da915Sopenharmony_ci /* Finally, return the number of bytes read. */ 1171987da915Sopenharmony_ci return total; 1172987da915Sopenharmony_cirl_err_out: 1173987da915Sopenharmony_ci if (total) 1174987da915Sopenharmony_ci return total; 1175987da915Sopenharmony_ci errno = err; 1176987da915Sopenharmony_ci return -1; 1177987da915Sopenharmony_ci} 1178987da915Sopenharmony_ci 1179987da915Sopenharmony_ci/** 1180987da915Sopenharmony_ci * ntfs_rl_pwrite - scatter write to disk 1181987da915Sopenharmony_ci * @vol: ntfs volume to write to 1182987da915Sopenharmony_ci * @rl: runlist entry specifying where to write the data to 1183987da915Sopenharmony_ci * @ofs: offset in file for runlist element indicated in @rl 1184987da915Sopenharmony_ci * @pos: byte position from runlist beginning at which to begin the write 1185987da915Sopenharmony_ci * @count: number of bytes to write 1186987da915Sopenharmony_ci * @b: data buffer to write to disk 1187987da915Sopenharmony_ci * 1188987da915Sopenharmony_ci * This function will write @count bytes from data buffer @b to the volume @vol 1189987da915Sopenharmony_ci * scattering the data as specified by the runlist @rl. The write begins at 1190987da915Sopenharmony_ci * offset @pos into the runlist @rl. If a run is sparse then the related buffer 1191987da915Sopenharmony_ci * data is ignored which means that the caller must ensure they are consistent. 1192987da915Sopenharmony_ci * 1193987da915Sopenharmony_ci * On success, return the number of successfully written bytes. If this number 1194987da915Sopenharmony_ci * is lower than @count this means that the write has been interrupted in 1195987da915Sopenharmony_ci * flight or that an error was encountered during the write so that the write 1196987da915Sopenharmony_ci * is partial. 0 means nothing was written (also return 0 when @count is 0). 1197987da915Sopenharmony_ci * 1198987da915Sopenharmony_ci * On error and nothing has been written, return -1 with errno set 1199987da915Sopenharmony_ci * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case 1200987da915Sopenharmony_ci * of invalid arguments. 1201987da915Sopenharmony_ci */ 1202987da915Sopenharmony_cis64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, 1203987da915Sopenharmony_ci s64 ofs, const s64 pos, s64 count, void *b) 1204987da915Sopenharmony_ci{ 1205987da915Sopenharmony_ci s64 written, to_write, total = 0; 1206987da915Sopenharmony_ci int err = EIO; 1207987da915Sopenharmony_ci 1208987da915Sopenharmony_ci if (!vol || !rl || pos < 0 || count < 0) { 1209987da915Sopenharmony_ci errno = EINVAL; 1210987da915Sopenharmony_ci ntfs_log_perror("Failed to write runlist [vol: %p rl: %p " 1211987da915Sopenharmony_ci "pos: %lld count: %lld]", vol, rl, 1212987da915Sopenharmony_ci (long long)pos, (long long)count); 1213987da915Sopenharmony_ci goto errno_set; 1214987da915Sopenharmony_ci } 1215987da915Sopenharmony_ci if (!count) 1216987da915Sopenharmony_ci goto out; 1217987da915Sopenharmony_ci /* Seek in @rl to the run containing @pos. */ 1218987da915Sopenharmony_ci while (rl->length && (ofs + (rl->length << 1219987da915Sopenharmony_ci vol->cluster_size_bits) <= pos)) { 1220987da915Sopenharmony_ci ofs += (rl->length << vol->cluster_size_bits); 1221987da915Sopenharmony_ci rl++; 1222987da915Sopenharmony_ci } 1223987da915Sopenharmony_ci /* Offset in the run at which to begin writing. */ 1224987da915Sopenharmony_ci ofs = pos - ofs; 1225987da915Sopenharmony_ci for (total = 0LL; count; rl++, ofs = 0) { 1226987da915Sopenharmony_ci if (!rl->length) 1227987da915Sopenharmony_ci goto rl_err_out; 1228987da915Sopenharmony_ci if (rl->lcn < (LCN)0) { 1229987da915Sopenharmony_ci 1230987da915Sopenharmony_ci if (rl->lcn != (LCN)LCN_HOLE) 1231987da915Sopenharmony_ci goto rl_err_out; 1232987da915Sopenharmony_ci 1233987da915Sopenharmony_ci to_write = min(count, (rl->length << 1234987da915Sopenharmony_ci vol->cluster_size_bits) - ofs); 1235987da915Sopenharmony_ci 1236987da915Sopenharmony_ci total += to_write; 1237987da915Sopenharmony_ci count -= to_write; 1238987da915Sopenharmony_ci b = (u8*)b + to_write; 1239987da915Sopenharmony_ci continue; 1240987da915Sopenharmony_ci } 1241987da915Sopenharmony_ci /* It is a real lcn, write it to the volume. */ 1242987da915Sopenharmony_ci to_write = min(count, (rl->length << vol->cluster_size_bits) - 1243987da915Sopenharmony_ci ofs); 1244987da915Sopenharmony_ciretry: 1245987da915Sopenharmony_ci if (!NVolReadOnly(vol)) 1246987da915Sopenharmony_ci written = ntfs_pwrite(vol->dev, (rl->lcn << 1247987da915Sopenharmony_ci vol->cluster_size_bits) + ofs, 1248987da915Sopenharmony_ci to_write, b); 1249987da915Sopenharmony_ci else 1250987da915Sopenharmony_ci written = to_write; 1251987da915Sopenharmony_ci /* If everything ok, update progress counters and continue. */ 1252987da915Sopenharmony_ci if (written > 0) { 1253987da915Sopenharmony_ci total += written; 1254987da915Sopenharmony_ci count -= written; 1255987da915Sopenharmony_ci b = (u8*)b + written; 1256987da915Sopenharmony_ci continue; 1257987da915Sopenharmony_ci } 1258987da915Sopenharmony_ci /* If the syscall was interrupted, try again. */ 1259987da915Sopenharmony_ci if (written == (s64)-1 && errno == EINTR) 1260987da915Sopenharmony_ci goto retry; 1261987da915Sopenharmony_ci if (written == (s64)-1) 1262987da915Sopenharmony_ci err = errno; 1263987da915Sopenharmony_ci goto rl_err_out; 1264987da915Sopenharmony_ci } 1265987da915Sopenharmony_ciout: 1266987da915Sopenharmony_ci return total; 1267987da915Sopenharmony_cirl_err_out: 1268987da915Sopenharmony_ci if (total) 1269987da915Sopenharmony_ci goto out; 1270987da915Sopenharmony_ci errno = err; 1271987da915Sopenharmony_cierrno_set: 1272987da915Sopenharmony_ci total = -1; 1273987da915Sopenharmony_ci goto out; 1274987da915Sopenharmony_ci} 1275987da915Sopenharmony_ci 1276987da915Sopenharmony_ci/** 1277987da915Sopenharmony_ci * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number 1278987da915Sopenharmony_ci * @n: number for which to get the number of bytes for 1279987da915Sopenharmony_ci * 1280987da915Sopenharmony_ci * Return the number of bytes required to store @n unambiguously as 1281987da915Sopenharmony_ci * a signed number. 1282987da915Sopenharmony_ci * 1283987da915Sopenharmony_ci * This is used in the context of the mapping pairs array to determine how 1284987da915Sopenharmony_ci * many bytes will be needed in the array to store a given logical cluster 1285987da915Sopenharmony_ci * number (lcn) or a specific run length. 1286987da915Sopenharmony_ci * 1287987da915Sopenharmony_ci * Return the number of bytes written. This function cannot fail. 1288987da915Sopenharmony_ci */ 1289987da915Sopenharmony_ciint ntfs_get_nr_significant_bytes(const s64 n) 1290987da915Sopenharmony_ci{ 1291987da915Sopenharmony_ci u64 l; 1292987da915Sopenharmony_ci int i; 1293987da915Sopenharmony_ci 1294987da915Sopenharmony_ci l = (n < 0 ? ~n : n); 1295987da915Sopenharmony_ci i = 1; 1296987da915Sopenharmony_ci if (l >= 128) { 1297987da915Sopenharmony_ci l >>= 7; 1298987da915Sopenharmony_ci do { 1299987da915Sopenharmony_ci i++; 1300987da915Sopenharmony_ci l >>= 8; 1301987da915Sopenharmony_ci } while (l); 1302987da915Sopenharmony_ci } 1303987da915Sopenharmony_ci return i; 1304987da915Sopenharmony_ci} 1305987da915Sopenharmony_ci 1306987da915Sopenharmony_ci/** 1307987da915Sopenharmony_ci * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array 1308987da915Sopenharmony_ci * @vol: ntfs volume (needed for the ntfs version) 1309987da915Sopenharmony_ci * @rl: runlist for which to determine the size of the mapping pairs 1310987da915Sopenharmony_ci * @start_vcn: vcn at which to start the mapping pairs array 1311987da915Sopenharmony_ci * 1312987da915Sopenharmony_ci * Walk the runlist @rl and calculate the size in bytes of the mapping pairs 1313987da915Sopenharmony_ci * array corresponding to the runlist @rl, starting at vcn @start_vcn. This 1314987da915Sopenharmony_ci * for example allows us to allocate a buffer of the right size when building 1315987da915Sopenharmony_ci * the mapping pairs array. 1316987da915Sopenharmony_ci * 1317987da915Sopenharmony_ci * If @rl is NULL, just return 1 (for the single terminator byte). 1318987da915Sopenharmony_ci * 1319987da915Sopenharmony_ci * Return the calculated size in bytes on success. On error, return -1 with 1320987da915Sopenharmony_ci * errno set to the error code. The following error codes are defined: 1321987da915Sopenharmony_ci * EINVAL - Run list contains unmapped elements. Make sure to only pass 1322987da915Sopenharmony_ci * fully mapped runlists to this function. 1323987da915Sopenharmony_ci * - @start_vcn is invalid. 1324987da915Sopenharmony_ci * EIO - The runlist is corrupt. 1325987da915Sopenharmony_ci */ 1326987da915Sopenharmony_ciint ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, 1327987da915Sopenharmony_ci const runlist_element *rl, const VCN start_vcn, int max_size) 1328987da915Sopenharmony_ci{ 1329987da915Sopenharmony_ci LCN prev_lcn; 1330987da915Sopenharmony_ci int rls; 1331987da915Sopenharmony_ci 1332987da915Sopenharmony_ci if (start_vcn < 0) { 1333987da915Sopenharmony_ci ntfs_log_trace("start_vcn %lld (should be >= 0)\n", 1334987da915Sopenharmony_ci (long long) start_vcn); 1335987da915Sopenharmony_ci errno = EINVAL; 1336987da915Sopenharmony_ci goto errno_set; 1337987da915Sopenharmony_ci } 1338987da915Sopenharmony_ci if (!rl) { 1339987da915Sopenharmony_ci if (start_vcn) { 1340987da915Sopenharmony_ci ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", 1341987da915Sopenharmony_ci (long long) start_vcn); 1342987da915Sopenharmony_ci errno = EINVAL; 1343987da915Sopenharmony_ci goto errno_set; 1344987da915Sopenharmony_ci } 1345987da915Sopenharmony_ci rls = 1; 1346987da915Sopenharmony_ci goto out; 1347987da915Sopenharmony_ci } 1348987da915Sopenharmony_ci /* Skip to runlist element containing @start_vcn. */ 1349987da915Sopenharmony_ci while (rl->length && start_vcn >= rl[1].vcn) 1350987da915Sopenharmony_ci rl++; 1351987da915Sopenharmony_ci if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) { 1352987da915Sopenharmony_ci errno = EINVAL; 1353987da915Sopenharmony_ci goto errno_set; 1354987da915Sopenharmony_ci } 1355987da915Sopenharmony_ci prev_lcn = 0; 1356987da915Sopenharmony_ci /* Always need the terminating zero byte. */ 1357987da915Sopenharmony_ci rls = 1; 1358987da915Sopenharmony_ci /* Do the first partial run if present. */ 1359987da915Sopenharmony_ci if (start_vcn > rl->vcn) { 1360987da915Sopenharmony_ci s64 delta; 1361987da915Sopenharmony_ci 1362987da915Sopenharmony_ci /* We know rl->length != 0 already. */ 1363987da915Sopenharmony_ci if (rl->length < 0 || rl->lcn < LCN_HOLE) 1364987da915Sopenharmony_ci goto err_out; 1365987da915Sopenharmony_ci delta = start_vcn - rl->vcn; 1366987da915Sopenharmony_ci /* Header byte + length. */ 1367987da915Sopenharmony_ci rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta); 1368987da915Sopenharmony_ci /* 1369987da915Sopenharmony_ci * If the logical cluster number (lcn) denotes a hole and we 1370987da915Sopenharmony_ci * are on NTFS 3.0+, we don't store it at all, i.e. we need 1371987da915Sopenharmony_ci * zero space. On earlier NTFS versions we just store the lcn. 1372987da915Sopenharmony_ci * Note: this assumes that on NTFS 1.2-, holes are stored with 1373987da915Sopenharmony_ci * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). 1374987da915Sopenharmony_ci */ 1375987da915Sopenharmony_ci if (rl->lcn >= 0 || vol->major_ver < 3) { 1376987da915Sopenharmony_ci prev_lcn = rl->lcn; 1377987da915Sopenharmony_ci if (rl->lcn >= 0) 1378987da915Sopenharmony_ci prev_lcn += delta; 1379987da915Sopenharmony_ci /* Change in lcn. */ 1380987da915Sopenharmony_ci rls += ntfs_get_nr_significant_bytes(prev_lcn); 1381987da915Sopenharmony_ci } 1382987da915Sopenharmony_ci /* Go to next runlist element. */ 1383987da915Sopenharmony_ci rl++; 1384987da915Sopenharmony_ci } 1385987da915Sopenharmony_ci /* Do the full runs. */ 1386987da915Sopenharmony_ci for (; rl->length && (rls <= max_size); rl++) { 1387987da915Sopenharmony_ci if (rl->length < 0 || rl->lcn < LCN_HOLE) 1388987da915Sopenharmony_ci goto err_out; 1389987da915Sopenharmony_ci /* Header byte + length. */ 1390987da915Sopenharmony_ci rls += 1 + ntfs_get_nr_significant_bytes(rl->length); 1391987da915Sopenharmony_ci /* 1392987da915Sopenharmony_ci * If the logical cluster number (lcn) denotes a hole and we 1393987da915Sopenharmony_ci * are on NTFS 3.0+, we don't store it at all, i.e. we need 1394987da915Sopenharmony_ci * zero space. On earlier NTFS versions we just store the lcn. 1395987da915Sopenharmony_ci * Note: this assumes that on NTFS 1.2-, holes are stored with 1396987da915Sopenharmony_ci * an lcn of -1 and not a delta_lcn of -1 (unless both are -1). 1397987da915Sopenharmony_ci */ 1398987da915Sopenharmony_ci if (rl->lcn >= 0 || vol->major_ver < 3) { 1399987da915Sopenharmony_ci /* Change in lcn. */ 1400987da915Sopenharmony_ci rls += ntfs_get_nr_significant_bytes(rl->lcn - 1401987da915Sopenharmony_ci prev_lcn); 1402987da915Sopenharmony_ci prev_lcn = rl->lcn; 1403987da915Sopenharmony_ci } 1404987da915Sopenharmony_ci } 1405987da915Sopenharmony_ciout: 1406987da915Sopenharmony_ci return rls; 1407987da915Sopenharmony_cierr_out: 1408987da915Sopenharmony_ci if (rl->lcn == LCN_RL_NOT_MAPPED) 1409987da915Sopenharmony_ci errno = EINVAL; 1410987da915Sopenharmony_ci else 1411987da915Sopenharmony_ci errno = EIO; 1412987da915Sopenharmony_cierrno_set: 1413987da915Sopenharmony_ci rls = -1; 1414987da915Sopenharmony_ci goto out; 1415987da915Sopenharmony_ci} 1416987da915Sopenharmony_ci 1417987da915Sopenharmony_ci/** 1418987da915Sopenharmony_ci * ntfs_write_significant_bytes - write the significant bytes of a number 1419987da915Sopenharmony_ci * @dst: destination buffer to write to 1420987da915Sopenharmony_ci * @dst_max: pointer to last byte of destination buffer for bounds checking 1421987da915Sopenharmony_ci * @n: number whose significant bytes to write 1422987da915Sopenharmony_ci * 1423987da915Sopenharmony_ci * Store in @dst, the minimum bytes of the number @n which are required to 1424987da915Sopenharmony_ci * identify @n unambiguously as a signed number, taking care not to exceed 1425987da915Sopenharmony_ci * @dest_max, the maximum position within @dst to which we are allowed to 1426987da915Sopenharmony_ci * write. 1427987da915Sopenharmony_ci * 1428987da915Sopenharmony_ci * This is used when building the mapping pairs array of a runlist to compress 1429987da915Sopenharmony_ci * a given logical cluster number (lcn) or a specific run length to the minimum 1430987da915Sopenharmony_ci * size possible. 1431987da915Sopenharmony_ci * 1432987da915Sopenharmony_ci * Return the number of bytes written on success. On error, i.e. the 1433987da915Sopenharmony_ci * destination buffer @dst is too small, return -1 with errno set ENOSPC. 1434987da915Sopenharmony_ci */ 1435987da915Sopenharmony_ciint ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n) 1436987da915Sopenharmony_ci{ 1437987da915Sopenharmony_ci s64 l = n; 1438987da915Sopenharmony_ci int i; 1439987da915Sopenharmony_ci 1440987da915Sopenharmony_ci i = 0; 1441987da915Sopenharmony_ci if (dst > dst_max) 1442987da915Sopenharmony_ci goto err_out; 1443987da915Sopenharmony_ci *dst++ = l; 1444987da915Sopenharmony_ci i++; 1445987da915Sopenharmony_ci while ((l > 0x7f) || (l < -0x80)) { 1446987da915Sopenharmony_ci if (dst > dst_max) 1447987da915Sopenharmony_ci goto err_out; 1448987da915Sopenharmony_ci l >>= 8; 1449987da915Sopenharmony_ci *dst++ = l; 1450987da915Sopenharmony_ci i++; 1451987da915Sopenharmony_ci } 1452987da915Sopenharmony_ci return i; 1453987da915Sopenharmony_cierr_out: 1454987da915Sopenharmony_ci errno = ENOSPC; 1455987da915Sopenharmony_ci return -1; 1456987da915Sopenharmony_ci} 1457987da915Sopenharmony_ci 1458987da915Sopenharmony_ci/** 1459987da915Sopenharmony_ci * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist 1460987da915Sopenharmony_ci * @vol: ntfs volume (needed for the ntfs version) 1461987da915Sopenharmony_ci * @dst: destination buffer to which to write the mapping pairs array 1462987da915Sopenharmony_ci * @dst_len: size of destination buffer @dst in bytes 1463987da915Sopenharmony_ci * @rl: runlist for which to build the mapping pairs array 1464987da915Sopenharmony_ci * @start_vcn: vcn at which to start the mapping pairs array 1465987da915Sopenharmony_ci * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error 1466987da915Sopenharmony_ci * 1467987da915Sopenharmony_ci * Create the mapping pairs array from the runlist @rl, starting at vcn 1468987da915Sopenharmony_ci * @start_vcn and save the array in @dst. @dst_len is the size of @dst in 1469987da915Sopenharmony_ci * bytes and it should be at least equal to the value obtained by calling 1470987da915Sopenharmony_ci * ntfs_get_size_for_mapping_pairs(). 1471987da915Sopenharmony_ci * 1472987da915Sopenharmony_ci * If @rl is NULL, just write a single terminator byte to @dst. 1473987da915Sopenharmony_ci * 1474987da915Sopenharmony_ci * On success or ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to 1475987da915Sopenharmony_ci * the first vcn outside the destination buffer. Note that on error @dst has 1476987da915Sopenharmony_ci * been filled with all the mapping pairs that will fit, thus it can be treated 1477987da915Sopenharmony_ci * as partial success, in that a new attribute extent needs to be created or the 1478987da915Sopenharmony_ci * next extent has to be used and the mapping pairs build has to be continued 1479987da915Sopenharmony_ci * with @start_vcn set to *@stop_vcn. 1480987da915Sopenharmony_ci * 1481987da915Sopenharmony_ci * Return 0 on success. On error, return -1 with errno set to the error code. 1482987da915Sopenharmony_ci * The following error codes are defined: 1483987da915Sopenharmony_ci * EINVAL - Run list contains unmapped elements. Make sure to only pass 1484987da915Sopenharmony_ci * fully mapped runlists to this function. 1485987da915Sopenharmony_ci * - @start_vcn is invalid. 1486987da915Sopenharmony_ci * EIO - The runlist is corrupt. 1487987da915Sopenharmony_ci * ENOSPC - The destination buffer is too small. 1488987da915Sopenharmony_ci */ 1489987da915Sopenharmony_ciint ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst, 1490987da915Sopenharmony_ci const int dst_len, const runlist_element *rl, 1491987da915Sopenharmony_ci const VCN start_vcn, runlist_element const **stop_rl) 1492987da915Sopenharmony_ci{ 1493987da915Sopenharmony_ci LCN prev_lcn; 1494987da915Sopenharmony_ci u8 *dst_max, *dst_next; 1495987da915Sopenharmony_ci s8 len_len, lcn_len; 1496987da915Sopenharmony_ci int ret = 0; 1497987da915Sopenharmony_ci 1498987da915Sopenharmony_ci if (start_vcn < 0) 1499987da915Sopenharmony_ci goto val_err; 1500987da915Sopenharmony_ci if (!rl) { 1501987da915Sopenharmony_ci if (start_vcn) 1502987da915Sopenharmony_ci goto val_err; 1503987da915Sopenharmony_ci if (stop_rl) 1504987da915Sopenharmony_ci *stop_rl = rl; 1505987da915Sopenharmony_ci if (dst_len < 1) 1506987da915Sopenharmony_ci goto nospc_err; 1507987da915Sopenharmony_ci goto ok; 1508987da915Sopenharmony_ci } 1509987da915Sopenharmony_ci /* Skip to runlist element containing @start_vcn. */ 1510987da915Sopenharmony_ci while (rl->length && start_vcn >= rl[1].vcn) 1511987da915Sopenharmony_ci rl++; 1512987da915Sopenharmony_ci if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) 1513987da915Sopenharmony_ci goto val_err; 1514987da915Sopenharmony_ci /* 1515987da915Sopenharmony_ci * @dst_max is used for bounds checking in 1516987da915Sopenharmony_ci * ntfs_write_significant_bytes(). 1517987da915Sopenharmony_ci */ 1518987da915Sopenharmony_ci dst_max = dst + dst_len - 1; 1519987da915Sopenharmony_ci prev_lcn = 0; 1520987da915Sopenharmony_ci /* Do the first partial run if present. */ 1521987da915Sopenharmony_ci if (start_vcn > rl->vcn) { 1522987da915Sopenharmony_ci s64 delta; 1523987da915Sopenharmony_ci 1524987da915Sopenharmony_ci /* We know rl->length != 0 already. */ 1525987da915Sopenharmony_ci if (rl->length < 0 || rl->lcn < LCN_HOLE) 1526987da915Sopenharmony_ci goto err_out; 1527987da915Sopenharmony_ci delta = start_vcn - rl->vcn; 1528987da915Sopenharmony_ci /* Write length. */ 1529987da915Sopenharmony_ci len_len = ntfs_write_significant_bytes(dst + 1, dst_max, 1530987da915Sopenharmony_ci rl->length - delta); 1531987da915Sopenharmony_ci if (len_len < 0) 1532987da915Sopenharmony_ci goto size_err; 1533987da915Sopenharmony_ci /* 1534987da915Sopenharmony_ci * If the logical cluster number (lcn) denotes a hole and we 1535987da915Sopenharmony_ci * are on NTFS 3.0+, we don't store it at all, i.e. we need 1536987da915Sopenharmony_ci * zero space. On earlier NTFS versions we just write the lcn 1537987da915Sopenharmony_ci * change. FIXME: Do we need to write the lcn change or just 1538987da915Sopenharmony_ci * the lcn in that case? Not sure as I have never seen this 1539987da915Sopenharmony_ci * case on NT4. - We assume that we just need to write the lcn 1540987da915Sopenharmony_ci * change until someone tells us otherwise... (AIA) 1541987da915Sopenharmony_ci */ 1542987da915Sopenharmony_ci if (rl->lcn >= 0 || vol->major_ver < 3) { 1543987da915Sopenharmony_ci prev_lcn = rl->lcn; 1544987da915Sopenharmony_ci if (rl->lcn >= 0) 1545987da915Sopenharmony_ci prev_lcn += delta; 1546987da915Sopenharmony_ci /* Write change in lcn. */ 1547987da915Sopenharmony_ci lcn_len = ntfs_write_significant_bytes(dst + 1 + 1548987da915Sopenharmony_ci len_len, dst_max, prev_lcn); 1549987da915Sopenharmony_ci if (lcn_len < 0) 1550987da915Sopenharmony_ci goto size_err; 1551987da915Sopenharmony_ci } else 1552987da915Sopenharmony_ci lcn_len = 0; 1553987da915Sopenharmony_ci dst_next = dst + len_len + lcn_len + 1; 1554987da915Sopenharmony_ci if (dst_next > dst_max) 1555987da915Sopenharmony_ci goto size_err; 1556987da915Sopenharmony_ci /* Update header byte. */ 1557987da915Sopenharmony_ci *dst = lcn_len << 4 | len_len; 1558987da915Sopenharmony_ci /* Position at next mapping pairs array element. */ 1559987da915Sopenharmony_ci dst = dst_next; 1560987da915Sopenharmony_ci /* Go to next runlist element. */ 1561987da915Sopenharmony_ci rl++; 1562987da915Sopenharmony_ci } 1563987da915Sopenharmony_ci /* Do the full runs. */ 1564987da915Sopenharmony_ci for (; rl->length; rl++) { 1565987da915Sopenharmony_ci if (rl->length < 0 || rl->lcn < LCN_HOLE) 1566987da915Sopenharmony_ci goto err_out; 1567987da915Sopenharmony_ci /* Write length. */ 1568987da915Sopenharmony_ci len_len = ntfs_write_significant_bytes(dst + 1, dst_max, 1569987da915Sopenharmony_ci rl->length); 1570987da915Sopenharmony_ci if (len_len < 0) 1571987da915Sopenharmony_ci goto size_err; 1572987da915Sopenharmony_ci /* 1573987da915Sopenharmony_ci * If the logical cluster number (lcn) denotes a hole and we 1574987da915Sopenharmony_ci * are on NTFS 3.0+, we don't store it at all, i.e. we need 1575987da915Sopenharmony_ci * zero space. On earlier NTFS versions we just write the lcn 1576987da915Sopenharmony_ci * change. FIXME: Do we need to write the lcn change or just 1577987da915Sopenharmony_ci * the lcn in that case? Not sure as I have never seen this 1578987da915Sopenharmony_ci * case on NT4. - We assume that we just need to write the lcn 1579987da915Sopenharmony_ci * change until someone tells us otherwise... (AIA) 1580987da915Sopenharmony_ci */ 1581987da915Sopenharmony_ci if (rl->lcn >= 0 || vol->major_ver < 3) { 1582987da915Sopenharmony_ci /* Write change in lcn. */ 1583987da915Sopenharmony_ci lcn_len = ntfs_write_significant_bytes(dst + 1 + 1584987da915Sopenharmony_ci len_len, dst_max, rl->lcn - prev_lcn); 1585987da915Sopenharmony_ci if (lcn_len < 0) 1586987da915Sopenharmony_ci goto size_err; 1587987da915Sopenharmony_ci prev_lcn = rl->lcn; 1588987da915Sopenharmony_ci } else 1589987da915Sopenharmony_ci lcn_len = 0; 1590987da915Sopenharmony_ci dst_next = dst + len_len + lcn_len + 1; 1591987da915Sopenharmony_ci if (dst_next > dst_max) 1592987da915Sopenharmony_ci goto size_err; 1593987da915Sopenharmony_ci /* Update header byte. */ 1594987da915Sopenharmony_ci *dst = lcn_len << 4 | len_len; 1595987da915Sopenharmony_ci /* Position at next mapping pairs array element. */ 1596987da915Sopenharmony_ci dst += 1 + len_len + lcn_len; 1597987da915Sopenharmony_ci } 1598987da915Sopenharmony_ci /* Set stop vcn. */ 1599987da915Sopenharmony_ci if (stop_rl) 1600987da915Sopenharmony_ci *stop_rl = rl; 1601987da915Sopenharmony_ciok: 1602987da915Sopenharmony_ci /* Add terminator byte. */ 1603987da915Sopenharmony_ci *dst = 0; 1604987da915Sopenharmony_ciout: 1605987da915Sopenharmony_ci return ret; 1606987da915Sopenharmony_cisize_err: 1607987da915Sopenharmony_ci /* Set stop vcn. */ 1608987da915Sopenharmony_ci if (stop_rl) 1609987da915Sopenharmony_ci *stop_rl = rl; 1610987da915Sopenharmony_ci /* Add terminator byte. */ 1611987da915Sopenharmony_ci *dst = 0; 1612987da915Sopenharmony_cinospc_err: 1613987da915Sopenharmony_ci errno = ENOSPC; 1614987da915Sopenharmony_ci goto errno_set; 1615987da915Sopenharmony_cival_err: 1616987da915Sopenharmony_ci errno = EINVAL; 1617987da915Sopenharmony_ci goto errno_set; 1618987da915Sopenharmony_cierr_out: 1619987da915Sopenharmony_ci if (rl->lcn == LCN_RL_NOT_MAPPED) 1620987da915Sopenharmony_ci errno = EINVAL; 1621987da915Sopenharmony_ci else 1622987da915Sopenharmony_ci errno = EIO; 1623987da915Sopenharmony_cierrno_set: 1624987da915Sopenharmony_ci ret = -1; 1625987da915Sopenharmony_ci goto out; 1626987da915Sopenharmony_ci} 1627987da915Sopenharmony_ci 1628987da915Sopenharmony_ci/** 1629987da915Sopenharmony_ci * ntfs_rl_truncate - truncate a runlist starting at a specified vcn 1630987da915Sopenharmony_ci * @arl: address of runlist to truncate 1631987da915Sopenharmony_ci * @start_vcn: first vcn which should be cut off 1632987da915Sopenharmony_ci * 1633987da915Sopenharmony_ci * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory 1634987da915Sopenharmony_ci * buffer holding the runlist. 1635987da915Sopenharmony_ci * 1636987da915Sopenharmony_ci * Return 0 on success and -1 on error with errno set to the error code. 1637987da915Sopenharmony_ci * 1638987da915Sopenharmony_ci * NOTE: @arl is the address of the runlist. We need the address so we can 1639987da915Sopenharmony_ci * modify the pointer to the runlist with the new, reallocated memory buffer. 1640987da915Sopenharmony_ci */ 1641987da915Sopenharmony_ciint ntfs_rl_truncate(runlist **arl, const VCN start_vcn) 1642987da915Sopenharmony_ci{ 1643987da915Sopenharmony_ci runlist *rl; 1644987da915Sopenharmony_ci /* BOOL is_end = FALSE; */ 1645987da915Sopenharmony_ci 1646987da915Sopenharmony_ci if (!arl || !*arl) { 1647987da915Sopenharmony_ci errno = EINVAL; 1648987da915Sopenharmony_ci if (!arl) 1649987da915Sopenharmony_ci ntfs_log_perror("rl_truncate error: arl: %p", arl); 1650987da915Sopenharmony_ci else 1651987da915Sopenharmony_ci ntfs_log_perror("rl_truncate error:" 1652987da915Sopenharmony_ci " arl: %p *arl: %p", arl, *arl); 1653987da915Sopenharmony_ci return -1; 1654987da915Sopenharmony_ci } 1655987da915Sopenharmony_ci 1656987da915Sopenharmony_ci rl = *arl; 1657987da915Sopenharmony_ci 1658987da915Sopenharmony_ci if (start_vcn < rl->vcn) { 1659987da915Sopenharmony_ci errno = EINVAL; 1660987da915Sopenharmony_ci ntfs_log_perror("Start_vcn lies outside front of runlist"); 1661987da915Sopenharmony_ci return -1; 1662987da915Sopenharmony_ci } 1663987da915Sopenharmony_ci 1664987da915Sopenharmony_ci /* Find the starting vcn in the run list. */ 1665987da915Sopenharmony_ci while (rl->length) { 1666987da915Sopenharmony_ci if (start_vcn < rl[1].vcn) 1667987da915Sopenharmony_ci break; 1668987da915Sopenharmony_ci rl++; 1669987da915Sopenharmony_ci } 1670987da915Sopenharmony_ci 1671987da915Sopenharmony_ci if (!rl->length) { 1672987da915Sopenharmony_ci errno = EIO; 1673987da915Sopenharmony_ci ntfs_log_trace("Truncating already truncated runlist?\n"); 1674987da915Sopenharmony_ci return -1; 1675987da915Sopenharmony_ci } 1676987da915Sopenharmony_ci 1677987da915Sopenharmony_ci /* Truncate the run. */ 1678987da915Sopenharmony_ci rl->length = start_vcn - rl->vcn; 1679987da915Sopenharmony_ci 1680987da915Sopenharmony_ci /* 1681987da915Sopenharmony_ci * If a run was partially truncated, make the following runlist 1682987da915Sopenharmony_ci * element a terminator instead of the truncated runlist 1683987da915Sopenharmony_ci * element itself. 1684987da915Sopenharmony_ci */ 1685987da915Sopenharmony_ci if (rl->length) { 1686987da915Sopenharmony_ci ++rl; 1687987da915Sopenharmony_ci/* 1688987da915Sopenharmony_ci if (!rl->length) 1689987da915Sopenharmony_ci is_end = TRUE; 1690987da915Sopenharmony_ci*/ 1691987da915Sopenharmony_ci rl->vcn = start_vcn; 1692987da915Sopenharmony_ci rl->length = 0; 1693987da915Sopenharmony_ci } 1694987da915Sopenharmony_ci rl->lcn = (LCN)LCN_ENOENT; 1695987da915Sopenharmony_ci /** 1696987da915Sopenharmony_ci * Reallocate memory if necessary. 1697987da915Sopenharmony_ci * FIXME: Below code is broken, because runlist allocations must be 1698987da915Sopenharmony_ci * a multiple of 4096. The code caused crashes and corruptions. 1699987da915Sopenharmony_ci */ 1700987da915Sopenharmony_ci/* 1701987da915Sopenharmony_ci if (!is_end) { 1702987da915Sopenharmony_ci size_t new_size = (rl - *arl + 1) * sizeof(runlist_element); 1703987da915Sopenharmony_ci rl = realloc(*arl, new_size); 1704987da915Sopenharmony_ci if (rl) 1705987da915Sopenharmony_ci *arl = rl; 1706987da915Sopenharmony_ci } 1707987da915Sopenharmony_ci*/ 1708987da915Sopenharmony_ci return 0; 1709987da915Sopenharmony_ci} 1710987da915Sopenharmony_ci 1711987da915Sopenharmony_ci/** 1712987da915Sopenharmony_ci * ntfs_rl_sparse - check whether runlist have sparse regions or not. 1713987da915Sopenharmony_ci * @rl: runlist to check 1714987da915Sopenharmony_ci * 1715987da915Sopenharmony_ci * Return 1 if have, 0 if not, -1 on error with errno set to the error code. 1716987da915Sopenharmony_ci */ 1717987da915Sopenharmony_ciint ntfs_rl_sparse(runlist *rl) 1718987da915Sopenharmony_ci{ 1719987da915Sopenharmony_ci runlist *rlc; 1720987da915Sopenharmony_ci 1721987da915Sopenharmony_ci if (!rl) { 1722987da915Sopenharmony_ci errno = EINVAL; 1723987da915Sopenharmony_ci ntfs_log_perror("%s: ", __FUNCTION__); 1724987da915Sopenharmony_ci return -1; 1725987da915Sopenharmony_ci } 1726987da915Sopenharmony_ci 1727987da915Sopenharmony_ci for (rlc = rl; rlc->length; rlc++) 1728987da915Sopenharmony_ci if (rlc->lcn < 0) { 1729987da915Sopenharmony_ci if (rlc->lcn != LCN_HOLE) { 1730987da915Sopenharmony_ci errno = EINVAL; 1731987da915Sopenharmony_ci ntfs_log_perror("%s: bad runlist", __FUNCTION__); 1732987da915Sopenharmony_ci return -1; 1733987da915Sopenharmony_ci } 1734987da915Sopenharmony_ci return 1; 1735987da915Sopenharmony_ci } 1736987da915Sopenharmony_ci return 0; 1737987da915Sopenharmony_ci} 1738987da915Sopenharmony_ci 1739987da915Sopenharmony_ci/** 1740987da915Sopenharmony_ci * ntfs_rl_get_compressed_size - calculate length of non sparse regions 1741987da915Sopenharmony_ci * @vol: ntfs volume (need for cluster size) 1742987da915Sopenharmony_ci * @rl: runlist to calculate for 1743987da915Sopenharmony_ci * 1744987da915Sopenharmony_ci * Return compressed size or -1 on error with errno set to the error code. 1745987da915Sopenharmony_ci */ 1746987da915Sopenharmony_cis64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl) 1747987da915Sopenharmony_ci{ 1748987da915Sopenharmony_ci runlist *rlc; 1749987da915Sopenharmony_ci s64 ret = 0; 1750987da915Sopenharmony_ci 1751987da915Sopenharmony_ci if (!rl) { 1752987da915Sopenharmony_ci errno = EINVAL; 1753987da915Sopenharmony_ci ntfs_log_perror("%s: ", __FUNCTION__); 1754987da915Sopenharmony_ci return -1; 1755987da915Sopenharmony_ci } 1756987da915Sopenharmony_ci 1757987da915Sopenharmony_ci for (rlc = rl; rlc->length; rlc++) { 1758987da915Sopenharmony_ci if (rlc->lcn < 0) { 1759987da915Sopenharmony_ci if (rlc->lcn != LCN_HOLE) { 1760987da915Sopenharmony_ci errno = EINVAL; 1761987da915Sopenharmony_ci ntfs_log_perror("%s: bad runlist", __FUNCTION__); 1762987da915Sopenharmony_ci return -1; 1763987da915Sopenharmony_ci } 1764987da915Sopenharmony_ci } else 1765987da915Sopenharmony_ci ret += rlc->length; 1766987da915Sopenharmony_ci } 1767987da915Sopenharmony_ci return ret << vol->cluster_size_bits; 1768987da915Sopenharmony_ci} 1769987da915Sopenharmony_ci 1770987da915Sopenharmony_ci 1771987da915Sopenharmony_ci#ifdef NTFS_TEST 1772987da915Sopenharmony_ci/** 1773987da915Sopenharmony_ci * test_rl_helper 1774987da915Sopenharmony_ci */ 1775987da915Sopenharmony_ci#define MKRL(R,V,L,S) \ 1776987da915Sopenharmony_ci (R)->vcn = V; \ 1777987da915Sopenharmony_ci (R)->lcn = L; \ 1778987da915Sopenharmony_ci (R)->length = S; 1779987da915Sopenharmony_ci/* 1780987da915Sopenharmony_ci} 1781987da915Sopenharmony_ci*/ 1782987da915Sopenharmony_ci/** 1783987da915Sopenharmony_ci * test_rl_dump_runlist - Runlist test: Display the contents of a runlist 1784987da915Sopenharmony_ci * @rl: 1785987da915Sopenharmony_ci * 1786987da915Sopenharmony_ci * Description... 1787987da915Sopenharmony_ci * 1788987da915Sopenharmony_ci * Returns: 1789987da915Sopenharmony_ci */ 1790987da915Sopenharmony_cistatic void test_rl_dump_runlist(const runlist_element *rl) 1791987da915Sopenharmony_ci{ 1792987da915Sopenharmony_ci int abbr = 0; /* abbreviate long lists */ 1793987da915Sopenharmony_ci int len = 0; 1794987da915Sopenharmony_ci int i; 1795987da915Sopenharmony_ci const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" }; 1796987da915Sopenharmony_ci 1797987da915Sopenharmony_ci if (!rl) { 1798987da915Sopenharmony_ci printf(" Run list not present.\n"); 1799987da915Sopenharmony_ci return; 1800987da915Sopenharmony_ci } 1801987da915Sopenharmony_ci 1802987da915Sopenharmony_ci if (abbr) 1803987da915Sopenharmony_ci for (len = 0; rl[len].length; len++) ; 1804987da915Sopenharmony_ci 1805987da915Sopenharmony_ci printf(" VCN LCN len\n"); 1806987da915Sopenharmony_ci for (i = 0; ; i++, rl++) { 1807987da915Sopenharmony_ci LCN lcn = rl->lcn; 1808987da915Sopenharmony_ci 1809987da915Sopenharmony_ci if ((abbr) && (len > 20)) { 1810987da915Sopenharmony_ci if (i == 4) 1811987da915Sopenharmony_ci printf(" ...\n"); 1812987da915Sopenharmony_ci if ((i > 3) && (i < (len - 3))) 1813987da915Sopenharmony_ci continue; 1814987da915Sopenharmony_ci } 1815987da915Sopenharmony_ci 1816987da915Sopenharmony_ci if (lcn < (LCN)0) { 1817987da915Sopenharmony_ci int ind = -lcn - 1; 1818987da915Sopenharmony_ci 1819987da915Sopenharmony_ci if (ind > -LCN_ENOENT - 1) 1820987da915Sopenharmony_ci ind = 3; 1821987da915Sopenharmony_ci printf("%8lld %8s %8lld\n", 1822987da915Sopenharmony_ci rl->vcn, lcn_str[ind], rl->length); 1823987da915Sopenharmony_ci } else 1824987da915Sopenharmony_ci printf("%8lld %8lld %8lld\n", 1825987da915Sopenharmony_ci rl->vcn, rl->lcn, rl->length); 1826987da915Sopenharmony_ci if (!rl->length) 1827987da915Sopenharmony_ci break; 1828987da915Sopenharmony_ci } 1829987da915Sopenharmony_ci if ((abbr) && (len > 20)) 1830987da915Sopenharmony_ci printf(" (%d entries)\n", len+1); 1831987da915Sopenharmony_ci printf("\n"); 1832987da915Sopenharmony_ci} 1833987da915Sopenharmony_ci 1834987da915Sopenharmony_ci/** 1835987da915Sopenharmony_ci * test_rl_runlists_merge - Runlist test: Merge two runlists 1836987da915Sopenharmony_ci * @drl: 1837987da915Sopenharmony_ci * @srl: 1838987da915Sopenharmony_ci * 1839987da915Sopenharmony_ci * Description... 1840987da915Sopenharmony_ci * 1841987da915Sopenharmony_ci * Returns: 1842987da915Sopenharmony_ci */ 1843987da915Sopenharmony_cistatic runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl) 1844987da915Sopenharmony_ci{ 1845987da915Sopenharmony_ci runlist_element *res = NULL; 1846987da915Sopenharmony_ci 1847987da915Sopenharmony_ci printf("dst:\n"); 1848987da915Sopenharmony_ci test_rl_dump_runlist(drl); 1849987da915Sopenharmony_ci printf("src:\n"); 1850987da915Sopenharmony_ci test_rl_dump_runlist(srl); 1851987da915Sopenharmony_ci 1852987da915Sopenharmony_ci res = ntfs_runlists_merge(drl, srl); 1853987da915Sopenharmony_ci 1854987da915Sopenharmony_ci printf("res:\n"); 1855987da915Sopenharmony_ci test_rl_dump_runlist(res); 1856987da915Sopenharmony_ci 1857987da915Sopenharmony_ci return res; 1858987da915Sopenharmony_ci} 1859987da915Sopenharmony_ci 1860987da915Sopenharmony_ci/** 1861987da915Sopenharmony_ci * test_rl_read_buffer - Runlist test: Read a file containing a runlist 1862987da915Sopenharmony_ci * @file: 1863987da915Sopenharmony_ci * @buf: 1864987da915Sopenharmony_ci * @bufsize: 1865987da915Sopenharmony_ci * 1866987da915Sopenharmony_ci * Description... 1867987da915Sopenharmony_ci * 1868987da915Sopenharmony_ci * Returns: 1869987da915Sopenharmony_ci */ 1870987da915Sopenharmony_cistatic int test_rl_read_buffer(const char *file, u8 *buf, int bufsize) 1871987da915Sopenharmony_ci{ 1872987da915Sopenharmony_ci FILE *fptr; 1873987da915Sopenharmony_ci 1874987da915Sopenharmony_ci fptr = fopen(file, "r"); 1875987da915Sopenharmony_ci if (!fptr) { 1876987da915Sopenharmony_ci printf("open %s\n", file); 1877987da915Sopenharmony_ci return 0; 1878987da915Sopenharmony_ci } 1879987da915Sopenharmony_ci 1880987da915Sopenharmony_ci if (fread(buf, bufsize, 1, fptr) == 99) { 1881987da915Sopenharmony_ci printf("read %s\n", file); 1882987da915Sopenharmony_ci return 0; 1883987da915Sopenharmony_ci } 1884987da915Sopenharmony_ci 1885987da915Sopenharmony_ci fclose(fptr); 1886987da915Sopenharmony_ci return 1; 1887987da915Sopenharmony_ci} 1888987da915Sopenharmony_ci 1889987da915Sopenharmony_ci/** 1890987da915Sopenharmony_ci * test_rl_pure_src - Runlist test: Complicate the simple tests a little 1891987da915Sopenharmony_ci * @contig: 1892987da915Sopenharmony_ci * @multi: 1893987da915Sopenharmony_ci * @vcn: 1894987da915Sopenharmony_ci * @len: 1895987da915Sopenharmony_ci * 1896987da915Sopenharmony_ci * Description... 1897987da915Sopenharmony_ci * 1898987da915Sopenharmony_ci * Returns: 1899987da915Sopenharmony_ci */ 1900987da915Sopenharmony_cistatic runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len) 1901987da915Sopenharmony_ci{ 1902987da915Sopenharmony_ci runlist_element *result; 1903987da915Sopenharmony_ci int fudge; 1904987da915Sopenharmony_ci 1905987da915Sopenharmony_ci if (contig) 1906987da915Sopenharmony_ci fudge = 0; 1907987da915Sopenharmony_ci else 1908987da915Sopenharmony_ci fudge = 999; 1909987da915Sopenharmony_ci 1910987da915Sopenharmony_ci result = ntfs_malloc(4096); 1911987da915Sopenharmony_ci if (!result) 1912987da915Sopenharmony_ci return NULL; 1913987da915Sopenharmony_ci 1914987da915Sopenharmony_ci if (multi) { 1915987da915Sopenharmony_ci MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4) 1916987da915Sopenharmony_ci MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4) 1917987da915Sopenharmony_ci MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4) 1918987da915Sopenharmony_ci MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4) 1919987da915Sopenharmony_ci MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0) 1920987da915Sopenharmony_ci } else { 1921987da915Sopenharmony_ci MKRL(result+0, vcn, fudge + vcn + 1000, len) 1922987da915Sopenharmony_ci MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0) 1923987da915Sopenharmony_ci } 1924987da915Sopenharmony_ci return result; 1925987da915Sopenharmony_ci} 1926987da915Sopenharmony_ci 1927987da915Sopenharmony_ci/** 1928987da915Sopenharmony_ci * test_rl_pure_test - Runlist test: Perform tests using simple runlists 1929987da915Sopenharmony_ci * @test: 1930987da915Sopenharmony_ci * @contig: 1931987da915Sopenharmony_ci * @multi: 1932987da915Sopenharmony_ci * @vcn: 1933987da915Sopenharmony_ci * @len: 1934987da915Sopenharmony_ci * @file: 1935987da915Sopenharmony_ci * @size: 1936987da915Sopenharmony_ci * 1937987da915Sopenharmony_ci * Description... 1938987da915Sopenharmony_ci * 1939987da915Sopenharmony_ci * Returns: 1940987da915Sopenharmony_ci */ 1941987da915Sopenharmony_cistatic void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size) 1942987da915Sopenharmony_ci{ 1943987da915Sopenharmony_ci runlist_element *src; 1944987da915Sopenharmony_ci runlist_element *dst; 1945987da915Sopenharmony_ci runlist_element *res; 1946987da915Sopenharmony_ci 1947987da915Sopenharmony_ci src = test_rl_pure_src(contig, multi, vcn, len); 1948987da915Sopenharmony_ci dst = ntfs_malloc(4096); 1949987da915Sopenharmony_ci if (!src || !dst) { 1950987da915Sopenharmony_ci printf("Test %2d ---------- FAILED! (no free memory?)\n", test); 1951987da915Sopenharmony_ci return; 1952987da915Sopenharmony_ci } 1953987da915Sopenharmony_ci 1954987da915Sopenharmony_ci memcpy(dst, file, size); 1955987da915Sopenharmony_ci 1956987da915Sopenharmony_ci printf("Test %2d ----------\n", test); 1957987da915Sopenharmony_ci res = test_rl_runlists_merge(dst, src); 1958987da915Sopenharmony_ci 1959987da915Sopenharmony_ci free(res); 1960987da915Sopenharmony_ci} 1961987da915Sopenharmony_ci 1962987da915Sopenharmony_ci/** 1963987da915Sopenharmony_ci * test_rl_pure - Runlist test: Create tests using simple runlists 1964987da915Sopenharmony_ci * @contig: 1965987da915Sopenharmony_ci * @multi: 1966987da915Sopenharmony_ci * 1967987da915Sopenharmony_ci * Description... 1968987da915Sopenharmony_ci * 1969987da915Sopenharmony_ci * Returns: 1970987da915Sopenharmony_ci */ 1971987da915Sopenharmony_cistatic void test_rl_pure(char *contig, char *multi) 1972987da915Sopenharmony_ci{ 1973987da915Sopenharmony_ci /* VCN, LCN, len */ 1974987da915Sopenharmony_ci static runlist_element file1[] = { 1975987da915Sopenharmony_ci { 0, -1, 100 }, /* HOLE */ 1976987da915Sopenharmony_ci { 100, 1100, 100 }, /* DATA */ 1977987da915Sopenharmony_ci { 200, -1, 100 }, /* HOLE */ 1978987da915Sopenharmony_ci { 300, 1300, 100 }, /* DATA */ 1979987da915Sopenharmony_ci { 400, -1, 100 }, /* HOLE */ 1980987da915Sopenharmony_ci { 500, -3, 0 } /* NOENT */ 1981987da915Sopenharmony_ci }; 1982987da915Sopenharmony_ci static runlist_element file2[] = { 1983987da915Sopenharmony_ci { 0, 1000, 100 }, /* DATA */ 1984987da915Sopenharmony_ci { 100, -1, 100 }, /* HOLE */ 1985987da915Sopenharmony_ci { 200, -3, 0 } /* NOENT */ 1986987da915Sopenharmony_ci }; 1987987da915Sopenharmony_ci static runlist_element file3[] = { 1988987da915Sopenharmony_ci { 0, 1000, 100 }, /* DATA */ 1989987da915Sopenharmony_ci { 100, -3, 0 } /* NOENT */ 1990987da915Sopenharmony_ci }; 1991987da915Sopenharmony_ci static runlist_element file4[] = { 1992987da915Sopenharmony_ci { 0, -3, 0 } /* NOENT */ 1993987da915Sopenharmony_ci }; 1994987da915Sopenharmony_ci static runlist_element file5[] = { 1995987da915Sopenharmony_ci { 0, -2, 100 }, /* NOTMAP */ 1996987da915Sopenharmony_ci { 100, 1100, 100 }, /* DATA */ 1997987da915Sopenharmony_ci { 200, -2, 100 }, /* NOTMAP */ 1998987da915Sopenharmony_ci { 300, 1300, 100 }, /* DATA */ 1999987da915Sopenharmony_ci { 400, -2, 100 }, /* NOTMAP */ 2000987da915Sopenharmony_ci { 500, -3, 0 } /* NOENT */ 2001987da915Sopenharmony_ci }; 2002987da915Sopenharmony_ci static runlist_element file6[] = { 2003987da915Sopenharmony_ci { 0, 1000, 100 }, /* DATA */ 2004987da915Sopenharmony_ci { 100, -2, 100 }, /* NOTMAP */ 2005987da915Sopenharmony_ci { 200, -3, 0 } /* NOENT */ 2006987da915Sopenharmony_ci }; 2007987da915Sopenharmony_ci BOOL c, m; 2008987da915Sopenharmony_ci 2009987da915Sopenharmony_ci if (strcmp(contig, "contig") == 0) 2010987da915Sopenharmony_ci c = TRUE; 2011987da915Sopenharmony_ci else if (strcmp(contig, "noncontig") == 0) 2012987da915Sopenharmony_ci c = FALSE; 2013987da915Sopenharmony_ci else { 2014987da915Sopenharmony_ci printf("rl pure [contig|noncontig] [single|multi]\n"); 2015987da915Sopenharmony_ci return; 2016987da915Sopenharmony_ci } 2017987da915Sopenharmony_ci if (strcmp(multi, "multi") == 0) 2018987da915Sopenharmony_ci m = TRUE; 2019987da915Sopenharmony_ci else if (strcmp(multi, "single") == 0) 2020987da915Sopenharmony_ci m = FALSE; 2021987da915Sopenharmony_ci else { 2022987da915Sopenharmony_ci printf("rl pure [contig|noncontig] [single|multi]\n"); 2023987da915Sopenharmony_ci return; 2024987da915Sopenharmony_ci } 2025987da915Sopenharmony_ci 2026987da915Sopenharmony_ci test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1)); 2027987da915Sopenharmony_ci test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1)); 2028987da915Sopenharmony_ci test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1)); 2029987da915Sopenharmony_ci test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1)); 2030987da915Sopenharmony_ci test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1)); 2031987da915Sopenharmony_ci test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1)); 2032987da915Sopenharmony_ci test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1)); 2033987da915Sopenharmony_ci test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1)); 2034987da915Sopenharmony_ci test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1)); 2035987da915Sopenharmony_ci test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1)); 2036987da915Sopenharmony_ci test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1)); 2037987da915Sopenharmony_ci test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1)); 2038987da915Sopenharmony_ci test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2)); 2039987da915Sopenharmony_ci test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2)); 2040987da915Sopenharmony_ci test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2)); 2041987da915Sopenharmony_ci test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2)); 2042987da915Sopenharmony_ci test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3)); 2043987da915Sopenharmony_ci test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3)); 2044987da915Sopenharmony_ci test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4)); 2045987da915Sopenharmony_ci test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4)); 2046987da915Sopenharmony_ci test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5)); 2047987da915Sopenharmony_ci test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5)); 2048987da915Sopenharmony_ci test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5)); 2049987da915Sopenharmony_ci test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5)); 2050987da915Sopenharmony_ci test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5)); 2051987da915Sopenharmony_ci test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5)); 2052987da915Sopenharmony_ci test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5)); 2053987da915Sopenharmony_ci test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5)); 2054987da915Sopenharmony_ci test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5)); 2055987da915Sopenharmony_ci test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5)); 2056987da915Sopenharmony_ci test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5)); 2057987da915Sopenharmony_ci test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5)); 2058987da915Sopenharmony_ci test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6)); 2059987da915Sopenharmony_ci test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6)); 2060987da915Sopenharmony_ci} 2061987da915Sopenharmony_ci 2062987da915Sopenharmony_ci/** 2063987da915Sopenharmony_ci * test_rl_zero - Runlist test: Merge a zero-length runlist 2064987da915Sopenharmony_ci * 2065987da915Sopenharmony_ci * Description... 2066987da915Sopenharmony_ci * 2067987da915Sopenharmony_ci * Returns: 2068987da915Sopenharmony_ci */ 2069987da915Sopenharmony_cistatic void test_rl_zero(void) 2070987da915Sopenharmony_ci{ 2071987da915Sopenharmony_ci runlist_element *jim = NULL; 2072987da915Sopenharmony_ci runlist_element *bob = NULL; 2073987da915Sopenharmony_ci 2074987da915Sopenharmony_ci bob = calloc(3, sizeof(runlist_element)); 2075987da915Sopenharmony_ci if (!bob) 2076987da915Sopenharmony_ci return; 2077987da915Sopenharmony_ci 2078987da915Sopenharmony_ci MKRL(bob+0, 10, 99, 5) 2079987da915Sopenharmony_ci MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0) 2080987da915Sopenharmony_ci 2081987da915Sopenharmony_ci jim = test_rl_runlists_merge(jim, bob); 2082987da915Sopenharmony_ci if (!jim) 2083987da915Sopenharmony_ci return; 2084987da915Sopenharmony_ci 2085987da915Sopenharmony_ci free(jim); 2086987da915Sopenharmony_ci} 2087987da915Sopenharmony_ci 2088987da915Sopenharmony_ci/** 2089987da915Sopenharmony_ci * test_rl_frag_combine - Runlist test: Perform tests using fragmented files 2090987da915Sopenharmony_ci * @vol: 2091987da915Sopenharmony_ci * @attr1: 2092987da915Sopenharmony_ci * @attr2: 2093987da915Sopenharmony_ci * @attr3: 2094987da915Sopenharmony_ci * 2095987da915Sopenharmony_ci * Description... 2096987da915Sopenharmony_ci * 2097987da915Sopenharmony_ci * Returns: 2098987da915Sopenharmony_ci */ 2099987da915Sopenharmony_cistatic void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3) 2100987da915Sopenharmony_ci{ 2101987da915Sopenharmony_ci runlist_element *run1; 2102987da915Sopenharmony_ci runlist_element *run2; 2103987da915Sopenharmony_ci runlist_element *run3; 2104987da915Sopenharmony_ci 2105987da915Sopenharmony_ci run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL); 2106987da915Sopenharmony_ci if (!run1) 2107987da915Sopenharmony_ci return; 2108987da915Sopenharmony_ci 2109987da915Sopenharmony_ci run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL); 2110987da915Sopenharmony_ci if (!run2) 2111987da915Sopenharmony_ci return; 2112987da915Sopenharmony_ci 2113987da915Sopenharmony_ci run1 = test_rl_runlists_merge(run1, run2); 2114987da915Sopenharmony_ci 2115987da915Sopenharmony_ci run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL); 2116987da915Sopenharmony_ci if (!run3) 2117987da915Sopenharmony_ci return; 2118987da915Sopenharmony_ci 2119987da915Sopenharmony_ci run1 = test_rl_runlists_merge(run1, run3); 2120987da915Sopenharmony_ci 2121987da915Sopenharmony_ci free(run1); 2122987da915Sopenharmony_ci} 2123987da915Sopenharmony_ci 2124987da915Sopenharmony_ci/** 2125987da915Sopenharmony_ci * test_rl_frag - Runlist test: Create tests using very fragmented files 2126987da915Sopenharmony_ci * @test: 2127987da915Sopenharmony_ci * 2128987da915Sopenharmony_ci * Description... 2129987da915Sopenharmony_ci * 2130987da915Sopenharmony_ci * Returns: 2131987da915Sopenharmony_ci */ 2132987da915Sopenharmony_cistatic void test_rl_frag(char *test) 2133987da915Sopenharmony_ci{ 2134987da915Sopenharmony_ci ntfs_volume vol; 2135987da915Sopenharmony_ci ATTR_RECORD *attr1 = ntfs_malloc(1024); 2136987da915Sopenharmony_ci ATTR_RECORD *attr2 = ntfs_malloc(1024); 2137987da915Sopenharmony_ci ATTR_RECORD *attr3 = ntfs_malloc(1024); 2138987da915Sopenharmony_ci 2139987da915Sopenharmony_ci if (!attr1 || !attr2 || !attr3) 2140987da915Sopenharmony_ci goto out; 2141987da915Sopenharmony_ci 2142987da915Sopenharmony_ci vol.sb = NULL; 2143987da915Sopenharmony_ci vol.sector_size_bits = 9; 2144987da915Sopenharmony_ci vol.cluster_size = 2048; 2145987da915Sopenharmony_ci vol.cluster_size_bits = 11; 2146987da915Sopenharmony_ci vol.major_ver = 3; 2147987da915Sopenharmony_ci 2148987da915Sopenharmony_ci if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024)) 2149987da915Sopenharmony_ci goto out; 2150987da915Sopenharmony_ci if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024)) 2151987da915Sopenharmony_ci goto out; 2152987da915Sopenharmony_ci if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024)) 2153987da915Sopenharmony_ci goto out; 2154987da915Sopenharmony_ci 2155987da915Sopenharmony_ci if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3); 2156987da915Sopenharmony_ci else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2); 2157987da915Sopenharmony_ci else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3); 2158987da915Sopenharmony_ci else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1); 2159987da915Sopenharmony_ci else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2); 2160987da915Sopenharmony_ci else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1); 2161987da915Sopenharmony_ci else 2162987da915Sopenharmony_ci printf("Frag: No such test '%s'\n", test); 2163987da915Sopenharmony_ci 2164987da915Sopenharmony_ciout: 2165987da915Sopenharmony_ci free(attr1); 2166987da915Sopenharmony_ci free(attr2); 2167987da915Sopenharmony_ci free(attr3); 2168987da915Sopenharmony_ci} 2169987da915Sopenharmony_ci 2170987da915Sopenharmony_ci/** 2171987da915Sopenharmony_ci * test_rl_main - Runlist test: Program start (main) 2172987da915Sopenharmony_ci * @argc: 2173987da915Sopenharmony_ci * @argv: 2174987da915Sopenharmony_ci * 2175987da915Sopenharmony_ci * Description... 2176987da915Sopenharmony_ci * 2177987da915Sopenharmony_ci * Returns: 2178987da915Sopenharmony_ci */ 2179987da915Sopenharmony_ciint test_rl_main(int argc, char *argv[]) 2180987da915Sopenharmony_ci{ 2181987da915Sopenharmony_ci if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero(); 2182987da915Sopenharmony_ci else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]); 2183987da915Sopenharmony_ci else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]); 2184987da915Sopenharmony_ci else 2185987da915Sopenharmony_ci printf("rl [zero|frag|pure] {args}\n"); 2186987da915Sopenharmony_ci 2187987da915Sopenharmony_ci return 0; 2188987da915Sopenharmony_ci} 2189987da915Sopenharmony_ci 2190987da915Sopenharmony_ci#endif 2191987da915Sopenharmony_ci 2192