From ba810877ca358ef27b50d93994a2ee66a4a8f90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 25 May 2017 10:28:05 +0200 Subject: [PATCH 01/87] Bypassed cluster allocation errors using --ignore-fs-check in ntfsclone When using option --ignore-fs-check in ntfsclone, doubly allocated cluster still lead to aborting the process. Bypassing the error is useful for creating a metadata image intended for debugging, for example when the partition was not closed properly and the logfile has to be applied to restore the integrity of metadata. --- ntfsprogs/ntfsclone.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index 60264c9d..e161d1d1 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -3,7 +3,7 @@ * * Copyright (c) 2003-2006 Szabolcs Szakacsits * Copyright (c) 2004-2006 Anton Altaparmakov - * Copyright (c) 2010-2016 Jean-Pierre Andre + * Copyright (c) 2010-2017 Jean-Pierre Andre * Special image format support copyright (c) 2004 Per Olofsson * * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. @@ -1711,10 +1711,17 @@ static void walk_runs(struct ntfs_walk_cluster *walk) for (j = 0; j < lcn_length; j++) { u64 k = (u64)lcn + j; - if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1)) - err_exit("Cluster %llu referenced twice!\n" - "You didn't shutdown your Windows " - "properly?\n", (unsigned long long)k); + if (ntfs_bit_get_and_set(lcn_bitmap.bm, k, 1)) { + if (opt.ignore_fs_check) + Printf("Cluster %llu is referenced" + " twice!\n", + (unsigned long long)k); + else + err_exit("Cluster %llu referenced" + " twice!\nYou didn't shutdown" + " your Windows properly?\n", + (unsigned long long)k); + } } if (!opt.metadata_image) From 1797ab5ecd5a544f34c83a9c7efabd0c33e3c954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 25 May 2017 10:44:18 +0200 Subject: [PATCH 02/87] Upgraded ntfsrecover to support log files 2.0 When the fast restart mode of Windows 8 (or later) is activated, the log file format is different (version 2.0 instead of 1.1), having 32 temporaty blocks instead of 2. This patch upgrades ntfsrecover to take the new format into account. --- ntfsprogs/ntfsrecover.c | 239 ++++++++++++++++++++++++++++++++++------ ntfsprogs/ntfsrecover.h | 1 + ntfsprogs/playlog.c | 17 ++- 3 files changed, 219 insertions(+), 38 deletions(-) diff --git a/ntfsprogs/ntfsrecover.c b/ntfsprogs/ntfsrecover.c index 957c0390..4a06aa70 100644 --- a/ntfsprogs/ntfsrecover.c +++ b/ntfsprogs/ntfsrecover.c @@ -1,7 +1,7 @@ /* * Process log data from an NTFS partition * - * Copyright (c) 2012-2016 Jean-Pierre Andre + * Copyright (c) 2012-2017 Jean-Pierre Andre * * This program examines the Windows log file of an ntfs partition * and plays the committed transactions in order to restore the @@ -43,6 +43,7 @@ */ #define BASEBLKS 4 /* number of special blocks (always shown) */ +#define BASEBLKS2 34 /* number of special blocks when version >= 2.0 */ #define RSTBLKS 2 /* number of restart blocks */ #define BUFFERCNT 64 /* number of block buffers - a power of 2 */ #define NTFSBLKLTH 512 /* usa block size */ @@ -122,6 +123,7 @@ u32 clustersz = 0; int clusterbits; u32 blocksz; int blockbits; +int log_major; u16 bytespersect; u64 mftlcn; u32 mftrecsz; @@ -136,6 +138,7 @@ u64 committed_lsn; u64 synced_lsn; u64 latest_lsn; u64 restart_lsn; +u64 offset_mask; /* block number in an lsn */ unsigned long firstblk; /* first block to dump (option -r) */ unsigned long lastblk; /* last block to dump (option -r) */ u64 firstlcn; /* first block to dump (option -c) */ @@ -164,6 +167,7 @@ unsigned int playedactions; // change the name unsigned int redocount; unsigned int undocount; struct BUFFER *buffer_table[BASEBLKS + BUFFERCNT]; +unsigned int redirect[BASEBLKS2]; static const le16 SDS[4] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), @@ -319,18 +323,22 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num) { struct BUFFER *buffer; BOOL got; + int k; + unsigned int rnum; /* * The first four blocks are stored apart, to make * sure pages 2 and 3 and the page which is logically * before them can be accessed at the same time. + * (Only two blocks are stored apart if version >= 2.0) * Also, block 0 is smaller because it has to be read * before the block size is known. * Note : the last block is supposed to have an odd - * number, and cannot be overwritten by block 4 which - * follows logically. + * number, and cannot be overwritten by block 4 (or 34 + * if version >= 2.0) which follows logically. */ - if (num < BASEBLKS) + if ((num < RSTBLKS) + || ((log_major < 2) && (num < BASEBLKS))) buffer = buffer_table[num + BUFFERCNT]; else buffer = buffer_table[num & (BUFFERCNT - 1)]; @@ -342,20 +350,27 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num) buffer = (struct BUFFER*) malloc(sizeof(struct BUFFER) + blocksz); buffer->size = blocksz; - buffer->num = num + 1; /* forced to being read */ + buffer->rnum = num + 1; /* forced to being read */ buffer->safe = FALSE; if (num < BASEBLKS) buffer_table[num + BUFFERCNT] = buffer; else buffer_table[num & (BUFFERCNT - 1)] = buffer; } - if (buffer && (buffer->num != num)) { + rnum = num; + if (log_major >= 2) { + for (k=RSTBLKS; krnum != rnum)) { buffer->num = num; + buffer->rnum = rnum; if (ctx->vol) - got = (ntfs_attr_pread(log_na,(u64)num << blockbits, + got = (ntfs_attr_pread(log_na,(u64)rnum << blockbits, blocksz, buffer->block.data) == blocksz); else - got = !fseek(ctx->file, loclogblk(ctx, num), 0) + got = !fseek(ctx->file, loclogblk(ctx, rnum), 0) && (fread(buffer->block.data, blocksz, 1, ctx->file) == 1); if (got) { @@ -365,7 +380,7 @@ static const struct BUFFER *read_buffer(CONTEXT *ctx, unsigned int num) buffer->safe = !replaceusa(buffer, blocksz); } else { buffer->safe = FALSE; - fprintf(stderr,"** Could not read block %d\n", num); + fprintf(stderr,"** Could not read block %d\n", rnum); } } return (buffer && buffer->safe ? buffer : (const struct BUFFER*)NULL); @@ -1096,22 +1111,30 @@ static const struct BUFFER *findprevious(CONTEXT *ctx, const struct BUFFER *buf) skipped = 0; do { prevmiddle = FALSE; - if (prevblk > BASEBLKS) + if (prevblk > (log_major < 2 ? BASEBLKS : BASEBLKS2)) prevblk--; else - if (prevblk == BASEBLKS) + if (prevblk == (log_major < 2 ? BASEBLKS : BASEBLKS2)) prevblk = (logfilesz >> blockbits) - 1; else { rph = &buf->block.record; - prevblk = (sle64_to_cpu(rph->copy.file_offset) + if (log_major < 2) + prevblk = (sle64_to_cpu( + rph->copy.file_offset) >> blockbits) - 1; + else + prevblk = (sle64_to_cpu( + rph->copy.last_lsn) + & offset_mask) + >> (blockbits - 3); /* * If an initial block leads to block 4, it * can mean the last block or no previous * block at all. Using the last block is safer, * its lsn will indicate whether it is stale. */ - if (prevblk < BASEBLKS) + if (prevblk + < (log_major < 2 ? BASEBLKS : BASEBLKS2)) prevblk = (logfilesz >> blockbits) - 1; } /* No previous block if the log only consists of block 2 or 3 */ @@ -2706,7 +2729,7 @@ static void showrest(const RESTART_PAGE_HEADER *rest) (long)le32_to_cpu(rest->system_page_size)); printf("log_page_size %08lx\n", (long)le32_to_cpu(rest->log_page_size)); - printf("restart_area_offset %04x\n", + printf("restart_area_offset %04x\n", (int)le16_to_cpu(rest->restart_area_offset)); printf("minor_vers %d\n", (int)sle16_to_cpu(rest->minor_ver)); @@ -2876,6 +2899,8 @@ static BOOL dorest(CONTEXT *ctx, unsigned long blk, } } restart_lsn = synced_lsn; + offset_mask = ((u64)1 << (64 - le32_to_cpu(restart.seq_number_bits))) + - (1 << (blockbits - 3)); return (dirty); } @@ -2895,9 +2920,13 @@ static const struct BUFFER *read_restart(CONTEXT *ctx) { const struct BUFFER *buf; BOOL bad; + int blk; int major, minor; bad = FALSE; + for (blk=0; blkvol) { RESTART_PAGE_HEADER *rph; @@ -2961,6 +2990,7 @@ static const struct BUFFER *read_restart(CONTEXT *ctx) major, minor); bad = TRUE; } + log_major = major; if (bad) { buf = (const struct BUFFER*)NULL; } @@ -3343,7 +3373,8 @@ static TRISTATE backoverlap(CONTEXT *ctx, int blk, mblk = blk + 1; while (total < size) { if (mblk >= (logfilesz >> blockbits)) - mblk = BASEBLKS; + mblk = (log_major < 2 ? BASEBLKS + : BASEBLKS2); more = size - total; if (more > nextspace) more = nextspace; @@ -3427,9 +3458,15 @@ static TRISTATE backward_rcrd(CONTEXT *ctx, u32 blk, int skipped, if (optv) { if (optv >= 2) hexdump(data,blocksz); - printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n", - (long)blk,(long)blk, - (long long)loclogblk(ctx, blk)); + if (buf->rnum != blk) + printf("* RCRD for block %ld 0x%lx" + " in block %ld (addr 0x%llx)\n", + (long)blk,(long)blk,(long)buf->rnum, + (long long)loclogblk(ctx, blk)); + else + printf("* RCRD in block %ld 0x%lx (addr 0x%llx)\n", + (long)blk,(long)blk, + (long long)loclogblk(ctx, blk)); } else { if (optt) printf("block %ld\n",(long)blk); @@ -3551,9 +3588,15 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, u32 stopblk; TRISTATE state; - if (optv) - printf("\n* block %d at 0x%llx\n",(int)blk, + if (optv) { + if ((log_major >= 2) && (buf->rnum != blk)) + printf("\n* block %d for block %d at 0x%llx\n", + (int)buf->rnum,(int)blk, + (long long)loclogblk(ctx, buf->rnum)); + else + printf("\n* block %d at 0x%llx\n",(int)blk, (long long)loclogblk(ctx, blk)); + } ctx->firstaction = (struct ACTION_RECORD*)NULL; ctx->lastaction = (struct ACTION_RECORD*)NULL; nextbuf = (const struct BUFFER*)NULL; @@ -3576,7 +3619,9 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, skipped = blk - prevblk - 1; else skipped = blk - prevblk - 1 - + (logfilesz >> blockbits) - BASEBLKS; + + (logfilesz >> blockbits) + - (log_major < 2 ? BASEBLKS + : BASEBLKS2); magic = prevbuf->block.record.magic; switch (magic) { case magic_RCRD : @@ -3599,9 +3644,18 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, (long long)loclogblk(ctx, blk), (long)prevblk); else - printf("\n* block %ld at 0x%llx\n", - (long)blk, - (long long)loclogblk(ctx, blk)); + if ((log_major >= 2) + && (buf->rnum != blk)) + printf("\n* block %ld for block %ld at 0x%llx\n", + (long)buf->rnum, + (long)blk, + (long long)loclogblk( + ctx,buf->rnum)); + else + printf("\n* block %ld at 0x%llx\n", + (long)blk, + (long long)loclogblk( + ctx, blk)); } state = backward_rcrd(ctx, blk, skipped, buf, prevbuf, nextbuf); @@ -3632,6 +3686,98 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, return (state == T_ERR ? 1 : 0); } +/* + * Determine the sequencing of blocks (when version >= 2.0) + * + * Blocks 2..17 and 18..33 are temporary blocks being filled until + * they are copied to their target locations, so there are three + * possible location for recent blocks. + * + * Returns the latest target block number + */ + +static int block_sequence(CONTEXT *ctx) +{ + const struct BUFFER *buf; + int blk; + int k; + int target_blk; + int latest_blk; + s64 final_lsn; + s64 last_lsn; + s64 last_lsn12; + s64 last_lsn1, last_lsn2; + + final_lsn = 0; + for (blk=RSTBLKS; 2*blk<(RSTBLKS+BASEBLKS2); blk++) { + /* First temporary block */ + last_lsn1 = 0; + buf = read_buffer(ctx, blk); + if (buf && (buf->block.record.magic == magic_RCRD)) { + last_lsn1 = le64_to_cpu( + buf->block.record.copy.last_lsn); + if (!final_lsn + || ((s64)(last_lsn1 - final_lsn) > 0)) + final_lsn = last_lsn1; + } + /* Second temporary block */ + buf = read_buffer(ctx, blk + (BASEBLKS2 - RSTBLKS)/2); + last_lsn2 = 0; + if (buf && (buf->block.record.magic == magic_RCRD)) { + last_lsn2 = le64_to_cpu( + buf->block.record.copy.last_lsn); + if (!final_lsn + || ((s64)(last_lsn2 - final_lsn) > 0)) + final_lsn = last_lsn2; + } + /* the latest last_lsn defines the target block */ + last_lsn12 = 0; + latest_blk = 0; + if (last_lsn1 || last_lsn2) { + if (!last_lsn2 + || ((s64)(last_lsn1 - last_lsn2) > 0)) { + last_lsn12 = last_lsn1; + latest_blk = blk; + } + if (!last_lsn1 + || ((s64)(last_lsn1 - last_lsn2) <= 0)) { + last_lsn12 = last_lsn2; + latest_blk = blk + (BASEBLKS2 - RSTBLKS)/2; + } + } + last_lsn = 0; + target_blk = 0; + if (last_lsn12) { + target_blk = (last_lsn12 & offset_mask) + >> (blockbits - 3); + buf = read_buffer(ctx, target_blk); + if (buf && (buf->block.record.magic == magic_RCRD)) { + last_lsn = le64_to_cpu( + buf->block.record.copy.last_lsn); + if (!final_lsn + || ((s64)(last_lsn - final_lsn) > 0)) + final_lsn = last_lsn; + } + } + /* redirect to the latest block */ + if (latest_blk + && (!last_lsn || ((s64)(last_lsn - last_lsn12) < 0))) + redirect[latest_blk] = target_blk; + } + if (optv) { + printf("\n Blocks redirected :\n"); + for (k=RSTBLKS; k> (blockbits - 3); + if (optv > 1) + printf("final lsn %llx in blk %d\n",(long long)final_lsn,blk); + return (blk); +} + static int walk(CONTEXT *ctx) { const struct BUFFER *buf; @@ -3644,6 +3790,7 @@ static int walk(CONTEXT *ctx) u32 blk; u32 nextblk; u32 prevblk; + u32 finalblk; int err; u16 blkheadsz; u16 pos; @@ -3657,6 +3804,7 @@ static int walk(CONTEXT *ctx) } done = FALSE; dirty = TRUE; + finalblk = 0; err = 0; blk = 0; pos = 0; @@ -3675,7 +3823,8 @@ static int walk(CONTEXT *ctx) while (!done) { /* next block is needed to process the current one */ if ((nextblk >= (logfilesz >> blockbits)) && (optr || optf)) - nextbuf = read_buffer(ctx, BASEBLKS); + nextbuf = read_buffer(ctx, + (log_major < 2 ? BASEBLKS : BASEBLKS2)); else nextbuf = read_buffer(ctx,nextblk); if (nextbuf) { @@ -3741,17 +3890,30 @@ static int walk(CONTEXT *ctx) } blk = nextblk; nextblk++; + + if (!optr && (log_major >= 2) && (nextblk == RSTBLKS)) { + finalblk = block_sequence(ctx); + if (!finalblk) { + done = TRUE; + err = 1; + } + } + if (optr) { /* Only selected range */ - if ((nextblk == BASEBLKS) && (nextblk < firstblk)) + u32 endblk; + + endblk = (log_major < 2 ? BASEBLKS : RSTBLKS); + if ((nextblk == endblk) && (nextblk < firstblk)) nextblk = firstblk; - if ((blk >= BASEBLKS) && (blk > lastblk)) + if ((blk >= endblk) && (blk > lastblk)) done = TRUE; } else if (optf) { /* Full log, forward */ if (blk*blocksz >= logfilesz) done = TRUE; } else - if (optb || optp || optu || opts) { + if (optb || optp || optu || opts + || (log_major >= 2)) { /* Restart blocks only (2 blocks) */ if (blk >= RSTBLKS) done = TRUE; @@ -3782,16 +3944,18 @@ static int walk(CONTEXT *ctx) } if (optv && opts && !dirty) printf("* Volume is clean, nothing to do\n"); - if (optb || optp || optu - || (opts && dirty)) { + if (log_major >= 2) + blk = finalblk; + if (!err + && (optb || optp || optu || (opts && dirty))) { playedactions = 0; ctx->firstaction = (struct ACTION_RECORD*)NULL; ctx->lastaction = (struct ACTION_RECORD*)NULL; - buf = nextbuf; - nextbuf = read_buffer(ctx, blk+1); - startbuf = best_start(buf,nextbuf); - if (startbuf) { - if (startbuf == nextbuf) { + if (log_major < 2) { + buf = nextbuf; + nextbuf = read_buffer(ctx, blk+1); + startbuf = best_start(buf,nextbuf); + if (startbuf && (startbuf == nextbuf)) { /* nextbuf is better, show blk */ if (optv && buf) { printf("* Ignored block %d at 0x%llx\n", @@ -3818,6 +3982,11 @@ static int walk(CONTEXT *ctx) &nextbuf->block.record); } } + } else { + buf = startbuf = read_buffer(ctx, blk); + nextbuf = (const struct BUFFER*)NULL; + } + if (startbuf) { /* The latest buf may be more recent than restart */ rph = &buf->block.record; if ((s64)(sle64_to_cpu(rph->last_end_lsn) diff --git a/ntfsprogs/ntfsrecover.h b/ntfsprogs/ntfsrecover.h index abc72a0f..0e8ba2bf 100644 --- a/ntfsprogs/ntfsrecover.h +++ b/ntfsprogs/ntfsrecover.h @@ -74,6 +74,7 @@ enum ACTIONS { struct BUFFER { unsigned int num; + unsigned int rnum; unsigned int size; unsigned int headsz; BOOL safe; diff --git a/ntfsprogs/playlog.c b/ntfsprogs/playlog.c index 69584694..07b03c46 100644 --- a/ntfsprogs/playlog.c +++ b/ntfsprogs/playlog.c @@ -1,7 +1,7 @@ /* * Redo or undo a list of logged actions * - * Copyright (c) 2014-2016 Jean-Pierre Andre + * Copyright (c) 2014-2017 Jean-Pierre Andre * */ @@ -229,7 +229,7 @@ static int sanity_indx_list(const char *buffer, u32 k, u32 end) err = 0; done = FALSE; - while ((k <= end) && !done) { + while ((k <= end) && !done && !err) { lth = getle16(buffer,k+8); if (optv > 1) /* Usual indexes can be determined from size */ @@ -270,9 +270,20 @@ static int sanity_indx_list(const char *buffer, u32 k, u32 end) (long long)getle64(buffer,k), (int)lth, (int)getle16(buffer,k+12),(int)k); + if ((lth < 80) || (lth & 7)) { + printf("** Invalid index record" + " length %d\n",lth); + err = 1; + } } done = (feedle16(buffer,k+12) & INDEX_ENTRY_END) || !lth; - k += lth; + if (lth & 7) { + if (optv <= 1) /* Do not repeat the warning */ + printf("** Invalid index record length %d\n", + lth); + err = 1; + } else + k += lth; } if (k != end) { printf("** Bad index record length %ld (computed %ld)\n", From 5be0b9f62a7eabddbac3119a12b98cc1b4c233d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 25 May 2017 10:52:54 +0200 Subject: [PATCH 03/87] Fixed the computation of highest_vcn when applying a runlist fixup When a file is partially truncated, the highest_vcn has to be recomputed before the file size is adjusted. As a consequence the unmapped run inserted at the end of runlist to match the file size should not be taken into account when determining the higest_vcn. --- ntfsprogs/playlog.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/playlog.c b/ntfsprogs/playlog.c index 07b03c46..14d24564 100644 --- a/ntfsprogs/playlog.c +++ b/ntfsprogs/playlog.c @@ -806,7 +806,9 @@ static int adjust_high_vcn(ntfs_volume *vol, ATTR_RECORD *attr) rl = ntfs_mapping_pairs_decompress(vol, attr, (runlist_element*)NULL); if (rl) { xrl = rl; - while (xrl->length) + if (xrl->length) + xrl++; + while ((xrl->length) && (xrl->lcn != LCN_RL_NOT_MAPPED)) xrl++; high_vcn = xrl->vcn - 1; attr->highest_vcn = cpu_to_sle64(high_vcn); From 866f5cf9e6afe331284c55f31df57252ee01e71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 20 Jun 2017 08:51:09 +0200 Subject: [PATCH 04/87] Mentioned ntfsfallocate and ntfsrecover in the general ntfsprogs manual The most recent ntfsprogs utilities should be referenced in the table of contents. --- ntfsprogs/ntfsprogs.8.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ntfsprogs/ntfsprogs.8.in b/ntfsprogs/ntfsprogs.8.in index f00f5f49..82abf861 100644 --- a/ntfsprogs/ntfsprogs.8.in +++ b/ntfsprogs/ntfsprogs.8.in @@ -31,6 +31,9 @@ available for free and come with full source code. .BR ntfscp (8) \- Copy a file to an NTFS volume. .PP +.BR ntfsfallocate (8) +\- Preallocate space to a file on an NTFS volume +.PP .BR ntfsfix (8) \- Check and fix some common errors, clear the LogFile and make Windows perform a thorough check next time it boots. @@ -47,6 +50,9 @@ perform a thorough check next time it boots. .BR ntfsresize (8) \- Resize NTFS without losing data. .PP +.BR ntfsrecover (8) +\- Recover updates committed by Windows on an NTFS volume. +.PP .BR ntfstruncate (8) \- Truncate a file on an NTFS volume. .PP From cdd58aaee3b90bb9208a347b60c908947dbd7cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 20 Jun 2017 08:56:00 +0200 Subject: [PATCH 05/87] Fixed updating the vcn of subtree in ntfsrecover The logic for determining where the actions SetIndexEntryVcnRoot and SetIndexEntryVcnAllocation had to insert a new vcn was unclear and was wrong in some situations. This is to fix the logic as determined by new evidence. --- ntfsprogs/playlog.c | 52 ++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/ntfsprogs/playlog.c b/ntfsprogs/playlog.c index 14d24564..cb850389 100644 --- a/ntfsprogs/playlog.c +++ b/ntfsprogs/playlog.c @@ -2662,12 +2662,14 @@ static int redo_update_root_vcn(ntfs_volume *vol, expected = ((const char*)&action->record) + get_undo_offset(&action->record); length = le16_to_cpu(action->record.redo_length); -// length must be 8 - target = le16_to_cpu(action->record.record_offset) - + le16_to_cpu(action->record.attribute_offset) -+ 16; // explanation needed (right justified ?) - err = change_resident_expect(vol, action, buffer, data, expected, - target, length, AT_INDEX_ROOT); + if (length == 8) { + target = le16_to_cpu(action->record.record_offset) + + le16_to_cpu(action->record.attribute_offset); + /* target is right-justified to end of attribute */ + target += getle16(buffer, target + 8) - length; + err = change_resident_expect(vol, action, buffer, data, + expected, target, length, AT_INDEX_ROOT); + } return (err); } @@ -2751,11 +2753,13 @@ static int redo_update_vcn(ntfs_volume *vol, data = ((const char*)&action->record) + get_redo_offset(&action->record); length = le16_to_cpu(action->record.redo_length); - /* target is left-justified to creation time */ - target = le16_to_cpu(action->record.record_offset) - + le16_to_cpu(action->record.attribute_offset) - + 16; // to better describe - err = update_index(vol, action, buffer, data, target, length); + if (length == 8) { + target = le16_to_cpu(action->record.record_offset) + + le16_to_cpu(action->record.attribute_offset); + /* target is right-justified to end of attribute */ + target += getle16(buffer, target + 8) - length; + err = update_index(vol, action, buffer, data, target, length); + } return (err); } @@ -3581,11 +3585,13 @@ static int undo_update_vcn(ntfs_volume *vol, const struct ACTION_RECORD *action, data = ((const char*)&action->record) + get_undo_offset(&action->record); length = le16_to_cpu(action->record.undo_length); - /* target is left-justified to creation time */ - target = le16_to_cpu(action->record.record_offset) - + le16_to_cpu(action->record.attribute_offset) - + 16; // to better describe - err = update_index(vol, action, buffer, data, target, length); + if (length == 8) { + target = le16_to_cpu(action->record.record_offset) + + le16_to_cpu(action->record.attribute_offset); + /* target is right-justified to end of attribute */ + target += getle16(buffer, target + 8) - length; + err = update_index(vol, action, buffer, data, target, length); + } return (err); } @@ -3788,12 +3794,14 @@ static int undo_update_root_vcn(ntfs_volume *vol, expected = ((const char*)&action->record) + get_redo_offset(&action->record); length = le16_to_cpu(action->record.undo_length); - /* the fixup is right-justified to the name length */ - target = le16_to_cpu(action->record.record_offset) - + le16_to_cpu(action->record.attribute_offset) - + 16; // explanation needed - err = change_resident_expect(vol, action, buffer, data, expected, - target, length, AT_INDEX_ROOT); + if (length == 8) { + target = le16_to_cpu(action->record.record_offset) + + le16_to_cpu(action->record.attribute_offset); + /* target is right-justified to end of attribute */ + target += getle16(buffer, target + 8) - length; + err = change_resident_expect(vol, action, buffer, data, + expected, target, length, AT_INDEX_ROOT); + } return (err); } From d108009c7cfd9556774c2f5a3a520a1dbc692694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 11 Aug 2017 09:29:52 +0200 Subject: [PATCH 06/87] Fixed a typo A typo made a directive for plugin developers difficult to understand --- include/ntfs-3g/plugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h index d32e149d..7e7b2a7c 100644 --- a/include/ntfs-3g/plugin.h +++ b/include/ntfs-3g/plugin.h @@ -110,7 +110,7 @@ typedef struct plugin_operations { * Get a symbolic link * The symbolic link must be returned in an allocated buffer, * encoded in a zero terminated multibyte string compatible - * which the locale mount option. + * with the locale mount option. * The returned value is zero for success or a negative errno * value for failure. */ From 67feb2c286a30a22470fb93e505783b90d435085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 11 Aug 2017 09:34:31 +0200 Subject: [PATCH 07/87] Relaxed limitations on security descriptors Windows 10 brought security descriptors which contain ACE's related to new ways of accessing files or directories. These ACE are now accepted with minimal consistency checks. They are still ignored for translating permissions and for Windows-type inheritance. --- include/ntfs-3g/layout.h | 20 +++++++++++++ libntfs-3g/acls.c | 64 ++++++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 816be49b..880d54b8 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -1433,6 +1433,26 @@ typedef enum { /* This one is for WinNT&2k. */ ACCESS_MAX_MS_ACE_TYPE = 8, + + /* Windows XP and later */ + ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 9, + ACCESS_DENIED_CALLBACK_ACE_TYPE = 10, + ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 11, + ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 12, + SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 13, + SYSTEM_ALARM_CALLBACK_ACE_TYPE = 14, /* reserved */ + SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 15, + SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 16, /* reserved */ + + /* Windows Vista and later */ + SYSTEM_MANDATORY_LABEL_ACE_TYPE = 17, + + /* Windows 8 and later */ + SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE = 18, + SYSTEM_SCOPED_POLICY_ID_ACE_TYPE = 19, + + /* Windows 10 and later */ + SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE = 20, } __attribute__((__packed__)) ACE_TYPES; /** diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c index b91e0413..9df71083 100644 --- a/libntfs-3g/acls.c +++ b/libntfs-3g/acls.c @@ -4,7 +4,7 @@ * This module is part of ntfs-3g library, but may also be * integrated in tools running over Linux or Windows * - * Copyright (c) 2007-2016 Jean-Pierre Andre + * Copyright (c) 2007-2017 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -560,16 +560,34 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) pace = (const ACCESS_ALLOWED_ACE*) &((const char*)pacl)[offace]; acesz = le16_to_cpu(pace->size); - if (((offace + acesz) > end) - || !ntfs_valid_sid(&pace->sid)) - ok = FALSE; - else { - /* Win10 may insert garbage in the last ACE */ + switch (pace->type) { + case ACCESS_ALLOWED_ACE_TYPE : + case ACCESS_DENIED_ACE_TYPE : wantsz = ntfs_sid_size(&pace->sid) + 8; - if (((nace < (acecnt - 1)) - && (wantsz != acesz)) + if (((offace + acesz) > end) + || !ntfs_valid_sid(&pace->sid) + || (wantsz != acesz)) + ok = FALSE; + break; + case SYSTEM_AUDIT_ACE_TYPE : + case ACCESS_ALLOWED_CALLBACK_ACE_TYPE : + case ACCESS_DENIED_CALLBACK_ACE_TYPE : + case SYSTEM_AUDIT_CALLBACK_ACE_TYPE : + case SYSTEM_MANDATORY_LABEL_ACE_TYPE : + case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE : + case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE : + /* Extra data after the SID */ + wantsz = ntfs_sid_size(&pace->sid) + 8; + if (((offace + acesz) > end) + || !ntfs_valid_sid(&pace->sid) || (wantsz > acesz)) ok = FALSE; + break; + default : + /* SID at a different location */ + if ((offace + acesz) > end) + ok = FALSE; + break; } offace += acesz; } @@ -683,6 +701,7 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, int acesz; int usidsz; int gsidsz; + BOOL acceptable; const ACCESS_ALLOWED_ACE *poldace; ACCESS_ALLOWED_ACE *pnewace; ACCESS_ALLOWED_ACE *pauthace; @@ -707,6 +726,20 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src); acesz = le16_to_cpu(poldace->size); src += acesz; + /* + * Currently only ACE for file or directory access are + * processed. More information needed about what to do + * for other types (whose SID may be at a different location) + */ + switch (poldace->type) { + case ACCESS_ALLOWED_ACE_TYPE : + case ACCESS_DENIED_ACE_TYPE : + acceptable = TRUE; + break; + default : + acceptable = FALSE; + break; + } /* * Extract inheritance for access, including inheritance for * access from an ACE with is both applied and inheritable. @@ -721,6 +754,7 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, * "information." */ if ((poldace->flags & selection) + && acceptable && (!fordir || (poldace->flags & NO_PROPAGATE_INHERIT_ACE) || (poldace->mask & (GENERIC_ALL | GENERIC_READ @@ -810,9 +844,10 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, * Inheritance for access, specific to * creator-owner (and creator-group) */ - if (fordir || !inherited - || (poldace->flags - & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) { + if ((fordir || !inherited + || (poldace->flags + & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) + && acceptable) { pnewace = (ACCESS_ALLOWED_ACE*) ((char*)newacl + dst); memcpy(pnewace,poldace,acesz); @@ -869,7 +904,8 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, && !(poldace->flags & (CONTAINER_INHERIT_ACE | NO_PROPAGATE_INHERIT_ACE))) pnewace->flags |= INHERIT_ONLY_ACE; - if ((poldace->flags & CONTAINER_INHERIT_ACE) + if (acceptable + && (poldace->flags & CONTAINER_INHERIT_ACE) && !(poldace->flags & NO_PROPAGATE_INHERIT_ACE) && !ntfs_same_sid(&poldace->sid, ownersid) && !ntfs_same_sid(&poldace->sid, groupsid)) { @@ -3867,7 +3903,9 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( } } } - if (!ignore) { + if (((pace->type == ACCESS_ALLOWED_ACE_TYPE) + || (pace->type == ACCESS_DENIED_ACE_TYPE)) + && !ignore) { pxace->perms = 0; /* specific decoding for vtx/uid/gid */ if (pxace->tag == POSIX_ACL_SPECIAL) { From 1611b2190866dd6ec736209c86c129f5943bb333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 11 Aug 2017 09:42:37 +0200 Subject: [PATCH 08/87] Enabled directory operations in plugins Windows 10 brought a new type of reparse point for directories (0x80000018), so add opendir() and readdir() to the plugin interface to take directories into account. The interface for releasedir() is merged with release() as the plugins can discriminate them if needed. --- include/ntfs-3g/plugin.h | 31 ++++++++++++++++-- src/lowntfs-3g.c | 70 +++++++++++++++++++++++++++++++++++++--- src/ntfs-3g.c | 31 ++++++++++++++++-- 3 files changed, 122 insertions(+), 10 deletions(-) diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h index 7e7b2a7c..a9d56a5f 100644 --- a/include/ntfs-3g/plugin.h +++ b/include/ntfs-3g/plugin.h @@ -30,8 +30,9 @@ #ifndef _NTFS_PLUGIN_H #define _NTFS_PLUGIN_H -#include "inode.h" #include "layout.h" +#include "inode.h" +#include "dir.h" struct fuse_file_info; struct stat; @@ -71,10 +72,10 @@ typedef struct plugin_operations { struct fuse_file_info *fi); /* - * Release an open file + * Release an open file or directory * This is only called if fi->fh has been set to a non-null * value while opening. It may be used to free some context - * specific to the open file. + * specific to the open file or directory * The returned value is zero for success or a negative errno * value for failure. */ @@ -126,6 +127,30 @@ typedef struct plugin_operations { */ int (*truncate)(ntfs_inode *ni, const REPARSE_POINT *reparse, off_t size); + /* + * Open a directory + * The field fi->flags indicates the kind of opening. + * The field fi->fh may be used to store some information which + * will be available to subsequent readdir(). When used + * this field must be non-null and freed in release(). + * The returned value is zero for success and a negative errno + * value for failure. + */ + int (*opendir)(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi); + + /* + * Get entries from a directory + * + * Use the filldir() function with fillctx argument to return + * the directory entries. + * Names "." and ".." are expected to be returned. + * The returned value is zero for success and a negative errno + * value for failure. + */ + int (*readdir)(ntfs_inode *ni, const REPARSE_POINT *reparse, + s64 *pos, void *fillctx, ntfs_filldir_t filldir, + struct fuse_file_info *fi); } plugin_operations_t; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 748ba518..4bd211f6 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -203,6 +203,9 @@ typedef struct fill_item { typedef struct fill_context { struct fill_item *first; struct fill_item *last; +#ifndef DISABLE_PLUGINS + u64 fh; +#endif /* DISABLE_PLUGINS */ off_t off; fuse_req_t req; fuse_ino_t ino; @@ -1292,6 +1295,17 @@ static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino, if (!ntfs_allowed_access(&security,ni,accesstype)) res = -EACCES; } + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + fi->fh = 0; + res = CALL_REPARSE_PLUGIN(ni, opendir, fi); +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } if (ntfs_inode_close(ni)) set_fuse_error(&res); if (!res) { @@ -1305,6 +1319,9 @@ static void ntfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino, fill->filled = FALSE; fill->ino = ino; fill->off = 0; +#ifndef DISABLE_PLUGINS + fill->fh = fi->fh; +#endif /* DISABLE_PLUGINS */ } fi->fh = (long)fill; } @@ -1321,9 +1338,15 @@ static void ntfs_fuse_releasedir(fuse_req_t req, fuse_ino_t ino __attribute__((unused)), struct fuse_file_info *fi) { +#ifndef DISABLE_PLUGINS + struct fuse_file_info ufi; + ntfs_inode *ni; +#endif /* DISABLE_PLUGINS */ ntfs_fuse_fill_context_t *fill; ntfs_fuse_fill_item_t *current; + int res; + res = 0; fill = (ntfs_fuse_fill_context_t*)(long)fi->fh; if (fill && (fill->ino == ino)) { /* make sure to clear results */ @@ -1333,16 +1356,38 @@ static void ntfs_fuse_releasedir(fuse_req_t req, free(fill->first); fill->first = current; } +#ifndef DISABLE_PLUGINS + if (fill->fh) { + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + ni = ntfs_inode_open(ctx->vol, INODE(ino)); + if (ni) { + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + memcpy(&ufi, fi, sizeof(ufi)); + ufi.fh = fill->fh; + res = CALL_REPARSE_PLUGIN(ni, release, + &ufi); + } + if (ntfs_inode_close(ni) && !res) + res = -errno; + } else + res = -errno; + } +#endif /* DISABLE_PLUGINS */ fill->ino = 0; free(fill); } - fuse_reply_err(req, 0); + fuse_reply_err(req, -res); } static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off __attribute__((unused)), struct fuse_file_info *fi __attribute__((unused))) { +#ifndef DISABLE_PLUGINS + struct fuse_file_info ufi; +#endif /* DISABLE_PLUGINS */ ntfs_fuse_fill_item_t *first; ntfs_fuse_fill_item_t *current; ntfs_fuse_fill_context_t *fill; @@ -1379,10 +1424,27 @@ static void ntfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, if (!ni) err = -errno; else { - if (ntfs_readdir(ni, &pos, fill, - (ntfs_filldir_t) + if (ni->flags + & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + memcpy(&ufi, fi, sizeof(ufi)); + ufi.fh = fill->fh; + err = CALL_REPARSE_PLUGIN(ni, + readdir, &pos, fill, + (ntfs_filldir_t) + ntfs_fuse_filler, &ufi); +#else /* DISABLE_PLUGINS */ + err = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else { + if (ntfs_readdir(ni, &pos, fill, + (ntfs_filldir_t) ntfs_fuse_filler)) - err = -errno; + err = -errno; + } fill->filled = TRUE; ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index e73eee33..9558d50b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -1300,6 +1300,17 @@ static int ntfs_fuse_opendir(const char *path, ni,accesstype)) res = -EACCES; } + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + fi->fh = 0; + res = CALL_REPARSE_PLUGIN(ni, opendir, fi); +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } if (ntfs_inode_close(ni)) set_fuse_error(&res); } else @@ -1323,9 +1334,22 @@ static int ntfs_fuse_readdir(const char *path, void *buf, ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; - if (ntfs_readdir(ni, &pos, &fill_ctx, - (ntfs_filldir_t)ntfs_fuse_filler)) - err = -errno; + + if (ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + err = CALL_REPARSE_PLUGIN(ni, readdir, &pos, &fill_ctx, + (ntfs_filldir_t)ntfs_fuse_filler, fi); +#else /* DISABLE_PLUGINS */ + err = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else { + if (ntfs_readdir(ni, &pos, &fill_ctx, + (ntfs_filldir_t)ntfs_fuse_filler)) + err = -errno; + } ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); if (ntfs_inode_close(ni)) set_fuse_error(&err); @@ -3732,6 +3756,7 @@ static struct fuse_operations ntfs_3g_ops = { #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) .access = ntfs_fuse_access, .opendir = ntfs_fuse_opendir, + .releasedir = ntfs_fuse_release, #endif #ifdef HAVE_SETXATTR .getxattr = ntfs_fuse_getxattr, From 45ba639781ec4d0f022ac5e58bca0245dca38a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 11 Aug 2017 09:52:54 +0200 Subject: [PATCH 09/87] Decoded more reparse tags in ntfsinfo Displayed some information in ntfsinfo output about a few new types of reparse point brought by Windows 10. --- include/ntfs-3g/layout.h | 4 ++++ ntfsprogs/ntfsinfo.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 880d54b8..1df07b98 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -2433,7 +2433,11 @@ typedef enum { IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x80000007), IO_REPARSE_TAG_SYMLINK = const_cpu_to_le32(0xA000000C), IO_REPARSE_TAG_WIM = const_cpu_to_le32(0x80000008), + IO_REPARSE_TAG_DFM = const_cpu_to_le32(0x80000016), IO_REPARSE_TAG_WOF = const_cpu_to_le32(0x80000017), + IO_REPARSE_TAG_WCI = const_cpu_to_le32(0x80000018), + IO_REPARSE_TAG_GVFS = const_cpu_to_le32(0x9000001C), + IO_REPARSE_TAG_LX_SYMLINK = const_cpu_to_le32(0xA000001D), IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff), } PREDEFINED_REPARSE_TAGS; diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 18d001af..94660b95 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -8,7 +8,7 @@ * Copyright (c) 2004-2005 Yuval Fledel * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2005 Cristian Klein - * Copyright (c) 2011-2015 Jean-Pierre Andre + * Copyright (c) 2011-2017 Jean-Pierre Andre * * This utility will dump a file's attributes. * @@ -119,7 +119,7 @@ static void version(void) printf(" 2003 Leonard NorrgÄrd\n"); printf(" 2004-2005 Yuval Fledel\n"); printf(" 2004-2007 Yura Pakhuchiy\n"); - printf(" 2011-2014 Jean-Pierre Andre\n"); + printf(" 2011-2017 Jean-Pierre Andre\n"); printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -425,6 +425,15 @@ static const char *reparse_type_name(le32 tag) case IO_REPARSE_TAG_DEDUP : name = " (deduplicated)"; break; + case IO_REPARSE_TAG_WCI : + name = " (Windows container)"; + break; + case IO_REPARSE_TAG_NFS : + name = " (NFS symlink)"; + break; + case IO_REPARSE_TAG_LX_SYMLINK : + name = " (Linux symlink)"; + break; default : name = ""; break; From 4128e9da58f7b557a146d31d9e955546ebb048f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 11 Aug 2017 09:56:44 +0200 Subject: [PATCH 10/87] Logged falling back to mounting read-only When the ntfs image is unclean, usually because it was not unmounted properly from Windows, mounting read-write is denied and falls back to read-only. Log this situation in the syslog, so that users mounting through /etc/fstab can more easily know what is going on. Also remove the "rw" option if it was stated. --- src/lowntfs-3g.c | 6 +++++- src/ntfs-3g.c | 6 +++++- src/ntfs-3g_common.c | 5 +++++ src/ntfs-3g_common.h | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 4bd211f6..87207ed8 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4449,10 +4449,14 @@ int main(int argc, char *argv[]) /* Force read-only mount if the device was found read-only */ if (!ctx->ro && NVolReadOnly(ctx->vol)) { + ctx->rw = FALSE; ctx->ro = TRUE; if (ntfs_strinsert(&parsed_options, ",ro")) goto err_out; - } + ntfs_log_info("Could not mount read-write, trying read-only\n"); + } else + if (ctx->rw && ntfs_strinsert(&parsed_options, ",rw")) + goto err_out; /* We must do this after ntfs_open() to be able to set the blksize */ if (ctx->blkdev && set_fuseblk_options(&parsed_options)) goto err_out; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 9558d50b..b224e188 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4212,10 +4212,14 @@ int main(int argc, char *argv[]) /* Force read-only mount if the device was found read-only */ if (!ctx->ro && NVolReadOnly(ctx->vol)) { + ctx->rw = FALSE; ctx->ro = TRUE; if (ntfs_strinsert(&parsed_options, ",ro")) goto err_out; - } + ntfs_log_info("Could not mount read-write, trying read-only\n"); + } else + if (ctx->rw && ntfs_strinsert(&parsed_options, ",rw")) + goto err_out; /* We must do this after ntfs_open() to be able to set the blksize */ if (ctx->blkdev && set_fuseblk_options(&parsed_options)) goto err_out; diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 708b177e..8fd7ee11 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -85,6 +85,7 @@ const struct DEFOPTION optionlist[] = { { "atime", OPT_ATIME, FLGOPT_BOGUS }, { "relatime", OPT_RELATIME, FLGOPT_BOGUS }, { "delay_mtime", OPT_DMTIME, FLGOPT_DECIMAL | FLGOPT_OPTIONAL }, + { "rw", OPT_RW, FLGOPT_BOGUS }, { "fake_rw", OPT_FAKE_RW, FLGOPT_BOGUS }, { "fsname", OPT_FSNAME, FLGOPT_NOSUPPORT }, { "no_def_opts", OPT_NO_DEF_OPTS, FLGOPT_BOGUS }, @@ -291,6 +292,9 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx, case OPT_FAKE_RW : ctx->ro = TRUE; break; + case OPT_RW : + ctx->rw = TRUE; + break; case OPT_NOATIME : ctx->atime = ATIME_DISABLED; break; @@ -542,6 +546,7 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx, if (ctx->ro) { ctx->secure_flags &= ~(1 << SECURITY_ADDSECURIDS); ctx->hiberfile = FALSE; + ctx->rw = FALSE; } exit: free(options); diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index e2d8790d..bcffe4f7 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -51,6 +51,7 @@ enum { OPT_ATIME, OPT_RELATIME, OPT_DMTIME, + OPT_RW, OPT_FAKE_RW, OPT_FSNAME, OPT_NO_DEF_OPTS, @@ -135,6 +136,7 @@ typedef struct { ntfs_atime_t atime; s64 dmtime; BOOL ro; + BOOL rw; BOOL show_sys_files; BOOL hide_hid_files; BOOL hide_dot_files; From a0bc659c7ff0205cfa2b2fc3429ee4d944e1bcc3 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Wed, 20 Sep 2017 04:56:06 +0200 Subject: [PATCH 11/87] lowntfs-3g.c: Fix compile error when struct stat doesn't have st_*tim. The struct members for the time fields in struct stat vary depending on platform, so introduce #ifdefs using the config.h-supplied definitions for determining which stat time definition is appropriate. --- src/lowntfs-3g.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 87207ed8..71eceeb4 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -1989,15 +1989,41 @@ static int ntfs_fuse_utimens(struct SECURITY_CONTEXT *scx, fuse_ino_t ino, if (to_set & FUSE_SET_ATTR_ATIME_NOW) mask |= NTFS_UPDATE_ATIME; else - if (to_set & FUSE_SET_ATTR_ATIME) + if (to_set & FUSE_SET_ATTR_ATIME) { +#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC + ni->last_access_time + = timespec2ntfs(stin->st_atimespec); +#elif defined(HAVE_STRUCT_STAT_ST_ATIM) ni->last_access_time = timespec2ntfs(stin->st_atim); +#else + ni->last_access_time.tv_sec + = stin->st_atime; +#ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC + ni->last_access_time.tv_nsec + = stin->st_atimensec; +#endif +#endif + } if (to_set & FUSE_SET_ATTR_MTIME_NOW) mask |= NTFS_UPDATE_MTIME; else - if (to_set & FUSE_SET_ATTR_MTIME) + if (to_set & FUSE_SET_ATTR_MTIME) { +#ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC + ni->last_data_change_time + = timespec2ntfs(stin->st_mtimespec); +#elif defined(HAVE_STRUCT_STAT_ST_ATIM) ni->last_data_change_time = timespec2ntfs(stin->st_mtim); +#else + ni->last_data_change_time.tv_sec + = stin->st_mtime; +#ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC + ni->last_data_change_time.tv_nsec + = stin->st_mtimensec; +#endif +#endif + } ntfs_inode_update_times(ni, mask); #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) } else From 02747ea647d300f9ce24801e508b720c0c9cc2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 26 Oct 2017 10:33:29 +0200 Subject: [PATCH 12/87] Aligned internal log buffers on CPUs which require them The generic buffer structure should respect the alignment of included log structures. This is required by some CPUs (such as Sparc). --- ntfsprogs/ntfsrecover.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ntfsprogs/ntfsrecover.h b/ntfsprogs/ntfsrecover.h index 0e8ba2bf..adf03e35 100644 --- a/ntfsprogs/ntfsrecover.h +++ b/ntfsprogs/ntfsrecover.h @@ -79,6 +79,7 @@ struct BUFFER { unsigned int headsz; BOOL safe; union { + u64 alignment; RESTART_PAGE_HEADER restart; RECORD_PAGE_HEADER record; char data[1]; From f06672a02cb334497abd69eadedb3379268c8129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 26 Oct 2017 10:40:17 +0200 Subject: [PATCH 13/87] Checked log file blocks more recent than temporary ones Under some circumstances, the temporary log file blocks are not the latest ones, so check whether there are more recent ones. Only done for log version 1.x, as log version 2.x follow a different logic. --- ntfsprogs/ntfsrecover.c | 62 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/ntfsrecover.c b/ntfsprogs/ntfsrecover.c index 4a06aa70..11a5e820 100644 --- a/ntfsprogs/ntfsrecover.c +++ b/ntfsprogs/ntfsrecover.c @@ -3686,6 +3686,60 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, return (state == T_ERR ? 1 : 0); } +/* + * Find the latest log block + * + * Usually, the latest block is either block 2 or 3 which act as + * temporary block before being copied to target location. + * However under some unknown condition the block are written + * immediately to target location, and we have to scan for the + * latest one. + * Currently this is not checked for logfile version 2.x which + * use a different layout of temporary blocks. + */ + +const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk, + const struct BUFFER *basebuf) +{ + le64 offset; + leLSN prevlsn; + leLSN curlsn; + u32 curblk; + u32 prevblk; + const struct BUFFER *prevbuf; + const struct BUFFER *curbuf; + + offset = basebuf->block.record.copy.file_offset; + curblk = baseblk; + do { + if (curblk < BASEBLKS) { + prevbuf = basebuf; + prevlsn = basebuf->block.record.last_end_lsn; + prevblk = baseblk; + curblk = le64_to_cpu(offset) >> blockbits; + } else { + if (optv) + printf("block %d is more recent than block %d\n", + (int)curblk, (int)prevblk); + prevbuf = curbuf; + prevlsn = curlsn; + prevblk = curblk; + curblk++; + if (curblk >= (logfilesz >> blockbits)) + curblk = (log_major < 2 ? BASEBLKS : BASEBLKS2); + } + curbuf = read_buffer(ctx, curblk); + if (curbuf && (curbuf->block.record.magic == magic_RCRD)) { + curlsn = curbuf->block.record.copy.last_lsn; + } + } while (curbuf + && (curbuf->block.record.magic == magic_RCRD) + && (le64_to_cpu(curlsn) > le64_to_cpu(prevlsn))); + if (optv) + printf("Block %d is the latest one\n",(int)prevblk); + return (prevbuf); +} + /* * Determine the sequencing of blocks (when version >= 2.0) * @@ -3982,6 +4036,12 @@ static int walk(CONTEXT *ctx) &nextbuf->block.record); } } + if (startbuf && opts) { + buf = startbuf = find_latest_block(ctx, + blk, startbuf); + latest_lsn = le64_to_cpu( + buf->block.record.last_end_lsn); + } } else { buf = startbuf = read_buffer(ctx, blk); nextbuf = (const struct BUFFER*)NULL; @@ -4043,7 +4103,7 @@ static void version(void) { printf("\n%s v%s (libntfs-3g) - Recover updates committed by Windows" " on an NTFS Volume.\n\n", "ntfsrecover", VERSION); - printf("Copyright (c) 2012-2016 Jean-Pierre Andre\n"); + printf("Copyright (c) 2012-2017 Jean-Pierre Andre\n"); printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } From 1f863fef7d47362ca1e4e337e1f4a99f5fec8ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 26 Oct 2017 10:44:36 +0200 Subject: [PATCH 14/87] Processed redo log actions associated to undoing a CompensationlogRecord At least when there is a shortage of space on the target device, several redo actions are associated to undoing a CompensationlogRecord, and they should be redone upon recovery. --- ntfsprogs/playlog.c | 96 ++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/ntfsprogs/playlog.c b/ntfsprogs/playlog.c index cb850389..d5fc90d4 100644 --- a/ntfsprogs/playlog.c +++ b/ntfsprogs/playlog.c @@ -2130,7 +2130,7 @@ static int redo_delete_file(ntfs_volume *vol, record = (MFT_RECORD*)buffer; if ((target + length) <= mftrecsz) { /* write a void mft entry (needed ?) */ - changed = memcmp(buffer + target, data, length) + changed = (length && memcmp(buffer + target, data, length)) || (record->flags & MFT_RECORD_IN_USE); err = 0; if (changed) { @@ -2170,7 +2170,6 @@ static int redo_delete_index(ntfs_volume *vol, data = ((const char*)&action->record) + get_undo_offset(&action->record); length = le16_to_cpu(action->record.undo_length); -// TODO merge with undo_add_index ? target = le16_to_cpu(action->record.record_offset) + le16_to_cpu(action->record.attribute_offset); if (optv > 1) { @@ -2189,7 +2188,9 @@ static int redo_delete_index(ntfs_volume *vol, && !(length & 7) && ((target + length) <= xsize)) { /* This has to be an idempotent action */ - found = !memcmp(buffer + target, data, length); + found = (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord)) + || !memcmp(buffer + target, data, length); err = 0; if (found) { /* Remove the entry */ @@ -2204,7 +2205,7 @@ static int redo_delete_index(ntfs_volume *vol, } if (optv > 1) { printf("-> INDX record %s\n", - (found ? "unchanged" : "removed")); + (found ? "removed" : "unchanged")); } } return (err); @@ -2251,7 +2252,9 @@ static int redo_delete_root_index(ntfs_volume *vol, && !(length & 7) && ((target + length) <= mftrecsz)) { /* This has to be an idempotent action */ - found = !memcmp(buffer + target, data, length); + found = (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord)) + || !memcmp(buffer + target, data, length); err = 0; /* Only delete if present */ if (found) { @@ -2591,7 +2594,9 @@ static int redo_update_resident(ntfs_volume *vol, dump(&buffer[target], length); } if ((target + length) <= mftrecsz) { - changed = memcmp(buffer + target, data, length); + changed = (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord)) + || memcmp(buffer + target, data, length); err = 0; if (changed) { memcpy(buffer + target, data, length); @@ -2640,8 +2645,13 @@ static int redo_update_root_index(ntfs_volume *vol, + le16_to_cpu(action->record.attribute_offset) + offsetof(INDEX_ENTRY, key.file_name.file_name_length) - length; - err = change_resident_expect(vol, action, buffer, data, expected, - target, length, AT_INDEX_ROOT); + if (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord)) + err = change_resident(vol, action, buffer, data, + target, length); + else + err = change_resident_expect(vol, action, buffer, data, + expected, target, length, AT_INDEX_ROOT); return (err); } @@ -4104,8 +4114,10 @@ static int distribute_redos(ntfs_volume *vol, err = redo_add_root_index(vol, action, buffer); break; case ClearBitsInNonResidentBitMap : - if (action->record.undo_operation - == const_cpu_to_le16(SetBitsInNonResidentBitMap)) + if ((action->record.undo_operation + == const_cpu_to_le16(SetBitsInNonResidentBitMap)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_force_bits(vol, action, buffer); break; case CompensationlogRecord : @@ -4114,28 +4126,38 @@ static int distribute_redos(ntfs_volume *vol, err = redo_compensate(vol, action, buffer); break; case CreateAttribute : - if (action->record.undo_operation - == const_cpu_to_le16(DeleteAttribute)) + if ((action->record.undo_operation + == const_cpu_to_le16(DeleteAttribute)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_create_attribute(vol, action, buffer); break; case DeallocateFileRecordSegment : - if (action->record.undo_operation + if ((action->record.undo_operation == const_cpu_to_le16(InitializeFileRecordSegment)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_delete_file(vol, action, buffer); break; case DeleteAttribute : - if (action->record.undo_operation - == const_cpu_to_le16(CreateAttribute)) + if ((action->record.undo_operation + == const_cpu_to_le16(CreateAttribute)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_delete_attribute(vol, action, buffer); break; case DeleteIndexEntryAllocation : - if (action->record.undo_operation - == const_cpu_to_le16(AddIndexEntryAllocation)) + if ((action->record.undo_operation + == const_cpu_to_le16(AddIndexEntryAllocation)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_delete_index(vol, action, buffer); break; case DeleteIndexEntryRoot : - if (action->record.undo_operation - == const_cpu_to_le16(AddIndexEntryRoot)) + if ((action->record.undo_operation + == const_cpu_to_le16(AddIndexEntryRoot)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_delete_root_index(vol, action, buffer); break; case InitializeFileRecordSegment : @@ -4154,8 +4176,10 @@ static int distribute_redos(ntfs_volume *vol, err = redo_force_bits(vol, action, buffer); break; case SetIndexEntryVcnAllocation : - if (action->record.undo_operation - == const_cpu_to_le16(SetIndexEntryVcnAllocation)) + if ((action->record.undo_operation + == const_cpu_to_le16(SetIndexEntryVcnAllocation)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_update_vcn(vol, action, buffer); break; case SetIndexEntryVcnRoot : @@ -4164,18 +4188,24 @@ static int distribute_redos(ntfs_volume *vol, err = redo_update_root_vcn(vol, action, buffer); break; case SetNewAttributeSizes : - if (action->record.undo_operation - == const_cpu_to_le16(SetNewAttributeSizes)) + if ((action->record.undo_operation + == const_cpu_to_le16(SetNewAttributeSizes)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_sizes(vol, action, buffer); break; case UpdateFileNameAllocation : - if (action->record.undo_operation - == const_cpu_to_le16(UpdateFileNameAllocation)) + if ((action->record.undo_operation + == const_cpu_to_le16(UpdateFileNameAllocation)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_update_index(vol, action, buffer); break; case UpdateFileNameRoot : - if (action->record.undo_operation - == const_cpu_to_le16(UpdateFileNameRoot)) + if ((action->record.undo_operation + == const_cpu_to_le16(UpdateFileNameRoot)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_update_root_index(vol, action, buffer); break; case UpdateMappingPairs : @@ -4197,8 +4227,10 @@ static int distribute_redos(ntfs_volume *vol, } break; case UpdateResidentValue : - if (action->record.undo_operation - == const_cpu_to_le16(UpdateResidentValue)) + if ((action->record.undo_operation + == const_cpu_to_le16(UpdateResidentValue)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_update_resident(vol, action, buffer); break; case Win10Action37 : @@ -4212,8 +4244,10 @@ static int distribute_redos(ntfs_volume *vol, err = redo_write_end(vol, action, buffer); break; case WriteEndOfIndexBuffer : - if (action->record.undo_operation - == const_cpu_to_le16(WriteEndOfIndexBuffer)) + if ((action->record.undo_operation + == const_cpu_to_le16(WriteEndOfIndexBuffer)) + || (action->record.undo_operation + == const_cpu_to_le16(CompensationlogRecord))) err = redo_write_index(vol, action, buffer); break; case AttributeNamesDump : From 9e92184b7c0caaf16f66f124550e006bfad3e41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 22 Dec 2017 11:24:58 +0100 Subject: [PATCH 15/87] Allowed setting a file object id without defining its birth ids Object ids can be used to locate files which have been move to another volume. This is only possible when the birth ids are recorded, but in most cases files reside on their birth volume and their birth ids are not set. The patch enables setting a file id without changing its birth id, by setting an extended attribute "system.ntfs_object_id" limited to 16 bytes. --- libntfs-3g/object_id.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/libntfs-3g/object_id.c b/libntfs-3g/object_id.c index bd72d026..b7cc77cc 100644 --- a/libntfs-3g/object_id.c +++ b/libntfs-3g/object_id.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2017 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -63,9 +63,13 @@ * significant byte first, and the six fields be compared individually * for ordering. RFC 4122 does not define the internal representation. * + * Windows apparently stores the first three fields in little endian + * order, and the last two fields in big endian order. + * * Here we always copy disk images with no endianness change, * and, for indexing, GUIDs are compared as if they were a sequence - * of four unsigned 32 bit integers. + * of four little-endian unsigned 32 bit integers (as Windows + * does it that way.) * * --------------------- begin from RFC 4122 ---------------------- * Consider each field of the UUID to be an unsigned integer as shown @@ -349,10 +353,12 @@ static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, res = -1; } } - /* write index part if provided */ + /* overwrite index data with new value */ + memcpy(&old_attr, value, + (size < sizeof(OBJECT_ID_ATTR) + ? size : sizeof(OBJECT_ID_ATTR))); if (!res - && ((size < sizeof(OBJECT_ID_ATTR)) - || set_object_id_index(ni,xo,value))) { + && set_object_id_index(ni,xo,&old_attr)) { /* * If cannot index, try to remove the object * id and log the error. There will be an @@ -500,9 +506,11 @@ int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) /* * Set the object id from an extended attribute * - * If the size is 64, the attribute and index are set. - * else if the size is not less than 16 only the attribute is set. - * The object id index is set accordingly. + * The first 16 bytes are the new object id, they can be followed + * by the birth volume id, the birth object id and the domain id. + * If they are not present, their previous value is kept. + * Only the object id is stored into the attribute, all the fields + * are stored into the index. * * Returns 0, or -1 if there is a problem */ @@ -519,10 +527,12 @@ int ntfs_set_ntfs_object_id(ntfs_inode *ni, if (ni && value && (size >= sizeof(GUID))) { xo = open_object_id_index(ni->vol); if (xo) { - /* make sure the GUID was not used somewhere */ + /* make sure the GUID was not used elsewhere */ memcpy(&key.object_id, value, sizeof(GUID)); - if (ntfs_index_lookup(&key, - sizeof(OBJECT_ID_INDEX_KEY), xo)) { + if ((ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) + || (MREF_LE(((struct OBJECT_ID_INDEX*)xo->entry) + ->data.file_id) == ni->mft_no)) { ntfs_index_ctx_reinit(xo); res = add_object_id(ni, flags); if (!res) { From d99a376a90ed94a75a68915de775b9f9a9b8353c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 22 Dec 2017 11:26:08 +0100 Subject: [PATCH 16/87] Documented read-only mount when Windows is hibernated Document an earlier update which forced read-only mode when mounting a partition which has been left by Windows in an inconsistent state through hibernation or fast restarting. --- src/ntfs-3g.8.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index a3fb0dad..2f2e8ca2 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -52,6 +52,8 @@ hibernation and fast restarting : powercfg /h off .sp .RE +If either Windows is hibernated or its fast restart is enabled, partitions +on internal disks are forced to be mounted in read-only mode. .SS Access Handling and Security By default, files and directories are owned by the effective user and group of the mounting process, and everybody has From 85e208176ffd16ce9a51a9f534b194f0ebbde6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 22 Dec 2017 11:29:00 +0100 Subject: [PATCH 17/87] Stopped checking matches of MFTMirr against MFT at record 16 Since its 2017 edition, Windows 10 has stopped mirroring $MFT to the full size of $MFTMirr leading to mounts of partitions with big clusters to be rejected because of mismatches. With this patch, only 16 records are checked, though mirroring is still done for all records in $MFTMirr. --- libntfs-3g/volume.c | 3 ++- ntfsprogs/ntfsfix.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index 68b8ee1d..d36c7d55 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -959,7 +959,8 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) vol->mftmirr_size = l; } ntfs_log_debug("Comparing $MFTMirr to $MFT...\n"); - for (i = 0; i < vol->mftmirr_size; ++i) { + /* Windows 10 does not update the full $MFTMirr any more */ + for (i = 0; (i < vol->mftmirr_size) && (i < FILE_first_user); ++i) { MFT_RECORD *mrec, *mrec2; const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", "$Volume", "$AttrDef", "root directory", "$Bitmap", diff --git a/ntfsprogs/ntfsfix.c b/ntfsprogs/ntfsfix.c index 2a624023..6801e70e 100644 --- a/ntfsprogs/ntfsfix.c +++ b/ntfsprogs/ntfsfix.c @@ -506,6 +506,11 @@ static int fix_mftmirr(ntfs_volume *vol) ntfs_log_info("Comparing $MFTMirr to $MFT... "); done = FALSE; + /* + * Since 2017, Windows 10 does not mirror to full $MFTMirr when + * using big clusters, and some records may be found different. + * Nevertheless chkdsk.exe mirrors it fully, so we do similarly. + */ for (i = 0; i < vol->mftmirr_size; ++i) { MFT_RECORD *mrec, *mrec2; const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", From 52399860938de8af888ca8cd798ef46ccf98bcd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 22 Dec 2017 11:37:58 +0100 Subject: [PATCH 18/87] Silenced compiler warnings in ntfsrecover Silence the compiler warnings which were introduced in a recent update. --- ntfsprogs/ntfsrecover.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/ntfsrecover.c b/ntfsprogs/ntfsrecover.c index 11a5e820..19366491 100644 --- a/ntfsprogs/ntfsrecover.c +++ b/ntfsprogs/ntfsrecover.c @@ -3698,7 +3698,7 @@ static int walkback(CONTEXT *ctx, const struct BUFFER *buf, u32 blk, * use a different layout of temporary blocks. */ -const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk, +static const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk, const struct BUFFER *basebuf) { le64 offset; @@ -3710,6 +3710,9 @@ const struct BUFFER *find_latest_block(CONTEXT *ctx, u32 baseblk, const struct BUFFER *curbuf; offset = basebuf->block.record.copy.file_offset; + curbuf = (const struct BUFFER*)NULL; + curlsn = const_cpu_to_le64(0); + prevblk = 0; curblk = baseblk; do { if (curblk < BASEBLKS) { From 3243e62396046019a93f7b20f3e03b4e2fe1ef58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Mon, 15 Jan 2018 11:04:29 +0100 Subject: [PATCH 19/87] Supported reparse tags for OneDrive cloud storage Newer versions of Windows 10 use several reparse tags for files which are synchronized to OneDrive cloud storage (0x9000301a, 0x9000601a, 0x9000701a, ...). identify them as IO_REPARSE_TAG_CLOUD and use a single plugin to process them. --- include/ntfs-3g/layout.h | 7 +++++++ ntfsprogs/ntfsinfo.c | 13 ++++++++++--- src/ntfs-3g_common.c | 15 +++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 1df07b98..fd6ad2c6 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -2412,8 +2412,13 @@ typedef struct { * be slow. (E.g. the data is stored on a tape drive.) * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User * defined tags have to use zero here. + * 4. However, on Windows 10 : + * Some flags may be used in bits 12 to 15 to further describe the + * reparse point, and bit 28 may be set, probably to signal the + * presence of these flags. */ typedef enum { + IO_REPARSE_TAG_WITH_FLAGS = const_cpu_to_le32(0x10000000), IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), @@ -2436,10 +2441,12 @@ typedef enum { IO_REPARSE_TAG_DFM = const_cpu_to_le32(0x80000016), IO_REPARSE_TAG_WOF = const_cpu_to_le32(0x80000017), IO_REPARSE_TAG_WCI = const_cpu_to_le32(0x80000018), + IO_REPARSE_TAG_CLOUD = const_cpu_to_le32(0x9000001A), IO_REPARSE_TAG_GVFS = const_cpu_to_le32(0x9000001C), IO_REPARSE_TAG_LX_SYMLINK = const_cpu_to_le32(0xA000001D), IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff), + IO_REPARSE_PLUGIN_SELECT = const_cpu_to_le32(0xffff0fff), } PREDEFINED_REPARSE_TAGS; /** diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 94660b95..0eb7bae9 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -8,7 +8,7 @@ * Copyright (c) 2004-2005 Yuval Fledel * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2005 Cristian Klein - * Copyright (c) 2011-2017 Jean-Pierre Andre + * Copyright (c) 2011-2018 Jean-Pierre Andre * * This utility will dump a file's attributes. * @@ -119,7 +119,7 @@ static void version(void) printf(" 2003 Leonard NorrgÄrd\n"); printf(" 2004-2005 Yuval Fledel\n"); printf(" 2004-2007 Yura Pakhuchiy\n"); - printf(" 2011-2017 Jean-Pierre Andre\n"); + printf(" 2011-2018 Jean-Pierre Andre\n"); printf("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -411,8 +411,12 @@ static char *ntfs_attr_get_name_mbs(ATTR_RECORD *attr) static const char *reparse_type_name(le32 tag) { const char *name; + le32 seltag; - switch (tag) { + seltag = tag; + if (tag & IO_REPARSE_TAG_WITH_FLAGS) + seltag &= IO_REPARSE_PLUGIN_SELECT; + switch (seltag) { case IO_REPARSE_TAG_MOUNT_POINT : name = " (mount point)"; break; @@ -428,6 +432,9 @@ static const char *reparse_type_name(le32 tag) case IO_REPARSE_TAG_WCI : name = " (Windows container)"; break; + case IO_REPARSE_TAG_CLOUD : + name = " (Cloud)"; + break; case IO_REPARSE_TAG_NFS : name = " (NFS symlink)"; break; diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 8fd7ee11..7f3247df 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -1,7 +1,7 @@ /** * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g. * - * Copyright (c) 2010-2016 Jean-Pierre Andre + * Copyright (c) 2010-2018 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -801,7 +801,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, const struct plugin_operations *ops; void *handle; REPARSE_POINT *reparse; - le32 tag; + le32 tag, seltag; plugin_list_t *plugin; plugin_init_t pinit; @@ -809,7 +809,10 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, reparse = ntfs_get_reparse_point(ni); if (reparse) { tag = reparse->reparse_tag; - for (plugin=ctx->plugins; plugin && (plugin->tag != tag); + seltag = tag; + if (tag & IO_REPARSE_TAG_WITH_FLAGS) + seltag &= IO_REPARSE_PLUGIN_SELECT; + for (plugin=ctx->plugins; plugin && (plugin->tag != seltag); plugin = plugin->next) { } if (plugin) { ops = plugin->ops; @@ -819,12 +822,12 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, snprintf(name,sizeof(name), PLUGIN_DIR "/ntfs-plugin-%08lx.so", - (long)le32_to_cpu(tag)); + (long)le32_to_cpu(seltag)); #else char name[64]; snprintf(name,sizeof(name), "ntfs-plugin-%08lx.so", - (long)le32_to_cpu(tag)); + (long)le32_to_cpu(seltag)); #endif handle = dlopen(name, RTLD_LAZY); if (handle) { @@ -833,7 +836,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, /* pinit() should set errno if it fails */ ops = (*pinit)(tag); if (ops && register_reparse_plugin(ctx, - tag, ops, handle)) + seltag, ops, handle)) ops = (struct plugin_operations*)NULL; } else errno = ELIBBAD; From b30460feded98173765b36127376b3c2e1d3a967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 13 Feb 2018 08:43:53 +0100 Subject: [PATCH 20/87] Removed an obsolete reference for getting support Remove from the ntfsresize man page an archaic url for getting support. --- ntfsprogs/ntfsresize.8.in | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/ntfsprogs/ntfsresize.8.in b/ntfsprogs/ntfsresize.8.in index 2ee53460..4c21ed2c 100644 --- a/ntfsprogs/ntfsresize.8.in +++ b/ntfsprogs/ntfsresize.8.in @@ -243,10 +243,9 @@ Display help and exit. .SH EXIT CODES The exit code is 0 on success, non\-zero otherwise. .SH KNOWN ISSUES -No reliability problem is known. If you need -help please try the Ntfsresize FAQ first (see below) and if you -don't find your answer then send your question, comment or bug report to -the development team: +No reliability problem is known. +If you find a bug please send an email describing the problem to the +development team at: .br .nh ntfs\-3g\-devel@lists.sf.net @@ -308,14 +307,6 @@ package and is available from: .nh http://www.tuxera.com/community/ .hy -.sp -.B Ntfsresize -related news, example of usage, troubleshooting, statically linked binary and -FAQ (frequently asked questions) are maintained at: -.br -.nh -http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize.html -.hy .SH SEE ALSO .BR fdisk (8), .BR cfdisk (8), From 163635f08fcad8358a2ba4a58e749c1f0cee12cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 13 Feb 2018 08:47:00 +0100 Subject: [PATCH 21/87] Fixed displaying an inode number in an error message In an error message, the inode number was displayed in decimal without taking off its generation number, making it difficult to interpret. --- libntfs-3g/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 4c364b9a..01430210 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -837,7 +837,7 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", - (long long)le64_to_cpu(fn->parent_directory)); + (long long)MREF_LE(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); From f334c1fdc397d92d2bb4aa9cf75b75dbb33eff40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 13 Feb 2018 08:54:12 +0100 Subject: [PATCH 22/87] Delayed updating the MFT runlist when resizing in read-only mode When trying a resize in "no action" (read-only) mode, and the MFT runlist has to be reorganized to take its new fragmentation into account, the updated runlist cannot be read from the device while updating the runlist of normal files. To avoid having to read the updated runlist, the update is delayed so that the original runlist is used. As a consequence the test of reorganizing the runlists is only an approximation of what would happen in a real resize. --- ntfsprogs/ntfsresize.c | 70 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index 00a80983..9189dc72 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Anton Altaparmakov * Copyright (c) 2002-2003 Richard Russon * Copyright (c) 2007 Yura Pakhuchiy - * Copyright (c) 2011-2016 Jean-Pierre Andre + * Copyright (c) 2011-2018 Jean-Pierre Andre * * This utility will resize an NTFS volume without data loss. * @@ -137,6 +137,8 @@ static const char *many_bad_sectors_msg = "* other reason. We suggest to get a replacement disk as soon as possible. *\n" "***************************************************************************\n"; +enum mirror_source { MIRR_OLD, MIRR_NEWMFT, MIRR_MFT }; + static struct { int verbose; int debug; @@ -226,6 +228,7 @@ typedef struct { struct llcn_t last_compressed; struct llcn_t last_lcn; s64 last_unsupp; /* last unsupported cluster */ + enum mirror_source mirr_from; } ntfs_resize_t; /* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster @@ -1459,10 +1462,13 @@ static int record_mft_in_bitmap(ntfs_resize_t *resize) static void delayed_updates(ntfs_resize_t *resize) { struct DELAYED *delayed; + struct DELAYED *delayed_mft_data; + int nr_extents; if (ntfs_volume_get_free_space(resize->vol)) err_exit("Failed to determine free space\n"); + delayed_mft_data = (struct DELAYED*)NULL; if (resize->delayed_runlists && reload_mft(resize)) err_exit("Failed to reload the MFT for delayed updates\n"); @@ -1476,19 +1482,55 @@ static void delayed_updates(ntfs_resize_t *resize) * So we update MFT data first, and we record the MFT * extents again in the MFT bitmap if they were recorded * in the old location. + * + * However, if we are operating in "no action" mode, the + * MFT records to update are not written to their new location + * and the MFT data runlist has to be updated last in order + * to have the entries read from their old location. + * In this situation the MFT bitmap is never written to + * disk, so the same extents are reallocated repeatedly, + * which is not what would be done in a real resizing. */ + if (opt.ro_flag + && resize->delayed_runlists + && (resize->delayed_runlists->mref == FILE_MFT) + && (resize->delayed_runlists->type == AT_DATA)) { + /* Update the MFT data runlist later */ + delayed_mft_data = resize->delayed_runlists; + resize->delayed_runlists = resize->delayed_runlists->next; + } + while (resize->delayed_runlists) { delayed = resize->delayed_runlists; expand_attribute_runlist(resize->vol, delayed); - if ((delayed->mref == FILE_MFT) && (delayed->type == AT_BITMAP)) - record_mft_in_bitmap(resize); + if (delayed->mref == FILE_MFT) { + if (delayed->type == AT_BITMAP) + record_mft_in_bitmap(resize); + if (delayed->type == AT_DATA) + resize->mirr_from = MIRR_MFT; + } resize->delayed_runlists = resize->delayed_runlists->next; if (delayed->attr_name) free(delayed->attr_name); free(delayed->head_rl); free(delayed); } + if (opt.ro_flag && delayed_mft_data) { + /* in "no action" mode, check updating the MFT runlist now */ + expand_attribute_runlist(resize->vol, delayed_mft_data); + resize->mirr_from = MIRR_MFT; + if (delayed_mft_data->attr_name) + free(delayed_mft_data->attr_name); + free(delayed_mft_data->head_rl); + free(delayed_mft_data); + } + /* Beware of MFT fragmentation when the target size is too small */ + nr_extents = resize->vol->mft_ni->nr_extents; + if (nr_extents > 2) { + printf("WARNING: The MFT is now severely fragmented" + " (%d extents)\n", nr_extents); + } } /* @@ -2262,6 +2304,7 @@ static void relocate_inodes(ntfs_resize_t *resize) err_exit("Could not allocate 16 records in" " the first MFT chunk\n"); } + resize->mirr_from = MIRR_NEWMFT; } for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++) @@ -2718,16 +2761,27 @@ static void update_bootsector(ntfs_resize_t *r) bs->number_of_sectors = cpu_to_sle64(r->new_volume_size * bs->bpb.sectors_per_cluster); - if (r->mftmir_old) { + if (r->mftmir_old || (r->mirr_from == MIRR_MFT)) { r->progress.flags |= NTFS_PROGBAR_SUPPRESS; /* Be sure the MFTMirr holds the updated MFT runlist */ - if (r->new_mft_start) + switch (r->mirr_from) { + case MIRR_MFT : + /* The late updates of MFT have not been synced */ + ntfs_inode_sync(vol->mft_ni); + copy_clusters(r, r->mftmir_rl.lcn, + vol->mft_na->rl->lcn, r->mftmir_rl.length); + break; + case MIRR_NEWMFT : copy_clusters(r, r->mftmir_rl.lcn, r->new_mft_start->lcn, r->mftmir_rl.length); - else + break; + default : copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old, r->mftmir_rl.length); - bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn); + break; + } + if (r->mftmir_old) + bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn); r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS; } /* Set the start of the relocated MFT */ @@ -3904,6 +3958,7 @@ static int update_runlist(expand_t *expand, s64 inum, ctx.mrec = mrec; resize.mref = inum; resize.delayed_runlists = expand->delayed_runlists; + resize.mirr_from = MIRR_OLD; must_delay = 1; replace_later(&resize,rl,head_rl); expand->delayed_runlists = resize.delayed_runlists; @@ -4577,6 +4632,7 @@ int main(int argc, char **argv) resize.inuse = fsck.inuse; resize.lcn_bitmap = fsck.lcn_bitmap; + resize.mirr_from = MIRR_OLD; set_resize_constraints(&resize); set_disk_usage_constraint(&resize); From 1f8b751341944545eceb6037176f288843eb7b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 13 Feb 2018 09:06:49 +0100 Subject: [PATCH 23/87] Double-checked whether record 15 is an extent of MFT When extents are needed to store the runlist of the MFT, the first one must be located in record 15 so that its location can be determined from the part in the base extent. As this record is always marked in use, determining whether it is not really in use requires a specific logic. --- libntfs-3g/mft.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 29f1f4bc..a80d1e4e 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2014-2015 Jean-Pierre Andre + * Copyright (c) 2014-2018 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -1389,16 +1389,27 @@ ntfs_inode *ntfs_mft_rec_alloc(ntfs_volume *vol, BOOL mft_data) */ if (ext_ni) { /* - * Make sure record 15 is a base extent and has - * no extents. - * Also make sure it has no name : a base inode with - * no extents and no name cannot be in use. - * Otherwise apply standard procedure. + * Make sure record 15 is a base extent and it has + * no name. A base inode with no name cannot be in use. + * The test based on base_mft_record fails for + * extents of MFT, so we need a special check. + * If already used, apply standard procedure. */ if (!ext_ni->mrec->base_mft_record - && !ext_ni->nr_extents) + && !ext_ni->mrec->link_count) forced_mft_data = TRUE; ntfs_inode_close(ext_ni); + /* Double-check, in case it is used for MFT */ + if (forced_mft_data && base_ni->nr_extents) { + int i; + + for (i=0; inr_extents; i++) { + if (base_ni->extent_nis[i] + && (base_ni->extent_nis[i]->mft_no + == FILE_mft_data)) + forced_mft_data = FALSE; + } + } } } if (forced_mft_data) From ad79372024fb7e2a94ea2db515d534143b53ae80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 1 Jun 2018 15:59:09 +0200 Subject: [PATCH 24/87] Checked whether the device to mount was forced read-only Force the read-only mount mode when the device was set so through a command "blockdev --setro". --- libntfs-3g/unix_io.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libntfs-3g/unix_io.c b/libntfs-3g/unix_io.c index 64b41d3e..c88e8f82 100644 --- a/libntfs-3g/unix_io.c +++ b/libntfs-3g/unix_io.c @@ -53,6 +53,9 @@ #ifdef HAVE_LINUX_FD_H #include #endif +#ifdef HAVE_LINUX_FS_H +#include +#endif #include "types.h" #include "mst.h" @@ -142,6 +145,21 @@ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) err = errno; goto err_out; } +#ifdef HAVE_LINUX_FS_H + /* Check whether the device was forced read-only */ + if (NDevBlock(dev) && ((flags & O_RDWR) == O_RDWR)) { + int r; + int state; + + r = ioctl(DEV_FD(dev), BLKROGET, &state); + if (!r && state) { + err = EROFS; + if (close(DEV_FD(dev))) + err = errno; + goto err_out; + } + } +#endif if ((flags & O_RDWR) != O_RDWR) NDevSetReadOnly(dev); From 4f450a35f55c7985122ad5f1505afda4aacae5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 1 Jun 2018 16:08:33 +0200 Subject: [PATCH 25/87] Made accessing reparse directories through internal plugins When the bit 28 of a reparse tag is set on a directory, the reparse information should be ignored and the directory should be accessed the usual way (this setting is new to Windows 10). In such a situation access to the directory through an internal plugin rather than through an external one. The same policy applies to REPARSE_TAG_WCI which had been defined earlier without the bit 28 being set. --- include/ntfs-3g/layout.h | 13 ++-- ntfsprogs/ntfsinfo.c | 4 +- src/lowntfs-3g.c | 1 + src/ntfs-3g.c | 1 + src/ntfs-3g_common.c | 145 ++++++++++++++++++++++++++++++++++++--- src/ntfs-3g_common.h | 2 +- 6 files changed, 146 insertions(+), 20 deletions(-) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index fd6ad2c6..754d66e9 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -2403,22 +2403,23 @@ typedef struct { * * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of * the reparse point. - * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use. - * 3. The most significant three bits are flags describing the reparse point. + * 2. The 12 bits after this (i.e. bits 16 to 27) are reserved for future use. + * 3. The most significant four bits are flags describing the reparse point. * They are defined as follows: + * bit 28: Directory bit. If set, the directory is not a surrogate + * and can be used the usual way. * bit 29: Name surrogate bit. If set, the filename is an alias for * another object in the system. * bit 30: High-latency bit. If set, accessing the first byte of data will * be slow. (E.g. the data is stored on a tape drive.) * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User * defined tags have to use zero here. - * 4. However, on Windows 10 : + * 4. Moreover, on Windows 10 : * Some flags may be used in bits 12 to 15 to further describe the - * reparse point, and bit 28 may be set, probably to signal the - * presence of these flags. + * reparse point. */ typedef enum { - IO_REPARSE_TAG_WITH_FLAGS = const_cpu_to_le32(0x10000000), + IO_REPARSE_TAG_DIRECTORY = const_cpu_to_le32(0x10000000), IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000), IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000), IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000), diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 0eb7bae9..50fbf431 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -413,9 +413,7 @@ static const char *reparse_type_name(le32 tag) const char *name; le32 seltag; - seltag = tag; - if (tag & IO_REPARSE_TAG_WITH_FLAGS) - seltag &= IO_REPARSE_PLUGIN_SELECT; + seltag = tag & IO_REPARSE_PLUGIN_SELECT; switch (seltag) { case IO_REPARSE_TAG_MOUNT_POINT : name = " (mount point)"; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 71eceeb4..993867fa 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4568,6 +4568,7 @@ int main(int argc, char *argv[]) #ifndef DISABLE_PLUGINS register_internal_reparse_plugins(); + register_directory_plugins(ctx); #endif /* DISABLE_PLUGINS */ se = mount_fuse(parsed_options); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index b224e188..6ce89fef 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4304,6 +4304,7 @@ int main(int argc, char *argv[]) #ifndef DISABLE_PLUGINS register_internal_reparse_plugins(); + register_directory_plugins(ctx); #endif /* DISABLE_PLUGINS */ fh = mount_fuse(parsed_options); diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 7f3247df..64b0f52b 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -36,6 +36,10 @@ #include #endif +#ifdef HAVE_FCNTL_H +#include +#endif + #ifdef HAVE_LIMITS_H #include #endif @@ -766,6 +770,108 @@ exit : #ifndef DISABLE_PLUGINS +/* + * Get attribute information for reparse directories + * + * Reparse directories have a reparse tag which should be ignored. + */ + +static int directory_getattr(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct stat *stbuf) +{ + static ntfschar I30[] = + { const_cpu_to_le16('$'), const_cpu_to_le16('I'), + const_cpu_to_le16('3'), const_cpu_to_le16('0') }; + ntfs_attr *na; + int res; + + res = -EOPNOTSUPP; + if (ni && reparse && stbuf + && ((reparse->reparse_tag == IO_REPARSE_TAG_WCI) + || ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY) + && !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS))) + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + /* Directory */ + stbuf->st_mode = S_IFDIR | 0555; + /* get index size, if not known */ + if (!test_nino_flag(ni, KnownSize)) { + na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, I30, 4); + if (na) { + ni->data_size = na->data_size; + ni->allocated_size = na->allocated_size; + set_nino_flag(ni, KnownSize); + ntfs_attr_close(na); + } + } + stbuf->st_size = ni->data_size; + stbuf->st_blocks = ni->allocated_size >> 9; + stbuf->st_nlink = 1; /* Make find(1) work */ + res = 0; + } + /* Not a directory, or another error occurred */ + return (res); +} + +/* + * Open a reparse directory for reading + * + * Currently no reading context is created. + */ + +static int directory_opendir(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct fuse_file_info *fi) +{ + int res; + + res = -EOPNOTSUPP; + if (ni && reparse && fi + && ((reparse->reparse_tag == IO_REPARSE_TAG_WCI) + || ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY) + && !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS))) + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + && ((fi->flags & O_ACCMODE) == O_RDONLY)) + res = 0; + return (res); +} + +/* + * Release a reparse directory + * + * Should never be called, as no reading context was defined. + */ + +static int directory_release(ntfs_inode *ni __attribute__((unused)), + const REPARSE_POINT *reparse __attribute__((unused)), + struct fuse_file_info *fi __attribute__((unused))) +{ + return 0; +} + +/* + * Read an open reparse directory + * + * Returns 0 or a negative error code + */ + +static int directory_readdir(ntfs_inode *ni, const REPARSE_POINT *reparse, + s64 *pos, void *fillctx, ntfs_filldir_t filldir, + struct fuse_file_info *fi __attribute__((unused))) +{ + int res; + + res = -EOPNOTSUPP; + if (ni && reparse && pos && fillctx && filldir + && ((reparse->reparse_tag == IO_REPARSE_TAG_WCI) + || ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY) + && !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS))) + && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { + res = 0; + if (ntfs_readdir(ni, pos, fillctx, filldir)) + res = -errno; + } + return (res); +} + int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag, const plugin_operations_t *ops, void *handle) { @@ -773,14 +879,16 @@ int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag, int res; res = -1; - plugin = (plugin_list_t*)ntfs_malloc(sizeof(plugin_list_t)); - if (plugin) { - plugin->tag = tag; - plugin->ops = ops; - plugin->handle = handle; - plugin->next = ctx->plugins; - ctx->plugins = plugin; - res = 0; + if (ctx) { + plugin = (plugin_list_t*)ntfs_malloc(sizeof(plugin_list_t)); + if (plugin) { + plugin->tag = tag; + plugin->ops = ops; + plugin->handle = handle; + plugin->next = ctx->plugins; + ctx->plugins = plugin; + res = 0; + } } return (res); } @@ -810,8 +918,8 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, if (reparse) { tag = reparse->reparse_tag; seltag = tag; - if (tag & IO_REPARSE_TAG_WITH_FLAGS) - seltag &= IO_REPARSE_PLUGIN_SELECT; + if (tag & IO_REPARSE_TAG_DIRECTORY) + seltag &= IO_REPARSE_TAG_DIRECTORY; for (plugin=ctx->plugins; plugin && (plugin->tag != seltag); plugin = plugin->next) { } if (plugin) { @@ -873,6 +981,23 @@ void close_reparse_plugins(ntfs_fuse_context_t *ctx) } } +void register_directory_plugins(ntfs_fuse_context_t *ctx) +{ + static const struct plugin_operations ops = { + .getattr = directory_getattr, + .release = directory_release, + .opendir = directory_opendir, + .readdir = directory_readdir, + } ; + + if (ctx) { + register_reparse_plugin(ctx, IO_REPARSE_TAG_WCI, + &ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_DIRECTORY, + &ops, (void*)NULL); + } +} + #endif /* DISABLE_PLUGINS */ #ifdef HAVE_SETXATTR diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index bcffe4f7..33cced36 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -211,7 +211,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, ntfs_inode *ni, REPARSE_POINT **reparse); int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag, const plugin_operations_t *ops, void *handle); - +void register_directory_plugins(ntfs_fuse_context_t *ctx); #endif /* DISABLE_PLUGINS */ #endif /* _NTFS_3G_COMMON_H */ From a951ed7f61ff7505315a2c5fc9a2734e751a0d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 1 Jun 2018 16:18:51 +0200 Subject: [PATCH 26/87] Added an option to ntfscp to copy the modification time When using option -t the modification timestamp is copied by ntfscp --- ntfsprogs/ntfscp.8.in | 4 ++++ ntfsprogs/ntfscp.c | 26 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ntfsprogs/ntfscp.8.in b/ntfsprogs/ntfscp.8.in index bf5127ba..67973331 100644 --- a/ntfsprogs/ntfscp.8.in +++ b/ntfsprogs/ntfscp.8.in @@ -59,6 +59,10 @@ Show a list of options with a brief description of each one. \fB\-q\fR, \fB\-\-quiet\fR Suppress some debug/warning/error messages. .TP +\fB\-t\fR, \fB\-\-timestamp\fR +Copy the modification time of source_file to destination. This is +not compatible with \fB\-\-attr\-name\fR and \fB\-\-attribute\fR. +.TP \fB\-V\fR, \fB\-\-version\fR Show the version number, copyright and license .BR ntfscp . diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index 24381b46..726e6471 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -58,6 +58,7 @@ #include "debug.h" /* #include "version.h" */ #include "logging.h" +#include "ntfstime.h" #include "misc.h" struct options { @@ -69,6 +70,7 @@ struct options { int quiet; /* Less output */ int verbose; /* Extra output */ int minfragments; /* Do minimal fragmentation */ + int timestamp; /* Copy the modification time */ int noaction; /* Do not write to disk */ ATTR_TYPES attribute; /* Write to this attribute. */ int inode; /* Treat dest_file as inode number. */ @@ -129,6 +131,7 @@ static void usage(void) " -N, --attr-name NAME Write to attribute with this name\n" " -n, --no-action Do not write to disk\n" " -q, --quiet Less output\n" + " -t, --timestamp Copy the modification time\n" " -V, --version Version information\n" " -v, --verbose More output\n\n", EXEC_NAME); @@ -146,7 +149,7 @@ static void usage(void) */ static int parse_options(int argc, char **argv) { - static const char *sopt = "-a:ifh?mN:no:qVv"; + static const char *sopt = "-a:ifh?mN:no:qtVv"; static const struct option lopt[] = { { "attribute", required_argument, NULL, 'a' }, { "inode", no_argument, NULL, 'i' }, @@ -156,6 +159,7 @@ static int parse_options(int argc, char **argv) { "attr-name", required_argument, NULL, 'N' }, { "no-action", no_argument, NULL, 'n' }, { "quiet", no_argument, NULL, 'q' }, + { "timestamp", no_argument, NULL, 't' }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } @@ -175,6 +179,7 @@ static int parse_options(int argc, char **argv) opts.attr_name = NULL; opts.inode = 0; opts.attribute = AT_DATA; + opts.timestamp = 0; opterr = 0; /* We'll handle the errors, thank you. */ @@ -235,6 +240,9 @@ static int parse_options(int argc, char **argv) opts.quiet++; ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); break; + case 't': + opts.timestamp++; + break; case 'V': ver++; break; @@ -284,6 +292,12 @@ static int parse_options(int argc, char **argv) "at the same time.\n"); err++; } + if (opts.timestamp + && (opts.attr_name || (opts.attribute != AT_DATA))) { + ntfs_log_error("Setting --timestamp is only possible" + " with unname data attribute.\n"); + err++; + } } if (ver) @@ -822,6 +836,7 @@ static ntfs_inode *ntfs_new_file(ntfs_inode *dir_ni, int main(int argc, char *argv[]) { FILE *in; + struct stat st; ntfs_volume *vol; ntfs_inode *out; ntfs_attr *na; @@ -1136,6 +1151,15 @@ int main(int argc, char *argv[]) free(buf); close_attr: ntfs_attr_close(na); + if (opts.timestamp) { + if (!fstat(fileno(in),&st)) { + out->last_data_change_time = st.st_mtime*10000000LL + + NTFS_TIME_OFFSET; + ntfs_inode_update_times(out, 0); + } else { + ntfs_log_error("Failed to get the time stamp.\n"); + } + } close_dst: while (ntfs_inode_close(out) && !opts.noaction) { if (errno != EBUSY) { From e758709a2c49c1daf11dc9d2e8ec0617727d7881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 1 Jun 2018 16:21:33 +0200 Subject: [PATCH 27/87] Appended a number to undeleted file name to avoid overwriting an existing one When an undeleted file name conflicts with an existing one, it is renamed in order to preserve the existing one. --- ntfsprogs/ntfsundelete.c | 55 ++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c index d479ec15..746eac7b 100644 --- a/ntfsprogs/ntfsundelete.c +++ b/ntfsprogs/ntfsundelete.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Holger Ohmacht * Copyright (c) 2005 Anton Altaparmakov * Copyright (c) 2007 Yura Pakhuchiy - * Copyright (c) 2013-2014 Jean-Pierre Andre + * Copyright (c) 2013-2018 Jean-Pierre Andre * * This utility will recover deleted files from an NTFS volume. * @@ -392,7 +392,7 @@ static void version(void) "Copyright (c) 2004-2005 Holger Ohmacht\n" "Copyright (c) 2005 Anton Altaparmakov\n" "Copyright (c) 2007 Yura Pakhuchiy\n" - "Copyright (c) 2013-2014 Jean-Pierre Andre\n"); + "Copyright (c) 2013-2018 Jean-Pierre Andre\n"); ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -1835,19 +1835,49 @@ static unsigned int write_data(int fd, const char *buffer, static int create_pathname(const char *dir, const char *name, const char *stream, char *buffer, int bufsize) { + struct stat st; + int s; + int len; + int suffix; + if (!name) name = UNKNOWN; - if (dir) + if (dir) { +#ifdef HAVE_WINDOWS_H if (stream) - snprintf(buffer, bufsize, "%s/%s:%s", dir, name, stream); + snprintf(buffer, bufsize, "%s\\%s:%s", dir, name, + stream); + else + snprintf(buffer, bufsize, "%s\\%s", dir, name); +#else + if (stream) + snprintf(buffer, bufsize, "%s/%s:%s", dir, name, + stream); else snprintf(buffer, bufsize, "%s/%s", dir, name); - else +#endif + } else if (stream) snprintf(buffer, bufsize, "%s:%s", name, stream); else snprintf(buffer, bufsize, "%s", name); + len = strlen(buffer); + suffix = 0; +#ifdef HAVE_WINDOWS_H + s = stat(buffer, &st); +#else + s = lstat(buffer, &st); +#endif + while (!s && (suffix < 999)) { + suffix++; + snprintf(&buffer[len], bufsize - len, ".%d", suffix); +#ifdef HAVE_WINDOWS_H + s = stat(buffer, &st); +#else + s = lstat(buffer, &st); +#endif + } return strlen(buffer); } @@ -2012,7 +2042,8 @@ static int undelete_file(ntfs_volume *vol, long long inode) if (d->resident) { fd = open_file(pathname); if (fd < 0) { - ntfs_log_perror("Couldn't create file"); + ntfs_log_perror("Couldn't create file %s", + pathname); goto free; } @@ -2041,7 +2072,8 @@ static int undelete_file(ntfs_volume *vol, long long inode) fd = open_file(pathname); if (fd < 0) { - ntfs_log_perror("Couldn't create output file"); + ntfs_log_perror("Couldn't create file %s", + pathname); goto free; } @@ -2151,9 +2183,11 @@ static int undelete_file(ntfs_volume *vol, long long inode) } set_date(pathname, file->date); if (d->name) - ntfs_log_quiet("Undeleted '%s:%s' successfully.\n", file->pref_name, d->name); + ntfs_log_quiet("Undeleted '%s:%s' successfully to %s.\n", + file->pref_name, d->name, pathname); else - ntfs_log_quiet("Undeleted '%s' successfully.\n", file->pref_name); + ntfs_log_quiet("Undeleted '%s' successfully to %s.\n", + file->pref_name, pathname); } result = 1; free: @@ -2348,7 +2382,7 @@ static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end) create_pathname(opts.dest, name, NULL, pathname, sizeof(pathname)); fd = open_file(pathname); if (fd < 0) { - ntfs_log_perror("Couldn't open output file '%s'", name); + ntfs_log_perror("Couldn't create output file '%s'", name); goto attr; } @@ -2376,6 +2410,7 @@ static int copy_mft(ntfs_volume *vol, long long mft_begin, long long mft_end) } ntfs_log_verbose("Read %lld MFT Records\n", mft_end - mft_begin + 1); + ntfs_log_quiet("MFT extracted to file %s\n", pathname); result = 0; close: close(fd); From f862fcee00cecfbb7427e0e2eec990229d52d99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 1 Jun 2018 16:29:01 +0200 Subject: [PATCH 28/87] Extended the allowed cluster size to 2MB From Windows 10 Creators edition, the cluster size limit has been extended to 2MB. This has implied redefining the boot sector field "sectors_per_cluster" so that values greater than 128 can be recorded. --- include/ntfs-3g/param.h | 7 +++++++ libntfs-3g/bootsect.c | 33 +++++++++++++++++++++++++-------- ntfsprogs/mkntfs.8.in | 2 +- ntfsprogs/mkntfs.c | 28 ++++++++++++++++++---------- ntfsprogs/ntfsclone.c | 34 +++++++++++++++++++++++++++------- ntfsprogs/ntfsresize.c | 20 ++++++++++++++------ 6 files changed, 92 insertions(+), 32 deletions(-) diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h index 23a728cc..c209c53e 100644 --- a/include/ntfs-3g/param.h +++ b/include/ntfs-3g/param.h @@ -39,6 +39,13 @@ enum { DEFSECBASE = 10000 }; +/* + * Parameters for formatting + */ + + /* Up to Windows 10, the cluster size was limited to 64K */ +#define NTFS_MAX_CLUSTER_SIZE 2097152 /* Windows 10 Creators allows 2MB */ + /* * Parameters for compression */ diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index e185fe3f..b0ac4d7a 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -38,6 +38,7 @@ #include #endif +#include "param.h" #include "compat.h" #include "bootsect.h" #include "debug.h" @@ -61,6 +62,7 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) { u32 i; BOOL ret = FALSE; + u16 sectors_per_cluster; ntfs_log_debug("Beginning bootsector check.\n"); @@ -83,15 +85,27 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; default: - ntfs_log_error("Unexpected sectors per cluster value (%d).\n", - b->bpb.sectors_per_cluster); - goto not_ntfs; + if ((b->bpb.sectors_per_cluster < 240) + || (b->bpb.sectors_per_cluster > 249)) { + if (b->bpb.sectors_per_cluster > 128) + ntfs_log_error("Unexpected sectors" + " per cluster value (code 0x%x)\n", + b->bpb.sectors_per_cluster); + else + ntfs_log_error("Unexpected sectors" + " per cluster value (%d).\n", + b->bpb.sectors_per_cluster); + goto not_ntfs; + } } ntfs_log_debug("Checking cluster size.\n"); - i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * - b->bpb.sectors_per_cluster; - if (i > 65536) { + if (b->bpb.sectors_per_cluster > 128) + sectors_per_cluster = 1 << (256 - b->bpb.sectors_per_cluster); + else + sectors_per_cluster = b->bpb.sectors_per_cluster; + i = (u32)le16_to_cpu(b->bpb.bytes_per_sector) * sectors_per_cluster; + if (i > NTFS_MAX_CLUSTER_SIZE) { ntfs_log_error("Unexpected cluster size (%d).\n", i); goto not_ntfs; } @@ -171,7 +185,7 @@ static const char *last_sector_error = int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) { s64 sectors; - u8 sectors_per_cluster; + u16 sectors_per_cluster; s8 c; /* We return -1 with errno = EINVAL on error. */ @@ -186,7 +200,10 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs) * below or equal the number_of_clusters) really belong in the * ntfs_boot_sector_is_ntfs but in this way we can just do this once. */ - sectors_per_cluster = bs->bpb.sectors_per_cluster; + if (bs->bpb.sectors_per_cluster > 128) + sectors_per_cluster = 1 << (256 - bs->bpb.sectors_per_cluster); + else + sectors_per_cluster = bs->bpb.sectors_per_cluster; ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster); if (sectors_per_cluster & (sectors_per_cluster - 1)) { ntfs_log_error("sectors_per_cluster (%d) is not a power of 2." diff --git a/ntfsprogs/mkntfs.8.in b/ntfsprogs/mkntfs.8.in index a83cab9c..13a17af2 100644 --- a/ntfsprogs/mkntfs.8.in +++ b/ntfsprogs/mkntfs.8.in @@ -132,7 +132,7 @@ actual writing to the device. .TP \fB\-c\fR, \fB\-\-cluster\-size\fR BYTES Specify the size of clusters in bytes. Valid cluster size values are powers of -two, with at least 256, and at most 65536 bytes per cluster. If omitted, +two, with at least 256, and at most 2097152 bytes (2MB) per cluster. If omitted, .B mkntfs uses 4096 bytes as the default cluster size. .sp diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c index 5eb7d4fb..7d03c51d 100644 --- a/ntfsprogs/mkntfs.c +++ b/ntfsprogs/mkntfs.c @@ -6,7 +6,7 @@ * Copyright (c) 2002-2006 Szabolcs Szakacsits * Copyright (c) 2005 Erik Sornes * Copyright (c) 2007 Yura Pakhuchiy - * Copyright (c) 2010-2014 Jean-Pierre Andre + * Copyright (c) 2010-2018 Jean-Pierre Andre * * This utility will create an NTFS 1.2 or 3.1 volume on a user * specified (block) device. @@ -119,6 +119,7 @@ # endif #endif +#include "param.h" #include "security.h" #include "types.h" #include "attrib.h" @@ -287,7 +288,7 @@ static void mkntfs_version(void) ntfs_log_info("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); ntfs_log_info("Copyright (c) 2005 Erik Sornes\n"); ntfs_log_info("Copyright (c) 2007 Yura Pakhuchiy\n"); - ntfs_log_info("Copyright (c) 2010-2014 Jean-Pierre Andre\n"); + ntfs_log_info("Copyright (c) 2010-2018 Jean-Pierre Andre\n"); ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -3719,11 +3720,11 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol) /* * For huge volumes, grow the cluster size until the number of * clusters fits into 32 bits or the cluster size exceeds the - * maximum limit of 64kiB. + * maximum limit of NTFS_MAX_CLUSTER_SIZE. */ while (volume_size >> (ffs(vol->cluster_size) - 1 + 32)) { vol->cluster_size <<= 1; - if (vol->cluster_size > 65535) { + if (vol->cluster_size >= NTFS_MAX_CLUSTER_SIZE) { ntfs_log_error("Device is too large to hold an " "NTFS volume (maximum size is " "256TiB).\n"); @@ -3744,15 +3745,18 @@ static BOOL mkntfs_override_vol_params(ntfs_volume *vol) "to, or larger than, the sector size.\n"); return FALSE; } - if (vol->cluster_size > 128 * (u32)opts.sector_size) { + /* Before Windows 10 Creators, the limit was 128 */ + if (vol->cluster_size > 4096 * (u32)opts.sector_size) { ntfs_log_error("The cluster size is invalid. It cannot be " - "more that 128 times the size of the sector " + "more that 4096 times the size of the sector " "size.\n"); return FALSE; } - if (vol->cluster_size > 65536) { + if (vol->cluster_size > NTFS_MAX_CLUSTER_SIZE) { ntfs_log_error("The cluster size is invalid. The maximum " - "cluster size is 65536 bytes (64kiB).\n"); + "cluster size is %lu bytes (%lukiB).\n", + (unsigned long)NTFS_MAX_CLUSTER_SIZE, + (unsigned long)(NTFS_MAX_CLUSTER_SIZE >> 10)); return FALSE; } vol->cluster_size_bits = ffs(vol->cluster_size) - 1; @@ -4387,6 +4391,7 @@ static BOOL mkntfs_create_root_structures(void) u8 *sd; FILE_ATTR_FLAGS extend_flags; VOLUME_FLAGS volume_flags = const_cpu_to_le16(0); + int sectors_per_cluster; int nr_sysfiles; int buf_sds_first_size; char *buf_sds; @@ -4639,8 +4644,11 @@ static BOOL mkntfs_create_root_structures(void) * already inserted, so no need to worry about these things. */ bs->bpb.bytes_per_sector = cpu_to_le16(opts.sector_size); - bs->bpb.sectors_per_cluster = (u8)(g_vol->cluster_size / - opts.sector_size); + sectors_per_cluster = g_vol->cluster_size / opts.sector_size; + if (sectors_per_cluster > 128) + bs->bpb.sectors_per_cluster = 257 - ffs(sectors_per_cluster); + else + bs->bpb.sectors_per_cluster = sectors_per_cluster; bs->bpb.media_type = 0xf8; /* hard disk */ bs->bpb.sectors_per_track = cpu_to_le16(opts.sectors_per_track); ntfs_log_debug("sectors per track = %ld (0x%lx)\n", diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index e161d1d1..dd1633af 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -3,7 +3,7 @@ * * Copyright (c) 2003-2006 Szabolcs Szakacsits * Copyright (c) 2004-2006 Anton Altaparmakov - * Copyright (c) 2010-2017 Jean-Pierre Andre + * Copyright (c) 2010-2018 Jean-Pierre Andre * Special image format support copyright (c) 2004 Per Olofsson * * Clone NTFS data and/or metadata to a sparse file, image, device or stdout. @@ -71,6 +71,7 @@ */ #define NTFS_DO_NOT_CHECK_ENDIANS +#include "param.h" #include "debug.h" #include "types.h" #include "support.h" @@ -270,7 +271,6 @@ static int compare_bitmaps(struct bitmap *a, BOOL copy); #define LAST_METADATA_INODE 11 -#define NTFS_MAX_CLUSTER_SIZE 65536 #define NTFS_SECTOR_SIZE 512 #define rounded_up_division(a, b) (((a) + (b - 1)) / (b)) @@ -393,7 +393,7 @@ static void version(void) "Efficiently clone, image, restore or rescue an NTFS Volume.\n\n" "Copyright (c) 2003-2006 Szabolcs Szakacsits\n" "Copyright (c) 2004-2006 Anton Altaparmakov\n" - "Copyright (c) 2010-2016 Jean-Pierre Andre\n\n"); + "Copyright (c) 2010-2018 Jean-Pierre Andre\n\n"); fprintf(stderr, "%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); exit(0); } @@ -756,7 +756,7 @@ static void read_rescue(void *fd, char *buff, u32 csize, u32 bytes_per_sector, static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) { - char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + char *buff; /* vol is NULL if opt.restore_image is set */ s32 csize = le32_to_cpu(image_hdr.cluster_size); BOOL backup_bootsector; @@ -783,6 +783,10 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) } } + buff = (char*)ntfs_malloc(csize); + if (!buff) + err_exit("Not enough memory"); + // need reading when not about to write ? if (read_all(fd, buff, csize) == -1) { @@ -858,6 +862,7 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) perr_printf("Write failed"); #endif } + free(buff); } static s64 lseek_out(int fd, s64 pos, int mode) @@ -995,7 +1000,11 @@ static void write_empty_clusters(s32 csize, s64 count, struct progress_bar *progress, u64 *p_counter) { s64 i; - char buff[NTFS_MAX_CLUSTER_SIZE]; + char *buff; + + buff = (char*)ntfs_malloc(csize); + if (!buff) + err_exit("Not enough memory"); memset(buff, 0, csize); @@ -1004,6 +1013,7 @@ static void write_empty_clusters(s32 csize, s64 count, perr_exit("write_all"); progress_update(progress, ++(*p_counter)); } + free(buff); } static void restore_image(void) @@ -1492,7 +1502,7 @@ static void write_set(char *buff, u32 csize, s64 *current_lcn, static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) { - char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + char *buff; void *fd; s64 mft_no; u32 mft_record_size; @@ -1522,6 +1532,10 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) clusters_per_set = mft_record_size/csize; records_per_set = 1; } + buff = (char*)ntfs_malloc(mft_record_size); + if (!buff) + err_exit("Not enough memory"); + mft_no = 0; ri = rj = 0; wi = wj = 0; @@ -1554,6 +1568,7 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) } } image->current_lcn = current_lcn; + free(buff); } /* @@ -1566,7 +1581,7 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) { - char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + char *buff; void *fd; u32 indx_record_size; u32 csize; @@ -1595,6 +1610,10 @@ static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) clusters_per_set = indx_record_size/csize; records_per_set = 1; } + buff = (char*)ntfs_malloc(indx_record_size); + if (!buff) + err_exit("Not enough memory"); + ri = rj = 0; wi = wj = 0; if (rl[ri].length) @@ -1627,6 +1646,7 @@ static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) } } image->current_lcn = current_lcn; + free(buff); } static void dump_clusters(ntfs_walk_clusters_ctx *image, runlist *rl) diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index 9189dc72..a912f9df 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -59,6 +59,7 @@ #include #endif +#include "param.h" #include "debug.h" #include "types.h" #include "support.h" @@ -246,8 +247,6 @@ static s64 max_free_cluster_range = 0; #define DIRTY_INODE (1) #define DIRTY_ATTRIB (2) -#define NTFS_MAX_CLUSTER_SIZE (65536) - static s64 rounded_up_division(s64 numer, s64 denom) { return (numer + (denom - 1)) / denom; @@ -407,7 +406,7 @@ static void version(void) printf("Copyright (c) 2002-2005 Anton Altaparmakov\n"); printf("Copyright (c) 2002-2003 Richard Russon\n"); printf("Copyright (c) 2007 Yura Pakhuchiy\n"); - printf("Copyright (c) 2011-2016 Jean-Pierre Andre\n"); + printf("Copyright (c) 2011-2018 Jean-Pierre Andre\n"); printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); } @@ -1891,9 +1890,13 @@ static void lseek_to_cluster(ntfs_volume *vol, s64 lcn) static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len) { s64 i; - char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ + char *buff; ntfs_volume *vol = resize->vol; + buff = (char*)ntfs_malloc(vol->cluster_size); + if (!buff) + perr_exit("ntfs_malloc"); + for (i = 0; i < len; i++) { lseek_to_cluster(vol, src + i); @@ -1917,6 +1920,7 @@ static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len) resize->relocations++; progress_update(&resize->progress, resize->relocations); } + free(buff); } static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn) @@ -2758,8 +2762,12 @@ static void update_bootsector(ntfs_resize_t *r) if (vol->dev->d_ops->read(vol->dev, bs, bs_size) == -1) perr_exit("read() error"); - bs->number_of_sectors = cpu_to_sle64(r->new_volume_size * - bs->bpb.sectors_per_cluster); + if (bs->bpb.sectors_per_cluster > 128) + bs->number_of_sectors = cpu_to_sle64(r->new_volume_size + << (256 - bs->bpb.sectors_per_cluster)); + else + bs->number_of_sectors = cpu_to_sle64(r->new_volume_size * + bs->bpb.sectors_per_cluster); if (r->mftmir_old || (r->mirr_from == MIRR_MFT)) { r->progress.flags |= NTFS_PROGBAR_SUPPRESS; From 90c361e7f862a7564eaea95e53d4d087df330084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 2 Aug 2018 16:41:19 +0200 Subject: [PATCH 29/87] Allocated full clusters for reading and rescuing in ntfsclone Even though mft or index records may be smaller than a cluster, reading and rescuing them is done on a full cluster base, so full clusters must be allocated for processing them. --- ntfsprogs/ntfsclone.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index dd1633af..bbf6d1f0 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -773,6 +773,9 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) } rescue_pos = (off_t)(rescue_lcn * csize); + buff = (char*)ntfs_malloc(csize); + if (!buff) + err_exit("Not enough memory"); /* possible partial cluster holding the backup boot sector */ backup_bootsector = (lcn + 1)*csize >= full_device_size; @@ -783,10 +786,6 @@ static void copy_cluster(int rescue, u64 rescue_lcn, u64 lcn) } } - buff = (char*)ntfs_malloc(csize); - if (!buff) - err_exit("Not enough memory"); - // need reading when not about to write ? if (read_all(fd, buff, csize) == -1) { @@ -1507,6 +1506,7 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) s64 mft_no; u32 mft_record_size; u32 csize; + u32 buff_size; u32 bytes_per_sector; u32 records_per_set; u32 clusters_per_set; @@ -1524,15 +1524,18 @@ static void copy_wipe_mft(ntfs_walk_clusters_ctx *image, runlist *rl) /* * Depending on the sizes, there may be several records * per cluster, or several clusters per record. + * Anyway, records are read and rescued by full clusters. */ if (csize >= mft_record_size) { records_per_set = csize/mft_record_size; clusters_per_set = 1; + buff_size = csize; } else { clusters_per_set = mft_record_size/csize; records_per_set = 1; + buff_size = mft_record_size; } - buff = (char*)ntfs_malloc(mft_record_size); + buff = (char*)ntfs_malloc(buff_size); if (!buff) err_exit("Not enough memory"); @@ -1585,6 +1588,7 @@ static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) void *fd; u32 indx_record_size; u32 csize; + u32 buff_size; u32 bytes_per_sector; u32 records_per_set; u32 clusters_per_set; @@ -1601,16 +1605,19 @@ static void copy_wipe_i30(ntfs_walk_clusters_ctx *image, runlist *rl) /* * Depending on the sizes, there may be several records * per cluster, or several clusters per record. + * Anyway, records are read and rescued by full clusters. */ indx_record_size = image->ni->vol->indx_record_size; if (csize >= indx_record_size) { records_per_set = csize/indx_record_size; clusters_per_set = 1; + buff_size = csize; } else { clusters_per_set = indx_record_size/csize; records_per_set = 1; + buff_size = indx_record_size; } - buff = (char*)ntfs_malloc(indx_record_size); + buff = (char*)ntfs_malloc(buff_size); if (!buff) err_exit("Not enough memory"); From 9a8aeeea1334eb8dc97061973707675e46f295ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 2 Aug 2018 16:47:16 +0200 Subject: [PATCH 30/87] Prevented locally defined headers from interfering with ntfs-3g ones Order the include directories so that those defined for ntfs-3g have priority over locally defined ones. --- libfuse-lite/Makefile.am | 5 ++++- libntfs-3g/Makefile.am | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libfuse-lite/Makefile.am b/libfuse-lite/Makefile.am index d9591ec4..ba2934cf 100644 --- a/libfuse-lite/Makefile.am +++ b/libfuse-lite/Makefile.am @@ -7,7 +7,10 @@ endif libfuse_lite_la_CFLAGS= \ $(AM_CFLAGS) \ - $(LIBFUSE_LITE_CFLAGS) \ + $(LIBFUSE_LITE_CFLAGS) + +libfuse_lite_la_CPPFLAGS= \ + $(AM_CPPFLAGS) \ -I$(top_srcdir)/include/fuse-lite libfuse_lite_la_LIBADD = $(LIBFUSE_LITE_LIBS) diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index d6b150e5..6feba7d7 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -9,8 +9,8 @@ else noinst_LTLIBRARIES = libntfs-3g.la endif -libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g -libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) +libntfs_3g_la_CFLAGS = $(AM_CFLAGS) +libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) -I$(top_srcdir)/include/ntfs-3g libntfs_3g_la_LIBADD = $(LIBNTFS_LIBS) libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined From d52b1ca9295084d97e3a71a53700253fa35eb20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Thu, 2 Aug 2018 16:51:57 +0200 Subject: [PATCH 31/87] Removed an unused field (cleanup) Cleanup imported from the fuse library project. --- libfuse-lite/fuse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 903a05f7..52ff8d4d 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -134,7 +134,6 @@ struct fuse_dh { struct fuse *fuse; fuse_req_t req; char *contents; - int allocated; unsigned len; unsigned size; unsigned needlen; From 2514ce6a4200a42a7d168a85132610c5c118df9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 22 Aug 2018 09:43:19 +0200 Subject: [PATCH 32/87] Attempted mounting read-only after failed permission to read-write If a partition image could not be opened read-write, retry as read-only --- libntfs-3g/unix_io.c | 3 +++ libntfs-3g/volume.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/unix_io.c b/libntfs-3g/unix_io.c index c88e8f82..1103c7e3 100644 --- a/libntfs-3g/unix_io.c +++ b/libntfs-3g/unix_io.c @@ -143,6 +143,9 @@ static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags) *(int*)dev->d_private = open(dev->d_name, flags); if (*(int*)dev->d_private == -1) { err = errno; + /* if permission error and rw, retry read-only */ + if ((err == EACCES) && ((flags & O_RDWR) == O_RDWR)) + err = EROFS; goto err_out; } #ifdef HAVE_LINUX_FS_H diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index d36c7d55..7a1bcf29 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -529,7 +529,7 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, dev->d_name); goto error_exit; } else { - ntfs_log_info("Can only open '%s' as read-only\n", + ntfs_log_info("Error opening '%s' read-write\n", dev->d_name); NVolSetReadOnly(vol); } From e87c85355162298caf794033c42b7369f23d664f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 22 Aug 2018 09:46:30 +0200 Subject: [PATCH 33/87] Fixed collecting the label argument in mkntfs The label argument could be wrongly interpreted, depending on the syntax use to state the options. --- ntfsprogs/mkntfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntfsprogs/mkntfs.c b/ntfsprogs/mkntfs.c index 7d03c51d..3e127a3d 100644 --- a/ntfsprogs/mkntfs.c +++ b/ntfsprogs/mkntfs.c @@ -670,7 +670,7 @@ static int mkntfs_parse_options(int argc, char *argv[], struct mkntfs_options *o break; case 'L': if (!opts2->label) { - opts2->label = argv[optind-1]; + opts2->label = optarg; } else { ntfs_log_error("You may only specify the label " "once.\n"); From 1ea2003e961f0c552c1c951555f3ba9be3333ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 19 Dec 2018 15:48:03 +0100 Subject: [PATCH 34/87] Realigned times set from extended attribute The alignment of times set in an extended attribute value cannot be asserted, and this cause alignment errors on some CPUs (met on ARM). Be safe by copying them in a properly aligned array. --- libntfs-3g/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 01430210..b25f4051 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -1518,14 +1518,16 @@ int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; FILE_NAME_ATTR *fn; - const u64 *times; + u64 times[4]; ntfs_time now; int cnt; int ret; ret = -1; if ((size >= 8) && !(flags & XATTR_CREATE)) { - times = (const u64*)value; + /* Copy, to avoid alignment issue encountered on ARM */ + memcpy(times, value, + (size < sizeof(times) ? size : sizeof(times))); now = ntfs_current_time(); /* update the standard information attribute */ ctx = ntfs_attr_get_search_ctx(ni, NULL); From f424cea20ab63cf8352d995500fd5549ad4e9192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 19 Dec 2018 15:53:08 +0100 Subject: [PATCH 35/87] Fixed a typo in the ntfscluster manual An essential word was missing. --- ntfsprogs/ntfscluster.8.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntfsprogs/ntfscluster.8.in b/ntfsprogs/ntfscluster.8.in index 9186a648..b0052df1 100644 --- a/ntfsprogs/ntfscluster.8.in +++ b/ntfsprogs/ntfscluster.8.in @@ -69,7 +69,7 @@ This option is not yet implemented. .TP \fB\-q\fR, \fB\-\-quiet\fR Reduce the amount of output to a minimum. Naturally, it doesn't make sense to -combine this option with +combine this option with \fB\-\-verbose\fR .TP \fB\-s\fR, \fB\-\-sector\fR RANGE Any files whose data is in this range of sectors will be displayed. From 85c1634a26faa572d3c558d4cf8aaaca5202d4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 19 Dec 2018 15:57:50 +0100 Subject: [PATCH 36/87] Fixed reporting an error when failed to build the mountpoint The size check was inefficient because getcwd() uses an unsigned int argument. --- src/lowntfs-3g.c | 6 +++++- src/ntfs-3g.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 993867fa..0660439b 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4411,7 +4411,8 @@ int main(int argc, char *argv[]) else { ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX); if (ctx->abs_mnt_point) { - if (getcwd(ctx->abs_mnt_point, + if ((strlen(opts.mnt_point) < PATH_MAX) + && getcwd(ctx->abs_mnt_point, PATH_MAX - strlen(opts.mnt_point) - 1)) { strcat(ctx->abs_mnt_point, "/"); strcat(ctx->abs_mnt_point, opts.mnt_point); @@ -4419,6 +4420,9 @@ int main(int argc, char *argv[]) /* Solaris also wants the absolute mount point */ opts.mnt_point = ctx->abs_mnt_point; #endif /* defined(__sun) && defined (__SVR4) */ + } else { + free(ctx->abs_mnt_point); + ctx->abs_mnt_point = (char*)NULL; } } } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 6ce89fef..4e0912ae 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4148,7 +4148,8 @@ int main(int argc, char *argv[]) else { ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX); if (ctx->abs_mnt_point) { - if (getcwd(ctx->abs_mnt_point, + if ((strlen(opts.mnt_point) < PATH_MAX) + && getcwd(ctx->abs_mnt_point, PATH_MAX - strlen(opts.mnt_point) - 1)) { strcat(ctx->abs_mnt_point, "/"); strcat(ctx->abs_mnt_point, opts.mnt_point); @@ -4156,6 +4157,9 @@ int main(int argc, char *argv[]) /* Solaris also wants the absolute mount point */ opts.mnt_point = ctx->abs_mnt_point; #endif /* defined(__sun) && defined (__SVR4) */ + } else { + free(ctx->abs_mnt_point); + ctx->abs_mnt_point = (char*)NULL; } } } From da39fbf24a6a967ad77c2de99408cbf4353dc9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 19 Dec 2018 16:01:53 +0100 Subject: [PATCH 37/87] Fixed reporting an error when setxattr is rejected When permissions are enabled and setxattr() is rejected, an error must be returned even though the option silent is set. This is needed for "cp -p" to know it has to try setting the permissions again and use chmod(). --- src/lowntfs-3g.c | 2 +- src/ntfs-3g.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 0660439b..b553838b 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3152,7 +3152,7 @@ static ntfs_inode *ntfs_check_access_xattr(fuse_req_t req, || !(ctx->secure_flags & (1 << SECURITY_ACL)) || (setting && ctx->inherit)) && foracl) { - if (ctx->silent) + if (ctx->silent && !ctx->security.mapping[MAPUSERS]) errno = 0; else errno = EOPNOTSUPP; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 4e0912ae..1631a224 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2879,7 +2879,7 @@ static ntfs_inode *ntfs_check_access_xattr(struct SECURITY_CONTEXT *security, || !(ctx->secure_flags & (1 << SECURITY_ACL)) || (setting && ctx->inherit)) && foracl) { - if (ctx->silent) + if (ctx->silent && !ctx->security.mapping[MAPUSERS]) errno = 0; else errno = EOPNOTSUPP; From 7a3ed93463a527e8c127199be41b145b5239b1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 23 Jan 2019 17:13:12 +0100 Subject: [PATCH 38/87] Reverted accessing reparse directories through internal plugins As similar reparse tags are being used for accessing OneDrive files and directories, a similar policy has to be used for accessing them. Until giving full access to OneDrive files with a local copy is mature enough to have it processed internally, it is safer to have it delegated to an external plugin. This reverts [4f450a] --- src/lowntfs-3g.c | 1 - src/ntfs-3g.c | 1 - src/ntfs-3g_common.c | 145 +++---------------------------------------- src/ntfs-3g_common.h | 2 +- 4 files changed, 10 insertions(+), 139 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index b553838b..26d6eca5 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4572,7 +4572,6 @@ int main(int argc, char *argv[]) #ifndef DISABLE_PLUGINS register_internal_reparse_plugins(); - register_directory_plugins(ctx); #endif /* DISABLE_PLUGINS */ se = mount_fuse(parsed_options); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 1631a224..d0256e22 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4308,7 +4308,6 @@ int main(int argc, char *argv[]) #ifndef DISABLE_PLUGINS register_internal_reparse_plugins(); - register_directory_plugins(ctx); #endif /* DISABLE_PLUGINS */ fh = mount_fuse(parsed_options); diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 64b0f52b..20a82dec 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -36,10 +36,6 @@ #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif - #ifdef HAVE_LIMITS_H #include #endif @@ -770,108 +766,6 @@ exit : #ifndef DISABLE_PLUGINS -/* - * Get attribute information for reparse directories - * - * Reparse directories have a reparse tag which should be ignored. - */ - -static int directory_getattr(ntfs_inode *ni, const REPARSE_POINT *reparse, - struct stat *stbuf) -{ - static ntfschar I30[] = - { const_cpu_to_le16('$'), const_cpu_to_le16('I'), - const_cpu_to_le16('3'), const_cpu_to_le16('0') }; - ntfs_attr *na; - int res; - - res = -EOPNOTSUPP; - if (ni && reparse && stbuf - && ((reparse->reparse_tag == IO_REPARSE_TAG_WCI) - || ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY) - && !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS))) - && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { - /* Directory */ - stbuf->st_mode = S_IFDIR | 0555; - /* get index size, if not known */ - if (!test_nino_flag(ni, KnownSize)) { - na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, I30, 4); - if (na) { - ni->data_size = na->data_size; - ni->allocated_size = na->allocated_size; - set_nino_flag(ni, KnownSize); - ntfs_attr_close(na); - } - } - stbuf->st_size = ni->data_size; - stbuf->st_blocks = ni->allocated_size >> 9; - stbuf->st_nlink = 1; /* Make find(1) work */ - res = 0; - } - /* Not a directory, or another error occurred */ - return (res); -} - -/* - * Open a reparse directory for reading - * - * Currently no reading context is created. - */ - -static int directory_opendir(ntfs_inode *ni, const REPARSE_POINT *reparse, - struct fuse_file_info *fi) -{ - int res; - - res = -EOPNOTSUPP; - if (ni && reparse && fi - && ((reparse->reparse_tag == IO_REPARSE_TAG_WCI) - || ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY) - && !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS))) - && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - && ((fi->flags & O_ACCMODE) == O_RDONLY)) - res = 0; - return (res); -} - -/* - * Release a reparse directory - * - * Should never be called, as no reading context was defined. - */ - -static int directory_release(ntfs_inode *ni __attribute__((unused)), - const REPARSE_POINT *reparse __attribute__((unused)), - struct fuse_file_info *fi __attribute__((unused))) -{ - return 0; -} - -/* - * Read an open reparse directory - * - * Returns 0 or a negative error code - */ - -static int directory_readdir(ntfs_inode *ni, const REPARSE_POINT *reparse, - s64 *pos, void *fillctx, ntfs_filldir_t filldir, - struct fuse_file_info *fi __attribute__((unused))) -{ - int res; - - res = -EOPNOTSUPP; - if (ni && reparse && pos && fillctx && filldir - && ((reparse->reparse_tag == IO_REPARSE_TAG_WCI) - || ((reparse->reparse_tag & IO_REPARSE_TAG_DIRECTORY) - && !(reparse->reparse_tag & IO_REPARSE_TAG_IS_ALIAS))) - && (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { - res = 0; - if (ntfs_readdir(ni, pos, fillctx, filldir)) - res = -errno; - } - return (res); -} - int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag, const plugin_operations_t *ops, void *handle) { @@ -879,16 +773,14 @@ int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag, int res; res = -1; - if (ctx) { - plugin = (plugin_list_t*)ntfs_malloc(sizeof(plugin_list_t)); - if (plugin) { - plugin->tag = tag; - plugin->ops = ops; - plugin->handle = handle; - plugin->next = ctx->plugins; - ctx->plugins = plugin; - res = 0; - } + plugin = (plugin_list_t*)ntfs_malloc(sizeof(plugin_list_t)); + if (plugin) { + plugin->tag = tag; + plugin->ops = ops; + plugin->handle = handle; + plugin->next = ctx->plugins; + ctx->plugins = plugin; + res = 0; } return (res); } @@ -917,9 +809,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, reparse = ntfs_get_reparse_point(ni); if (reparse) { tag = reparse->reparse_tag; - seltag = tag; - if (tag & IO_REPARSE_TAG_DIRECTORY) - seltag &= IO_REPARSE_TAG_DIRECTORY; + seltag = tag & IO_REPARSE_PLUGIN_SELECT; for (plugin=ctx->plugins; plugin && (plugin->tag != seltag); plugin = plugin->next) { } if (plugin) { @@ -981,23 +871,6 @@ void close_reparse_plugins(ntfs_fuse_context_t *ctx) } } -void register_directory_plugins(ntfs_fuse_context_t *ctx) -{ - static const struct plugin_operations ops = { - .getattr = directory_getattr, - .release = directory_release, - .opendir = directory_opendir, - .readdir = directory_readdir, - } ; - - if (ctx) { - register_reparse_plugin(ctx, IO_REPARSE_TAG_WCI, - &ops, (void*)NULL); - register_reparse_plugin(ctx, IO_REPARSE_TAG_DIRECTORY, - &ops, (void*)NULL); - } -} - #endif /* DISABLE_PLUGINS */ #ifdef HAVE_SETXATTR diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index 33cced36..bcffe4f7 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -211,7 +211,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, ntfs_inode *ni, REPARSE_POINT **reparse); int register_reparse_plugin(ntfs_fuse_context_t *ctx, le32 tag, const plugin_operations_t *ops, void *handle); -void register_directory_plugins(ntfs_fuse_context_t *ctx); + #endif /* DISABLE_PLUGINS */ #endif /* _NTFS_3G_COMMON_H */ From 96825f3c2bcd08dc37698809b6585077162d8dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 23 Jan 2019 17:27:25 +0100 Subject: [PATCH 39/87] Cleaned object ids beyond the updated part An earlier patch enabled updating a file proper id without changing the other id (birth, volume, domain). However the first time the id is set, these other ids have to be zeroed. --- libntfs-3g/object_id.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/object_id.c b/libntfs-3g/object_id.c index b7cc77cc..090070f3 100644 --- a/libntfs-3g/object_id.c +++ b/libntfs-3g/object_id.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2009-2017 Jean-Pierre Andre + * Copyright (c) 2009-2019 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -333,7 +333,7 @@ static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); if (na) { - + memset(&old_attr, 0, sizeof(OBJECT_ID_ATTR)); /* remove the existing index entry */ oldsize = remove_object_id_index(na,xo,&old_attr); if (oldsize < 0) From facb69730396d5b033cd2289903a00d1b645b2fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 23 Jan 2019 17:33:15 +0100 Subject: [PATCH 40/87] Fixed reacting to missing plugin When a plugin is missing, dlopen() does not set errno, which leads to erratic behavior. Set errno as ELIBACC when this occurs. --- include/ntfs-3g/compat.h | 7 ++++++- src/lowntfs-3g.c | 9 ++++----- src/ntfs-3g_common.c | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/ntfs-3g/compat.h b/include/ntfs-3g/compat.h index ded6c5f8..fa57a38a 100644 --- a/include/ntfs-3g/compat.h +++ b/include/ntfs-3g/compat.h @@ -1,9 +1,10 @@ /* - * compat.h - Tweaks for Windows compatibility. + * compat.h - Tweaks for compatibility with non-Linux systems. * * Copyright (c) 2002 Richard Russon * Copyright (c) 2002-2004 Anton Altaparmakov * Copyright (c) 2008-2009 Szabolcs Szakacsits + * Copyright (c) 2019 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -41,6 +42,10 @@ #define ELIBBAD ENOEXEC #endif +#ifndef ELIBACC +#define ELIBACC ENOENT +#endif + #ifndef PATH_MAX #define PATH_MAX 4096 #endif diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 26d6eca5..983ab010 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2017 Jean-Pierre Andre + * Copyright (c) 2007-2019 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -263,7 +263,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2017 Jean-Pierre Andre\n" +"Copyright (C) 2007-2019 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -1074,10 +1074,9 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino) REPARSE_POINT *reparse; res = CALL_REPARSE_PLUGIN(ni, readlink, &buf); - if (res) { + if (res || !buf) { buf = strdup(ntfs_bad_reparse); - if (!buf) - res = -errno; + res = (buf ? 0 : -errno); } #else /* DISABLE_PLUGINS */ errno = 0; diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 20a82dec..a8ea6385 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -1,7 +1,7 @@ /** * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g. * - * Copyright (c) 2010-2018 Jean-Pierre Andre + * Copyright (c) 2010-2019 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -841,6 +841,7 @@ const struct plugin_operations *select_reparse_plugin(ntfs_fuse_context_t *ctx, if (!ops) dlclose(handle); } else { + errno = ELIBACC; if (!(ctx->errors_logged & ERR_PLUGIN)) { ntfs_log_perror( "Could not load plugin %s", From 006799ab801a80c519b0ea63bcff5e60837a681b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 23 Jan 2019 17:38:10 +0100 Subject: [PATCH 41/87] Fixed an endianness error in ntfscp The file timestamp was not set according to CPU endianness. --- ntfsprogs/ntfscp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ntfsprogs/ntfscp.c b/ntfsprogs/ntfscp.c index 726e6471..f30be5a7 100644 --- a/ntfsprogs/ntfscp.c +++ b/ntfsprogs/ntfscp.c @@ -4,7 +4,7 @@ * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2005 Anton Altaparmakov * Copyright (c) 2006 Hil Liao - * Copyright (c) 2014 Jean-Pierre Andre + * Copyright (c) 2014-2019 Jean-Pierre Andre * * This utility will copy file to an NTFS volume. * @@ -1153,8 +1153,9 @@ close_attr: ntfs_attr_close(na); if (opts.timestamp) { if (!fstat(fileno(in),&st)) { - out->last_data_change_time = st.st_mtime*10000000LL + s64 change_time = st.st_mtime*10000000LL + NTFS_TIME_OFFSET; + out->last_data_change_time = cpu_to_le64(change_time); ntfs_inode_update_times(out, 0); } else { ntfs_log_error("Failed to get the time stamp.\n"); From 1ae6d818f0d57f527eda37d692bbb51ceb44fe06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 23 Jan 2019 17:40:23 +0100 Subject: [PATCH 42/87] Returned a low level error when an ioctl fails When an ioctl fails, the errno is not correctly propagated, and the log was wrong. --- libfuse-lite/fuse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 52ff8d4d..256bed0f 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -2781,8 +2781,10 @@ static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg, fuse_finish_interrupt(f, req, &d); free(path); - fuse_reply_ioctl(req, err, out_buf, out_bufsz); + if (err >= 0) { /* not an error */ + fuse_reply_ioctl(req, err, out_buf, out_bufsz); goto out; + } err: reply_err(req, err); out: From b9ad82ced79163d28571a0be65196c078bd67ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 23 Jan 2019 17:43:47 +0100 Subject: [PATCH 43/87] Truncated SSD trimming zones to granularity supported by the device When the trimming granularity is greater than the cluster size, the free zones have to be truncated to match the granularity. --- libntfs-3g/ioctl.c | 63 +++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c index 2eef5a51..6c49395d 100644 --- a/libntfs-3g/ioctl.c +++ b/libntfs-3g/ioctl.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2014-2015 Jean-Pierre Andre + * Copyright (c) 2014-2019 Jean-Pierre Andre * Copyright (c) 2014 Red Hat, Inc. * * This program/include file is free software; you can redistribute it and/or @@ -225,6 +225,24 @@ not_found: return 0; } +static inline LCN align_up(ntfs_volume *vol, LCN lcn, u64 granularity) +{ + u64 aligned; + + aligned = (lcn << vol->cluster_size_bits) + granularity - 1; + aligned -= aligned % granularity; + return (aligned >> vol->cluster_size_bits); +} + +static inline u64 align_down(ntfs_volume *vol, u64 count, u64 granularity) +{ + u64 aligned; + + aligned = count << vol->cluster_size_bits; + aligned -= aligned % granularity; + return (aligned >> vol->cluster_size_bits); +} + #define FSTRIM_BUFSIZ 4096 /* Trim the filesystem. @@ -255,11 +273,11 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed) * XXX We could fix these limitations in future. */ if (start != 0 || len != (uint64_t)-1) { - ntfs_log_debug("fstrim: setting start or length is not supported\n"); + ntfs_log_error("fstrim: setting start or length is not supported\n"); return -EINVAL; } if (minlen > vol->cluster_size) { - ntfs_log_debug("fstrim: minlen > cluster size is not supported\n"); + ntfs_log_error("fstrim: minlen > cluster size is not supported\n"); return -EINVAL; } @@ -269,7 +287,7 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed) * different. */ if (!NDevBlock(vol->dev)) { - ntfs_log_debug("fstrim: not supported for non-block-device\n"); + ntfs_log_error("fstrim: not supported for non-block-device\n"); return -EOPNOTSUPP; } @@ -278,15 +296,12 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed) if (ret) return ret; if (discard_alignment != 0) { - ntfs_log_debug("fstrim: backing device is not aligned for discards\n"); - return -EOPNOTSUPP; - } - if (discard_granularity > vol->cluster_size) { - ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n"); + ntfs_log_error("fstrim: backing device is not aligned for discards\n"); return -EOPNOTSUPP; } + if (discard_max_bytes == 0) { - ntfs_log_debug("fstrim: backing device does not support discard (discard_max_bytes == 0)\n"); + ntfs_log_error("fstrim: backing device does not support discard (discard_max_bytes == 0)\n"); return -EOPNOTSUPP; } @@ -323,11 +338,14 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed) } /* Trim the clusters in large as possible blocks, but - * not larger than discard_max_bytes. + * not larger than discard_max_bytes, and compatible + * with the supported trim granularity. */ for (start_lcn = start_buf; start_lcn < end_buf; ++start_lcn) { if (!ntfs_bit_get(buf, start_lcn-start_buf)) { LCN end_lcn; + LCN aligned_lcn; + u64 aligned_count; /* Cluster 'start_lcn' is not in use, * find end of this run. @@ -338,14 +356,25 @@ static int fstrim(ntfs_volume *vol, void *data, u64 *trimmed) < discard_max_bytes && !ntfs_bit_get(buf, end_lcn-start_buf)) end_lcn++; + aligned_lcn = align_up(vol, start_lcn, + discard_granularity); + if (aligned_lcn >= end_lcn) + aligned_count = 0; + else { + aligned_count = + align_down(vol, + end_lcn - aligned_lcn, + discard_granularity); + } + if (aligned_count) { + ret = fstrim_clusters(vol, + aligned_lcn, aligned_count); + if (ret) + goto free_out; - ret = fstrim_clusters(vol, - start_lcn, end_lcn-start_lcn); - if (ret) - goto free_out; - - *trimmed += (end_lcn - start_lcn) + *trimmed += aligned_count << vol->cluster_size_bits; + } start_lcn = end_lcn-1; } } From 3433dd0b3c3a5b41fd6b6a85f9815ee32adbeacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 10:18:01 +0100 Subject: [PATCH 44/87] Updated the copyright notice Updated the copyright notice to 2019 --- src/ntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d0256e22..465f1525 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2017 Jean-Pierre Andre + * Copyright (c) 2007-2019 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -196,7 +196,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2017 Jean-Pierre Andre\n" +"Copyright (C) 2007-2019 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" From 32c27a8a4fda5205e579165baa716231efb6c1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 10:31:12 +0100 Subject: [PATCH 45/87] Defined the request argument of ioctl() as unsigned long On linux the request argument of ioctl() is defined as an unsigned long, but the fuse protocol squashes it into a signed int. As a consequence the value received by ntfs-3g may appear as negative and different from the value defined by the corresponding macro. So define the request argument as unsigned long in ntfs-3g. It has however to be fed as unsigned from fuse until the fuse protocol is updated. --- include/fuse-lite/fuse.h | 3 +++ include/fuse-lite/fuse_lowlevel.h | 3 +++ include/ntfs-3g/device.h | 3 ++- include/ntfs-3g/ioctl.h | 7 ++++++- libntfs-3g/unix_io.c | 4 ++-- libntfs-3g/win32_io.c | 8 ++++---- src/lowntfs-3g.c | 9 ++++++++- src/ntfs-3g.c | 8 +++++++- 8 files changed, 35 insertions(+), 10 deletions(-) diff --git a/include/fuse-lite/fuse.h b/include/fuse-lite/fuse.h index 2e3d53d8..b4ec2323 100644 --- a/include/fuse-lite/fuse.h +++ b/include/fuse-lite/fuse.h @@ -432,6 +432,9 @@ struct fuse_operations { * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. * * Introduced in version 2.8 + * + * Note : the unsigned long request submitted by the application + * is truncated to 32 bits, and forwarded as a signed int. */ int (*ioctl) (const char *, int cmd, void *arg, struct fuse_file_info *, unsigned int flags, void *data); diff --git a/include/fuse-lite/fuse_lowlevel.h b/include/fuse-lite/fuse_lowlevel.h index 8c84d965..3b10c35d 100644 --- a/include/fuse-lite/fuse_lowlevel.h +++ b/include/fuse-lite/fuse_lowlevel.h @@ -814,6 +814,9 @@ struct fuse_lowlevel_ops { * * Introduced in version 2.8 * + * Note : the unsigned long request submitted by the application + * is truncated to 32 bits, and forwarded as a signed int. + * * Valid replies: * fuse_reply_ioctl_retry * fuse_reply_ioctl diff --git a/include/ntfs-3g/device.h b/include/ntfs-3g/device.h index c7cc9b64..709cb4fa 100644 --- a/include/ntfs-3g/device.h +++ b/include/ntfs-3g/device.h @@ -111,7 +111,8 @@ struct ntfs_device_operations { s64 offset); int (*sync)(struct ntfs_device *dev); int (*stat)(struct ntfs_device *dev, struct stat *buf); - int (*ioctl)(struct ntfs_device *dev, int request, void *argp); + int (*ioctl)(struct ntfs_device *dev, unsigned long request, + void *argp); }; extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state, diff --git a/include/ntfs-3g/ioctl.h b/include/ntfs-3g/ioctl.h index 4ed6c010..2b25c782 100644 --- a/include/ntfs-3g/ioctl.h +++ b/include/ntfs-3g/ioctl.h @@ -24,7 +24,12 @@ #ifndef IOCTL_H #define IOCTL_H -int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg, +/* + * Using an "unsigned long cmd" internally, like in for Linux + * Note however that fuse truncates the arg to 32 bits, and that + * some commands (e.g. FITRIM) do not fit in a signed 32 bit field. + */ +int ntfs_ioctl(ntfs_inode *ni, unsigned long cmd, void *arg, unsigned int flags, void *data); #endif /* IOCTL_H */ diff --git a/libntfs-3g/unix_io.c b/libntfs-3g/unix_io.c index 1103c7e3..5495a6a4 100644 --- a/libntfs-3g/unix_io.c +++ b/libntfs-3g/unix_io.c @@ -369,8 +369,8 @@ static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf) * * Returns: */ -static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request, - void *argp) +static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, + unsigned long request, void *argp) { return ioctl(DEV_FD(dev), request, argp); } diff --git a/libntfs-3g/win32_io.c b/libntfs-3g/win32_io.c index e5cfb640..9c72dee3 100644 --- a/libntfs-3g/win32_io.c +++ b/libntfs-3g/win32_io.c @@ -1847,14 +1847,14 @@ static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp) return -1; } -static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, - void *argp) +static int ntfs_device_win32_ioctl(struct ntfs_device *dev, + unsigned long request, void *argp) { #if defined(BLKGETSIZE) | defined(BLKGETSIZE64) win32_fd *fd = (win32_fd *)dev->d_private; #endif - ntfs_log_trace("win32_ioctl(%d) called.\n", request); + ntfs_log_trace("win32_ioctl(0x%lx) called.\n", request); switch (request) { #if defined(BLKGETSIZE) case BLKGETSIZE: @@ -1897,7 +1897,7 @@ static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, return 0; #endif default: - ntfs_log_debug("unimplemented ioctl %d.\n", request); + ntfs_log_debug("unimplemented ioctl 0x%lx.\n", request); errno = EOPNOTSUPP; return -1; } diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 983ab010..7fa0b195 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3036,7 +3036,14 @@ static void ntfs_fuse_ioctl(fuse_req_t req __attribute__((unused)), } memcpy(buf, data, in_bufsz); } - ret = ntfs_ioctl(ni, cmd, arg, flags, buf); + /* + * Linux defines the request argument of ioctl() to be an + * unsigned long, which fuse 2.x forwards as a signed int + * into which the request sometimes does not fit. + * So we must expand the value and make sure it is not + * sign-extended. + */ + ret = ntfs_ioctl(ni, (unsigned int)cmd, arg, flags, buf); if (ntfs_inode_close (ni)) set_fuse_error(&ret); } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 465f1525..5b3cfc8d 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2777,7 +2777,13 @@ static int ntfs_fuse_ioctl(const char *path, if (!ni) return -errno; - ret = ntfs_ioctl(ni, cmd, arg, flags, data); + /* + * Linux defines the request argument of ioctl() to be an + * unsigned long, which fuse 2.x forwards as a signed int into + * which the request sometimes does not fit. + * So we must expand the value and make sure it is not sign-extended. + */ + ret = ntfs_ioctl(ni, (unsigned int)cmd, arg, flags, data); if (ntfs_inode_close (ni)) set_fuse_error(&ret); From 2c6472ee5a27284c85c00dc2800df2c595c4c924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 10:43:00 +0100 Subject: [PATCH 46/87] Accepted alternative recording of cluster size Since Windows 10, the cluster size may be greater than 128 sectors, and it has to be recorded as a power of 2 in the boot sector. Hence there are two possible ways of cluster size. Accept both ways leading to valid values. --- libntfs-3g/bootsect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index b0ac4d7a..e9eb0b15 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -86,7 +86,7 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) break; default: if ((b->bpb.sectors_per_cluster < 240) - || (b->bpb.sectors_per_cluster > 249)) { + || (b->bpb.sectors_per_cluster > 253)) { if (b->bpb.sectors_per_cluster > 128) ntfs_log_error("Unexpected sectors" " per cluster value (code 0x%x)\n", From 314b5396bdf0329f03976caf4b13d90368de2e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 10:51:53 +0100 Subject: [PATCH 47/87] Fixed returning errno in setxattr In case of error in setxattr, it must be returned in errno instead of the return value. --- libntfs-3g/xattrs.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/libntfs-3g/xattrs.c b/libntfs-3g/xattrs.c index 2b7e7091..4e410de0 100644 --- a/libntfs-3g/xattrs.c +++ b/libntfs-3g/xattrs.c @@ -470,6 +470,15 @@ void ntfs_xattr_free_mapping(struct XATTRMAPPING *mapping) #endif /* XATTR_MAPPINGS */ +/* + * Get an NTFS attribute into an extended attribute + * + * Returns the non-negative size of attribute if successful, + * or negative, with errno set, when fails + * Note : the size is returned even if no buffer is provided + * for returning the attribute, or if it is zero-sized. + */ + int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni, @@ -483,12 +492,6 @@ int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, #endif #endif - /* - * the returned value is the needed - * size. If it is too small, no copy - * is done, and the caller has to - * issue a new call with correct size. - */ switch (attr) { case XATTR_NTFS_ACL : res = ntfs_get_ntfs_acl(scx, ni, value, size); @@ -596,6 +599,13 @@ int ntfs_xattr_system_getxattr(struct SECURITY_CONTEXT *scx, return (res); } +/* + * Set an NTFS attribute from an extended attribute + * + * Returns 0 if successful, + * non-zero, with errno set, when fails + */ + int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, enum SYSTEMXATTRS attr, ntfs_inode *ni, ntfs_inode *dir_ni, @@ -669,8 +679,10 @@ int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, case XATTR_NTFS_EFSINFO : if (ni->vol->efs_raw) res = ntfs_set_efs_info(ni, value, size, flags); - else + else { + errno = EPERM; res = -EPERM; + } break; case XATTR_NTFS_REPARSE_DATA : res = ntfs_set_ntfs_reparse_data(ni, value, size, flags); @@ -683,8 +695,10 @@ int ntfs_xattr_system_setxattr(struct SECURITY_CONTEXT *scx, /* warning : this closes both inodes */ res = ntfs_set_ntfs_dos_name(ni, dir_ni, value, size, flags); - else + else { + errno = EINVAL; res = -errno; + } break; case XATTR_NTFS_TIMES: res = ntfs_inode_set_times(ni, value, size, flags); From aa7af7d53b32b2937a9406d5fa364a660877bc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 11:35:48 +0100 Subject: [PATCH 48/87] Fixed returning EPERM when not allowed as owner For actions which may be allowed depending on the ownership rather than permissions (such as utime()), return EPERM if the owner cannot be determined. --- libntfs-3g/security.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index 1871175a..acee0a5e 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -3048,13 +3048,13 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) free(oldattr); } } - allowed = FALSE; - if (gotowner) { /* TODO : use CAP_FOWNER process capability */ - if (!processuid || (processuid == uid)) - allowed = TRUE; - else - errno = EPERM; + if (gotowner + && (!processuid || (processuid == uid))) + allowed = TRUE; + else { + allowed = FALSE; + errno = EPERM; } } return (allowed); From 39384c8a1b2b6f4357f37d1371b3253483791a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 11:43:49 +0100 Subject: [PATCH 49/87] Disabled the use of cache in lowntfs-3g Following some change in the Linux kernel, the kernel cacheing of attributes is not satisfactory (at least the number of hard links is not refreshed), and has to be disabled. --- include/ntfs-3g/param.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h index c209c53e..a4c5656b 100644 --- a/include/ntfs-3g/param.h +++ b/include/ntfs-3g/param.h @@ -126,8 +126,8 @@ enum { * 7 : no cache, kernel control for ACLs * * Possible values for low level : - * 2 : no cache, kernel control - * 3 : use kernel/fuse cache, kernel control (recommended) + * 2 : no cache, kernel control (recommended) + * 3 : use kernel/fuse cache, kernel control * 5 : no cache, file system control * 6 : kernel/fuse cache, file system control (OpenIndiana only) * 8 : no cache, kernel control for ACLs @@ -150,9 +150,10 @@ enum { * Cacheing by kernel is buggy on Linux when access control is done * by the file system, and also when using hard-linked files on * the fuse high level interface. + * Also ACL checks by recent kernels do not prove satisfactory. */ #define HPERMSCONFIG 1 -#define LPERMSCONFIG 3 /* Use 9 when ACLs are supported by fuse kernel */ +#define LPERMSCONFIG 2 #endif /* defined(__sun) && defined(__SVR4) */ #endif /* defined _NTFS_PARAM_H */ From 688578c1a6da5bc9b45353ffb76bcc5b36c78a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 11:50:26 +0100 Subject: [PATCH 50/87] Removed from lowntfs-3g.c The headers from now conflicts with and are not needed any more, so remove them. --- src/lowntfs-3g.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 7fa0b195..78d6e29c 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -86,10 +86,6 @@ #include #endif /* defined(__APPLE__) || defined(__DARWIN__), ... */ -#ifdef HAVE_LINUX_FS_H -#include -#endif - #ifndef FUSE_CAP_POSIX_ACL /* until defined in */ #define FUSE_CAP_POSIX_ACL (1 << 18) #endif /* FUSE_CAP_POSIX_ACL */ From ef61c82529db5cb53328afceba6d44ff2ee08f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 11:56:46 +0100 Subject: [PATCH 51/87] Avoided using a truncate strncpy() in the fuse library Replace strncpy() by memcpy() when the exact size if known, thus silencing the compiler warnings. --- libfuse-lite/fuse.c | 2 +- libfuse-lite/fuse_lowlevel.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libfuse-lite/fuse.c b/libfuse-lite/fuse.c index 256bed0f..6f9242b7 100644 --- a/libfuse-lite/fuse.c +++ b/libfuse-lite/fuse.c @@ -463,7 +463,7 @@ static char *add_name(char *buf, char *s, const char *name) return NULL; } #endif /* __SOLARIS__ */ - strncpy(s, name, len); + memcpy(s, name, len); s--; *s = '/'; diff --git a/libfuse-lite/fuse_lowlevel.c b/libfuse-lite/fuse_lowlevel.c index fb93b074..a2a58278 100644 --- a/libfuse-lite/fuse_lowlevel.c +++ b/libfuse-lite/fuse_lowlevel.c @@ -247,7 +247,7 @@ char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, dirent->off = off; dirent->namelen = namelen; dirent->type = (stbuf->st_mode & 0170000) >> 12; - strncpy(dirent->name, name, namelen); + memcpy(dirent->name, name, namelen); if (padlen) memset(buf + entlen, 0, padlen); From c5530af50821fa8a55bca624ee8f1e5c9d426e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sat, 7 Mar 2020 12:00:11 +0100 Subject: [PATCH 52/87] Silenced warnings about fallthrough situations in libntfs-3g Insert a comment when fallthrough situations are desired in switch cases. --- libntfs-3g/attrib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 30a8e9cf..7d5d5374 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -6902,7 +6902,9 @@ s64 ntfs_attr_get_free_bits(ntfs_attr *na) } switch (br % 4) { case 3: nr_free += lut[*(buf + br - 3)]; + /* FALLTHRU */ case 2: nr_free += lut[*(buf + br - 2)]; + /* FALLTHRU */ case 1: nr_free += lut[*(buf + br - 1)]; } } From b68c27ea74d5295ea3eac4f066f3fdf86a79399a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 09:29:04 +0100 Subject: [PATCH 53/87] Silenced warnings about string lengths in snprintf() Adjust string lengths to the worst case estimated by the compiler, even though they cannot be reached. --- libntfs-3g/ioctl.c | 3 ++- libntfs-3g/realpath.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c index 6c49395d..66cedaf9 100644 --- a/libntfs-3g/ioctl.c +++ b/libntfs-3g/ioctl.c @@ -141,7 +141,8 @@ static int fstrim_limits(ntfs_volume *vol, u64 *discard_max_bytes) { struct stat statbuf; - char path1[80], path2[80]; + char path1[40]; /* holds "/sys/dev/block/%d:%d" */ + char path2[40 + sizeof(path1)]; /* less than 40 bytes more than path1 */ int ret; /* Stat the backing device. Caller has ensured it is a block device. */ diff --git a/libntfs-3g/realpath.c b/libntfs-3g/realpath.c index a93bc698..d56667e4 100644 --- a/libntfs-3g/realpath.c +++ b/libntfs-3g/realpath.c @@ -45,8 +45,8 @@ canonicalize_dm_name(const char *ptname, char *canonical) { FILE *f; size_t sz; - char path[MAPPERNAMELTH + 24]; char name[MAPPERNAMELTH + 16]; + char path[sizeof(name) + 16]; char *res = NULL; snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); From 004709fcc10685b429c7fc90ad91980aeeb7aa8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 09:38:00 +0100 Subject: [PATCH 54/87] Silenced warnings about fallthrough situations in switch cases of ntfsprogs Insert comments to silence compiler about fallthrough situations when they are wanted. --- ntfsprogs/ntfsclone.c | 1 + ntfsprogs/ntfsdecrypt.c | 4 ++++ ntfsprogs/ntfsfallocate.c | 5 +++++ ntfsprogs/ntfsresize.c | 2 ++ ntfsprogs/ntfsundelete.c | 5 +++++ ntfsprogs/ntfswipe.c | 1 + ntfsprogs/utils.c | 4 ++++ 7 files changed, 22 insertions(+) diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index bbf6d1f0..00876313 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -465,6 +465,7 @@ static void parse_options(int argc, char **argv) break; case 'O': opt.overwrite++; + /* FALLTHRU */ case 'o': if (opt.output) usage(1); diff --git a/ntfsprogs/ntfsdecrypt.c b/ntfsprogs/ntfsdecrypt.c index dbffc946..3fc34312 100644 --- a/ntfsprogs/ntfsdecrypt.c +++ b/ntfsprogs/ntfsdecrypt.c @@ -557,6 +557,7 @@ check_again: switch (err) { case GNUTLS_BAG_PKCS8_KEY: flags = GNUTLS_PKCS_PLAIN; + /* FALLTHRU */ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey); if (err < 0) { @@ -1481,12 +1482,15 @@ static int ntfs_feed_encrypt(ntfs_inode *inode, ntfs_fek *fek) default : *b++ = val; val >>= 8; + /* FALLTHRU */ case 3 : *b++ = val; val >>= 8; + /* FALLTHRU */ case 2 : *b++ = val; val >>= 8; + /* FALLTHRU */ case 1 : *b++ = val; val >>= 8; diff --git a/ntfsprogs/ntfsfallocate.c b/ntfsprogs/ntfsfallocate.c index b0a7c4ec..7a2df7d1 100644 --- a/ntfsprogs/ntfsfallocate.c +++ b/ntfsprogs/ntfsfallocate.c @@ -214,10 +214,15 @@ static s64 option_value(const char *arg) count = 0; switch (*s++) { case 'E' : count++; + /* FALLTHRU */ case 'P' : count++; + /* FALLTHRU */ case 'T' : count++; + /* FALLTHRU */ case 'G' : count++; + /* FALLTHRU */ case 'M' : count++; + /* FALLTHRU */ case 'K' : count++; switch (*s++) { case 'i' : diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index a912f9df..48bb76ee 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -453,8 +453,10 @@ static s64 get_new_volume_size(char *s) switch (*suffix) { case 'G': size *= prefix_kind; + /* FALLTHRU */ case 'M': size *= prefix_kind; + /* FALLTHRU */ case 'k': size *= prefix_kind; break; diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c index 746eac7b..caa1a128 100644 --- a/ntfsprogs/ntfsundelete.c +++ b/ntfsprogs/ntfsundelete.c @@ -567,10 +567,15 @@ static int parse_time(const char *value, time_t *since) switch (suffix[0]) { case 'y': case 'Y': result *= 12; + /* FALLTHRU */ case 'm': case 'M': result *= 4; + /* FALLTHRU */ case 'w': case 'W': result *= 7; + /* FALLTHRU */ case 'd': case 'D': result *= 24; + /* FALLTHRU */ case 'h': case 'H': result *= 3600; + /* FALLTHRU */ case 0: break; diff --git a/ntfsprogs/ntfswipe.c b/ntfsprogs/ntfswipe.c index b41f6168..ef67101e 100644 --- a/ntfsprogs/ntfswipe.c +++ b/ntfsprogs/ntfswipe.c @@ -314,6 +314,7 @@ static int parse_options(int argc, char *argv[]) case 'i': opts.info++; /* and fall through */ + /* FALLTHRU */ case 'a': opts.directory++; opts.logfile++; diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c index 45a03a4f..393c34a4 100644 --- a/ntfsprogs/utils.c +++ b/ntfsprogs/utils.c @@ -364,9 +364,13 @@ int utils_parse_size(const char *value, s64 *size, BOOL scale) if (scale) { switch (suffix[0]) { case 't': case 'T': result *= 1000; + /* FALLTHRU */ case 'g': case 'G': result *= 1000; + /* FALLTHRU */ case 'm': case 'M': result *= 1000; + /* FALLTHRU */ case 'k': case 'K': result *= 1000; + /* FALLTHRU */ case '-': case 0: break; default: From 8e01e1ed65c9463884fe0bdd517798f883807af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 09:42:15 +0100 Subject: [PATCH 55/87] Fixed a wrong fallthrough situation in ntfscat A fallthrough situation was not intentional. --- ntfsprogs/ntfscat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ntfsprogs/ntfscat.c b/ntfsprogs/ntfscat.c index 410c7dce..b8af2503 100644 --- a/ntfsprogs/ntfscat.c +++ b/ntfsprogs/ntfscat.c @@ -236,6 +236,7 @@ static int parse_options(int argc, char **argv) optarg); usage(); } + break; case 'q': opts.quiet++; From eddd96f9e600994a8c2459daf99386fb63824566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 09:44:52 +0100 Subject: [PATCH 56/87] Fixed a poorly sized string in ntfsinfo A string overflow was possible, extend its size to worst case. --- ntfsprogs/ntfsinfo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 50fbf431..676fa20b 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -8,7 +8,7 @@ * Copyright (c) 2004-2005 Yuval Fledel * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2005 Cristian Klein - * Copyright (c) 2011-2018 Jean-Pierre Andre + * Copyright (c) 2011-2020 Jean-Pierre Andre * * This utility will dump a file's attributes. * @@ -331,7 +331,7 @@ static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock) "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" } ; static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ; - static char str[30]; + static char str[50]; long long stamp; u32 days; u32 seconds; @@ -371,7 +371,7 @@ static char *ntfsinfo_time_to_str(const sle64 sle_ntfs_clock) mon = days/31 + 1; days -= 31*(mon - 1) - 1; } - sprintf(str,"%3s %3s %2u %02u:%02u:%02u %4u UTC\n", + snprintf(str, sizeof(str), "%3s %3s %2u %02u:%02u:%02u %4u UTC\n", wdays[wday], months[mon-1],(unsigned int)days, (unsigned int)(seconds/3600), From 4c8d97cbd27db4f415f7bfde5474effcdedc27e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 09:51:48 +0100 Subject: [PATCH 57/87] Fixed ntfsfallocate on a void file The situation when there were no runlist when ntfsfallocate is applied was not taken into account. --- ntfsprogs/ntfsfallocate.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ntfsprogs/ntfsfallocate.c b/ntfsprogs/ntfsfallocate.c index 7a2df7d1..d857922f 100644 --- a/ntfsprogs/ntfsfallocate.c +++ b/ntfsprogs/ntfsfallocate.c @@ -1,7 +1,7 @@ /** * ntfsfallocate * - * Copyright (c) 2013-2014 Jean-Pierre Andre + * Copyright (c) 2013-2020 Jean-Pierre Andre * * This utility will allocate clusters to a specified attribute belonging * to a specified file or directory, to a specified length. @@ -775,10 +775,14 @@ static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len) /* Get and save the initial allocations */ allocated_size = na->allocated_size; data_size = ni->data_size; - err = ntfs_attr_map_whole_runlist(na); + if (na->rl) + err = ntfs_attr_map_whole_runlist(na); if (!err) { - oldrl = ntfs_save_rl(na->rl); - if (oldrl) { + if (na->rl) + oldrl = ntfs_save_rl(na->rl); + else + oldrl = (runlist_element*)NULL; + if (!na->rl || oldrl) { err = ntfs_full_allocation(na, ctx, alloc_offs, alloc_len); if (err) { From aecd9f011ef256eee2c86680f0667780679d0ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 09:57:27 +0100 Subject: [PATCH 58/87] Decoded execlink reparse data Add execlink to the list of recognized reparse tags. --- include/ntfs-3g/layout.h | 1 + ntfsprogs/ntfsinfo.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 754d66e9..6d4d589a 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -2443,6 +2443,7 @@ typedef enum { IO_REPARSE_TAG_WOF = const_cpu_to_le32(0x80000017), IO_REPARSE_TAG_WCI = const_cpu_to_le32(0x80000018), IO_REPARSE_TAG_CLOUD = const_cpu_to_le32(0x9000001A), + IO_REPARSE_TAG_APPEXECLINK = const_cpu_to_le32(0x8000001B), IO_REPARSE_TAG_GVFS = const_cpu_to_le32(0x9000001C), IO_REPARSE_TAG_LX_SYMLINK = const_cpu_to_le32(0xA000001D), diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 676fa20b..0a6364c7 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -439,6 +439,9 @@ static const char *reparse_type_name(le32 tag) case IO_REPARSE_TAG_LX_SYMLINK : name = " (Linux symlink)"; break; + case IO_REPARSE_TAG_APPEXECLINK : + name = " (Exec link)"; + break; default : name = ""; break; From 7cd46f95dfa3872536fe98a4e158fdc6ab212b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 10:03:23 +0100 Subject: [PATCH 59/87] Fixed object types returned in readdir() for reparse points The types of reparse point objects cannot be decided upon the data available in a directory, so we must delegate their determination to a specific plugin when available, and be consistent if there is none. --- include/ntfs-3g/dir.h | 1 + libntfs-3g/dir.c | 8 ++++---- src/lowntfs-3g.c | 27 +++++++++++++++++++++++++-- src/ntfs-3g.c | 27 +++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index e6a2c3b1..ced83f43 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -94,6 +94,7 @@ extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, #define NTFS_DT_LNK 10 #define NTFS_DT_SOCK 12 #define NTFS_DT_WHT 14 +#define NTFS_DT_REPARSE 32 /* * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index a66f807f..0721e782 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2008-2014 Jean-Pierre Andre + * Copyright (c) 2008-2020 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -934,9 +934,9 @@ static u32 ntfs_dir_entry_type(ntfs_inode *dir_ni, MFT_REF mref, dt_type = NTFS_DT_UNKNOWN; ni = ntfs_inode_open(dir_ni->vol, mref); if (ni) { - if ((attributes & FILE_ATTR_REPARSE_POINT) - && ntfs_possible_symlink(ni)) - dt_type = NTFS_DT_LNK; + if (attributes & FILE_ATTR_REPARSE_POINT) + dt_type = (ntfs_possible_symlink(ni) + ? NTFS_DT_LNK : NTFS_DT_REPARSE); else if ((attributes & FILE_ATTR_SYSTEM) && !(attributes & FILE_ATTR_I30_INDEX_PRESENT)) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 78d6e29c..b0ef839d 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2019 Jean-Pierre Andre + * Copyright (c) 2007-2020 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -259,7 +259,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2019 Jean-Pierre Andre\n" +"Copyright (C) 2007-2020 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -1167,6 +1167,9 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, /* never return inodes 0 and 1 */ if (MREF(mref) > 1) { struct stat st = { .st_ino = MREF(mref) }; +#ifndef DISABLE_PLUGINS + ntfs_inode *ni; +#endif /* DISABLE_PLUGINS */ switch (dt_type) { case NTFS_DT_DIR : @@ -1187,6 +1190,26 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, case NTFS_DT_CHR : st.st_mode = S_IFCHR; break; + case NTFS_DT_REPARSE : + st.st_mode = S_IFLNK | 0777; /* default */ +#ifndef DISABLE_PLUGINS + /* get emulated type from plugin if available */ + ni = ntfs_inode_open(ctx->vol, mref); + if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) { + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + int res; + + res = CALL_REPARSE_PLUGIN(ni, getattr, &st); + if (!res) + apply_umask(&st); + else + st.st_mode = S_IFLNK; + } + if (ni) + ntfs_inode_close(ni); +#endif /* DISABLE_PLUGINS */ + break; default : /* unexpected types shown as plain files */ case NTFS_DT_REG : st.st_mode = S_IFREG | (0777 & ~ctx->fmask); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 5b3cfc8d..cacb1b92 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2019 Jean-Pierre Andre + * Copyright (c) 2007-2020 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -196,7 +196,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2019 Jean-Pierre Andre\n" +"Copyright (C) 2007-2020 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -1199,6 +1199,9 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, return 0; } else { struct stat st = { .st_ino = MREF(mref) }; +#ifndef DISABLE_PLUGINS + ntfs_inode *ni; +#endif /* DISABLE_PLUGINS */ switch (dt_type) { case NTFS_DT_DIR : @@ -1219,6 +1222,26 @@ static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, case NTFS_DT_CHR : st.st_mode = S_IFCHR; break; + case NTFS_DT_REPARSE : + st.st_mode = S_IFLNK | 0777; /* default */ +#ifndef DISABLE_PLUGINS + /* get emulated type from plugin if available */ + ni = ntfs_inode_open(ctx->vol, mref); + if (ni && (ni->flags & FILE_ATTR_REPARSE_POINT)) { + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + int res; + + res = CALL_REPARSE_PLUGIN(ni, getattr, &st); + if (!res) + apply_umask(&st); + else + st.st_mode = S_IFLNK; + } + if (ni) + ntfs_inode_close(ni); +#endif /* DISABLE_PLUGINS */ + break; default : /* unexpected types shown as plain files */ case NTFS_DT_REG : st.st_mode = S_IFREG | (0777 & ~ctx->fmask); From 3f65ccd9496451397f92e537a0d3defd97e854da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 10:17:21 +0100 Subject: [PATCH 60/87] Exported the translations of Windows paths The translations of Windows paths designed for translating Windows symlinks and mount points may also be used in plugins for translation execlinks, so make them available. --- include/ntfs-3g/reparse.h | 3 +++ include/ntfs-3g/volume.h | 2 +- libntfs-3g/reparse.c | 2 +- src/lowntfs-3g.c | 1 + src/ntfs-3g.c | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 76af9150..9109762b 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -30,6 +30,9 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni); int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); +char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, + int count, const char *mnt_point, BOOL isdir); + REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni); int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index cee91e21..bbb61203 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -274,7 +274,7 @@ struct _ntfs_volume { #if CACHE_LEGACY_SIZE struct CACHE_HEADER *legacy_cache; #endif - + const char *abs_mnt_point; /* Mount point */ }; extern const char *ntfs_home; diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index 2e92fbb6..c1d226be 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -604,7 +604,7 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, * or NULL if there were some problem, as described by errno */ -static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, +char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, int count, const char *mnt_point, BOOL isdir) { char *target; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index b0ef839d..d005e683 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4516,6 +4516,7 @@ int main(int argc, char *argv[]) if (ctx->blkdev && set_fuseblk_options(&parsed_options)) goto err_out; + ctx->vol->abs_mnt_point = ctx->abs_mnt_point; ctx->security.vol = ctx->vol; ctx->vol->secure_flags = ctx->secure_flags; #ifdef HAVE_SETXATTR /* extended attributes interface required */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index cacb1b92..b5d425e4 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4257,6 +4257,7 @@ int main(int argc, char *argv[]) if (ctx->blkdev && set_fuseblk_options(&parsed_options)) goto err_out; + ctx->vol->abs_mnt_point = ctx->abs_mnt_point; ctx->security.vol = ctx->vol; ctx->vol->secure_flags = ctx->secure_flags; #ifdef HAVE_SETXATTR /* extended attributes interface required */ From 4163390f2bcc41307aa583ca533491bfce483951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Sun, 8 Mar 2020 10:34:17 +0100 Subject: [PATCH 61/87] Fixed defining the request argument of ioctl() An occurrence of changing the request from int to unsigned long was missing. --- libntfs-3g/ioctl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c index 66cedaf9..b059a53f 100644 --- a/libntfs-3g/ioctl.c +++ b/libntfs-3g/ioctl.c @@ -389,7 +389,8 @@ free_out: #endif /* FITRIM && BLKDISCARD */ -int ntfs_ioctl(ntfs_inode *ni, int cmd, void *arg __attribute__((unused)), +int ntfs_ioctl(ntfs_inode *ni, unsigned long cmd, + void *arg __attribute__((unused)), unsigned int flags __attribute__((unused)), void *data) { int ret = 0; From 5d6fd5c2d25d0807cf23968c63f255df105d4dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 14 Aug 2020 11:27:24 +0200 Subject: [PATCH 62/87] Used kernel cacheing on read-only mounts or with lowntfs-3g Kernel cacheing is now safe when using lowntfs-3g on Linux. It is also safe on read-only mounts with both ntfs-3g and lowntfs-3g. --- include/ntfs-3g/param.h | 10 +++++++--- src/lowntfs-3g.c | 18 ++++++++++-------- src/ntfs-3g.c | 23 +++++++++++++++++++---- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/include/ntfs-3g/param.h b/include/ntfs-3g/param.h index a4c5656b..ebc8a0ae 100644 --- a/include/ntfs-3g/param.h +++ b/include/ntfs-3g/param.h @@ -119,6 +119,9 @@ enum { * having access control in the file system leads to fewer requests * to the file system and fewer context switches. * + * Irrespective of the selected mode, cacheing is always used + * in read-only mounts + * * Possible values for high level : * 1 : no cache, kernel control (recommended) * 4 : no cache, file system control @@ -126,8 +129,8 @@ enum { * 7 : no cache, kernel control for ACLs * * Possible values for low level : - * 2 : no cache, kernel control (recommended) - * 3 : use kernel/fuse cache, kernel control + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control (recommended) * 5 : no cache, file system control * 6 : kernel/fuse cache, file system control (OpenIndiana only) * 8 : no cache, kernel control for ACLs @@ -138,6 +141,7 @@ enum { * of 6 is added in the mount report. */ +#define TIMEOUT_RO 600 /* Attribute time out for read-only mounts */ #if defined(__sun) && defined(__SVR4) /* * Access control by kernel is not implemented on OpenIndiana, @@ -153,7 +157,7 @@ enum { * Also ACL checks by recent kernels do not prove satisfactory. */ #define HPERMSCONFIG 1 -#define LPERMSCONFIG 2 +#define LPERMSCONFIG 3 #endif /* defined(__sun) && defined(__SVR4) */ #endif /* defined _NTFS_PARAM_H */ diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index d005e683..04fbef65 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -129,12 +129,12 @@ #endif #if !CACHEING -#define ATTR_TIMEOUT 0.0 -#define ENTRY_TIMEOUT 0.0 +#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : 0.0) +#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : 0.0) #else #if defined(__sun) && defined (__SVR4) -#define ATTR_TIMEOUT 10.0 -#define ENTRY_TIMEOUT 10.0 +#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0) +#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0) #else /* defined(__sun) && defined (__SVR4) */ /* * FUSE cacheing is only usable with basic permissions @@ -144,11 +144,13 @@ #warning "Fuse cacheing is only usable with basic permissions checked by kernel" #endif #if KERNELACLS -#define ATTR_TIMEOUT 10.0 -#define ENTRY_TIMEOUT 10.0 +#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0) +#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : 10.0) #else /* KERNELACLS */ -#define ATTR_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0) -#define ENTRY_TIMEOUT (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0) +#define ATTR_TIMEOUT (ctx->ro ? TIMEOUT_RO : \ + (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0)) +#define ENTRY_TIMEOUT (ctx->ro ? TIMEOUT_RO : \ + (ctx->vol->secure_flags & (1 << SECURITY_DEFAULT) ? 10.0 : 0.0)) #endif /* KERNELACLS */ #endif /* defined(__sun) && defined (__SVR4) */ #endif /* !CACHEING */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index b5d425e4..eb91797e 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4054,13 +4054,28 @@ static struct fuse *mount_fuse(char *parsed_options) if (fuse_opt_add_arg(&args, "") == -1) goto err; + if (ctx->ro) { + char buf[128]; + int len; + + len = snprintf(buf, sizeof(buf), "-ouse_ino,kernel_cache" + ",attr_timeout=%d,entry_timeout=%d", + (int)TIMEOUT_RO, (int)TIMEOUT_RO); + if ((len < 0) + || (len >= (int)sizeof(buf)) + || (fuse_opt_add_arg(&args, buf) == -1)) + goto err; + } else { #if !CACHEING - if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=0") == -1) - goto err; + if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache" + ",attr_timeout=0") == -1) + goto err; #else - if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache,attr_timeout=1") == -1) - goto err; + if (fuse_opt_add_arg(&args, "-ouse_ino,kernel_cache" + ",attr_timeout=1") == -1) + goto err; #endif + } if (ctx->debug) if (fuse_opt_add_arg(&args, "-odebug") == -1) goto err; From 6bdd1e85ac15f287f13305204c7f5b7af032a497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 14 Aug 2020 11:32:50 +0200 Subject: [PATCH 63/87] Displayed the plugin path in the basic help message The ntfs-3g plugin directory depends on the distribution and may be difficult to determine. This displays the directory in the basic help. --- src/lowntfs-3g.c | 5 ++++- src/ntfs-3g.c | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 04fbef65..a58c1c1a 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -270,8 +270,11 @@ static const char *usage_msg = " umask=, fmask=, dmask=, streams_interface=.\n" " Please see the details in the manual (type: man ntfs-3g).\n" "\n" -"Example: ntfs-3g /dev/sda1 /mnt/windows\n" +"Example: lowntfs-3g /dev/sda1 /mnt/windows\n" "\n" +#ifdef PLUGIN_DIR +"Plugin path: " PLUGIN_DIR "\n\n" +#endif /* PLUGIN_DIR */ "%s"; static const char ntfs_bad_reparse[] = "unsupported reparse point"; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index eb91797e..fdb42948 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -207,6 +207,9 @@ static const char *usage_msg = "\n" "Example: ntfs-3g /dev/sda1 /mnt/windows\n" "\n" +#ifdef PLUGIN_DIR +"Plugin path: " PLUGIN_DIR "\n\n" +#endif /* PLUGIN_DIR */ "%s"; static const char ntfs_bad_reparse[] = "unsupported reparse point"; From 1bc996f52f1291896177daee15ca9ee5fa2c928d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 14 Aug 2020 11:36:57 +0200 Subject: [PATCH 64/87] Avoided information leak when processing garbled compressed data When a compressed file has been deteriorated through hardware error or accidental overwriting, some unrelated data could be leaked. Make sure to zero fill the buffer when this happens. --- libntfs-3g/compress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c index 2c0c260a..fb69a0e6 100644 --- a/libntfs-3g/compress.c +++ b/libntfs-3g/compress.c @@ -491,6 +491,8 @@ do_next_sb: * first two checks do not detect it. */ if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) { + if (dest_end > dest) + memset(dest, 0, dest_end - dest); ntfs_log_debug("Completed. Returning success (0).\n"); return 0; } From d6558f1dea374d4b47b323c02b7ae0ffad5d5c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 14 Aug 2020 11:50:35 +0200 Subject: [PATCH 65/87] Defined option "posix_nlink" to compute a Posix compliant st_nlink When the mount option "posix_nlink" is used, the number of links returned by stat complies with Posix : the legacy 8.3 names are not taken into account, and the subdirectories are taken into account for directories. This causes some overhead for recomputing the number of links. --- include/ntfs-3g/dir.h | 1 + libntfs-3g/dir.c | 79 +++++++++++++++++++++++++++++++++++++++++++ src/lowntfs-3g.c | 6 +++- src/ntfs-3g.8.in | 7 ++++ src/ntfs-3g.c | 6 +++- src/ntfs-3g_common.c | 6 +++- src/ntfs-3g_common.h | 2 ++ 7 files changed, 104 insertions(+), 3 deletions(-) diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index ced83f43..60169151 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -117,6 +117,7 @@ int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, const char *value, size_t size, int flags); int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); +int ntfs_dir_link_cnt(ntfs_inode *ni); #if CACHE_INODE_SIZE diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 0721e782..5e5baf62 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -2792,3 +2792,82 @@ int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) } return (res); } + +/* + * Increment the count of subdirectories + * (excluding entries with a short name) + */ + +static int nlink_increment(void *nlink_ptr, + const ntfschar *name __attribute__((unused)), + const int len __attribute__((unused)), + const int type, + const s64 pos __attribute__((unused)), + const MFT_REF mref __attribute__((unused)), + const unsigned int dt_type) +{ + if ((dt_type == NTFS_DT_DIR) && (type != FILE_NAME_DOS)) + (*((int*)nlink_ptr))++; + return (0); +} + +/* + * Compute the number of hard links according to Posix + * For a directory count the subdirectories whose name is not + * a short one, but count "." and ".." + * Otherwise count the names, excluding the short ones. + * + * if there is an error, a null count is returned. + */ + +int ntfs_dir_link_cnt(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *actx; + FILE_NAME_ATTR *fn; + s64 pos; + int err = 0; + int nlink = 0; + + if (!ni) { + ntfs_log_error("Invalid argument.\n"); + errno = EINVAL; + goto err_out; + } + if (ni->nr_extents == -1) + ni = ni->base_ni; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + /* + * Directory : scan the directory and count + * subdirectories whose name is not DOS-only. + * The directory names are ignored, but "." and ".." + * are taken into account. + */ + pos = 0; + err = ntfs_readdir(ni, &pos, &nlink, nlink_increment); + if (err) + nlink = 0; + } else { + /* + * Non-directory : search for FILE_NAME attributes, + * and count those which are not DOS-only ones. + */ + actx = ntfs_attr_get_search_ctx(ni, NULL); + if (!actx) + goto err_out; + while (!(err = ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, actx))) { + fn = (FILE_NAME_ATTR*)((u8*)actx->attr + + le16_to_cpu(actx->attr->value_offset)); + if (fn->file_name_type != FILE_NAME_DOS) + nlink++; + } + if (err && (errno != ENOENT)) + nlink = 0; + ntfs_attr_put_search_ctx(actx); + } + if (!nlink) + ntfs_log_perror("Failed to compute nlink of inode %lld", + (long long)ni->mft_no); +err_out : + return (nlink); +} diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index a58c1c1a..7d5c3e8f 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -708,6 +708,9 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, memset(stbuf, 0, sizeof(struct stat)); withusermapping = (scx->mapping[MAPUSERS] != (struct MAPPING*)NULL); stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); + if (ctx->posix_nlink + && !(ni->flags & FILE_ATTR_REPARSE_POINT)) + stbuf->st_nlink = ntfs_dir_link_cnt(ni); if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) || (ni->flags & FILE_ATTR_REPARSE_POINT)) { if (ni->flags & FILE_ATTR_REPARSE_POINT) { @@ -770,7 +773,8 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, } stbuf->st_size = ni->data_size; stbuf->st_blocks = ni->allocated_size >> 9; - stbuf->st_nlink = 1; /* Make find(1) work */ + if (!ctx->posix_nlink) + stbuf->st_nlink = 1; /* Make find(1) work */ } } else { /* Regular or Interix (INTX) file. */ diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 2f2e8ca2..2d18fb4b 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -250,6 +250,13 @@ they do not appear in Windows directory displays either. When a file is renamed or linked with a new name, the hidden flag is adjusted to the latest name. .TP +.B posix_nlink +Compute the count of hard links of a file or directory according to +the Posix specifications. When this option is not set, a count of 1 +is set for directories, and the short name of files is accounted for. +Using the option entails some penalty as the count is not stored and +has to be computed. +.TP .B windows_names This option prevents files, directories and extended attributes to be created with a name not allowed by windows, because diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index fdb42948..2fd98285 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -790,6 +790,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) } #endif stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); + if (ctx->posix_nlink + && !(ni->flags & FILE_ATTR_REPARSE_POINT)) + stbuf->st_nlink = ntfs_dir_link_cnt(ni); if (((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) || (ni->flags & FILE_ATTR_REPARSE_POINT)) @@ -852,7 +855,8 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) } stbuf->st_size = ni->data_size; stbuf->st_blocks = ni->allocated_size >> 9; - stbuf->st_nlink = 1; /* Make find(1) work */ + if (!ctx->posix_nlink) + stbuf->st_nlink = 1; /* Make find(1) work */ } } else { /* Regular or Interix (INTX) file. */ diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index a8ea6385..397b72a0 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -1,7 +1,7 @@ /** * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g. * - * Copyright (c) 2010-2019 Jean-Pierre Andre + * Copyright (c) 2010-2020 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -126,6 +126,7 @@ const struct DEFOPTION optionlist[] = { { "usermapping", OPT_USERMAPPING, FLGOPT_STRING }, { "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING }, { "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS }, + { "posix_nlink", OPT_POSIX_NLINK, FLGOPT_BOGUS }, { (const char*)NULL, 0, 0 } /* end marker */ } ; @@ -492,6 +493,9 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx, ctx->efs_raw = TRUE; break; #endif /* HAVE_SETXATTR */ + case OPT_POSIX_NLINK : + ctx->posix_nlink = TRUE; + break; case OPT_FSNAME : /* Filesystem name. */ /* * We need this to be able to check whether filesystem diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index bcffe4f7..8c2ecf39 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -92,6 +92,7 @@ enum { OPT_USERMAPPING, OPT_XATTRMAPPING, OPT_EFS_RAW, + OPT_POSIX_NLINK, } ; /* Option flags */ @@ -153,6 +154,7 @@ typedef struct { BOOL no_detach; BOOL blkdev; BOOL mounted; + BOOL posix_nlink; #ifdef HAVE_SETXATTR /* extended attributes interface required */ BOOL efs_raw; #ifdef XATTR_MAPPINGS From cd68a084fce98e78e1fc56c232e714da8263a4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 14 Aug 2020 11:57:02 +0200 Subject: [PATCH 66/87] Recovered space when an index root is shortened In rare situations, removing an entry from an index root while rebalancing the index tree, its space was not recovered from the index root, causing chkdsk to complain. Truncate the index root when this happens. --- libntfs-3g/index.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 58568ded..6fc79d5d 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy * Copyright (c) 2005-2008 Szabolcs Szakacsits - * Copyright (c) 2007 Jean-Pierre Andre + * Copyright (c) 2007-2020 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -1563,19 +1563,32 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_BLOCK *ib) { INDEX_ENTRY *ie_roam; + int freed_space; + BOOL full; int ret = STATUS_ERROR; ntfs_log_trace("Entering\n"); + full = ih->index_length == ih->allocated_size; ie_roam = ntfs_ie_dup_novcn(ie); if (!ie_roam) return STATUS_ERROR; ntfs_ie_delete(ih, ie); - if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) + if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) { + /* + * Recover the space which may have been freed + * while deleting an entry from root index + */ + freed_space = le32_to_cpu(ih->allocated_size) + - le32_to_cpu(ih->index_length); + if (full && (freed_space > 0) && !(freed_space & 7)) { + ntfs_ir_truncate(icx, cpu_to_le32(ih->index_length)); + /* do nothing if truncation fails */ + } ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - else + } else if (ntfs_ib_write(icx, ib)) goto out; From 39579a045da3838a3bc0b443163fbcf5100ae7f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Fri, 14 Aug 2020 12:03:09 +0200 Subject: [PATCH 67/87] Fixed possible null dereferencings A null directory pointer could appear as being dereferenced. Fix that, though it probably never occurs in real conditions. --- src/ntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 2fd98285..6e0d688c 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2026,7 +2026,7 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev, if (!dir_ni || (dir_ni->mft_no == FILE_Extend)) { free(path); res = -errno; - if (dir_ni->mft_no == FILE_Extend) + if (dir_ni) res = -EPERM; goto exit; } @@ -2373,7 +2373,7 @@ static int ntfs_fuse_rm(const char *org_path) /* deny unlinking metadata files from $Extend */ if (!dir_ni || (dir_ni->mft_no == FILE_Extend)) { res = -errno; - if (dir_ni->mft_no == FILE_Extend) + if (dir_ni) res = -EPERM; goto exit; } From 4ecc13c0acdb12b0a4bf22d0077406e0840679db Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Mon, 24 Aug 2020 16:50:18 +0300 Subject: [PATCH 68/87] Replace ENODATA with ENOATTR in xattrs functions for macOS builds. The contract in macOS xattr functions is to return ENOATTR when an extended attribute cannot be found. --- include/ntfs-3g/compat.h | 10 ++++++++++ src/lowntfs-3g.c | 10 +++++----- src/ntfs-3g.c | 10 +++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/include/ntfs-3g/compat.h b/include/ntfs-3g/compat.h index fa57a38a..288a86a2 100644 --- a/include/ntfs-3g/compat.h +++ b/include/ntfs-3g/compat.h @@ -46,6 +46,16 @@ #define ELIBACC ENOENT #endif +/* xattr APIs in macOS differs from Linux ones in that they expect the special + * error code ENOATTR to be returned when an attribute cannot be found. So + * define NTFS_NOXATTR_ERRNO to the appropriate "no xattr found" errno value for + * the platform. */ +#if defined(__APPLE__) || defined(__DARWIN__) +#define NTFS_NOXATTR_ERRNO ENOATTR +#else +#define NTFS_NOXATTR_ERRNO ENODATA +#endif + #ifndef PATH_MAX #define PATH_MAX 4096 #endif diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 7d5c3e8f..1e7098ef 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3465,7 +3465,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, /* trusted only readable by root */ if ((namespace == XATTRNS_TRUSTED) && security.uid) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto out; } #endif @@ -3476,7 +3476,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } /* Return with no result for symlinks, fifo, etc. */ if (!user_xattrs_allowed(ctx, ni)) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto exit; } /* otherwise file must be readable */ @@ -3493,7 +3493,7 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); if (!na) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto exit; } rsize = na->data_size; @@ -3704,7 +3704,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } if (!na) { if (flags == XATTR_REPLACE) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto exit; } if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) { @@ -3942,7 +3942,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na } if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) { if (errno == ENOENT) - errno = ENODATA; + errno = NTFS_NOXATTR_ERRNO; res = -errno; } if (!res) { diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 6e0d688c..d3537660 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -3236,14 +3236,14 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, /* trusted only readable by root */ if ((namespace == XATTRNS_TRUSTED) && security.uid) - return -ENODATA; + return -NTFS_NOXATTR_ERRNO; #endif ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; /* Return with no result for symlinks, fifo, etc. */ if (!user_xattrs_allowed(ctx, ni)) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto exit; } /* otherwise file must be readable */ @@ -3260,7 +3260,7 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, } na = ntfs_attr_open(ni, AT_DATA, lename, lename_len); if (!na) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto exit; } rsize = na->data_size; @@ -3450,7 +3450,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, } if (!na) { if (flags == XATTR_REPLACE) { - res = -ENODATA; + res = -NTFS_NOXATTR_ERRNO; goto exit; } if (ntfs_attr_add(ni, AT_DATA, lename, lename_len, NULL, 0)) { @@ -3680,7 +3680,7 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) } if (ntfs_attr_remove(ni, AT_DATA, lename, lename_len)) { if (errno == ENOENT) - errno = ENODATA; + errno = NTFS_NOXATTR_ERRNO; res = -errno; } if (!res) { From 1511a5ca51ba52b3b27bc4f00210f9e7f28f72c3 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Tue, 25 Aug 2020 11:26:29 +0300 Subject: [PATCH 69/87] Add support for 'position' argument in macOS xattr functions. The 'position' argument is only used for the legacy resource fork and is disallowed for other extended attributes. The name check is placed first in the functions as this is how macOS behaves (EINVAL is returned if the attribute is not the resource fork attribute and the position is non-0 even when the attribute does not exist). --- src/lowntfs-3g.c | 55 +++++++++++++++++++++++++++++++++++++++++++++--- src/ntfs-3g.c | 52 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 1e7098ef..f78cb9c8 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3373,9 +3373,18 @@ out : free(list); } +#if defined(__APPLE__) || defined(__DARWIN__) +static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size, uint32_t position) +#else static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) +#endif { +#if !(defined(__APPLE__) || defined(__DARWIN__)) + static const unsigned int position = 0U; +#endif + ntfs_inode *ni; ntfs_inode *dir_ni; ntfs_attr *na = NULL; @@ -3388,6 +3397,16 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, int namespace; struct SECURITY_CONTEXT security; +#if defined(__APPLE__) || defined(__DARWIN__) + /* If the attribute is not a resource fork attribute and the position + * parameter is non-zero, we return with EINVAL as requesting position + * is not permitted for non-resource fork attributes. */ + if (position && strcmp(name, XATTR_RESOURCEFORK_NAME)) { + fuse_reply_err(req, EINVAL); + return; + } +#endif + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { /* @@ -3502,11 +3521,13 @@ static void ntfs_fuse_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, && (na->data_flags & ATTR_IS_ENCRYPTED) && NAttrNonResident(na)) rsize = ((na->data_size + 511) & ~511) + 2; + rsize -= position; if (size) { if (size >= (size_t)rsize) { value = (char*)ntfs_malloc(rsize); if (value) - res = ntfs_attr_pread(na, 0, rsize, value); + res = ntfs_attr_pread(na, position, rsize, + value); if (!value || (res != rsize)) res = -errno; } else @@ -3531,9 +3552,21 @@ out : free(value); } +#if defined(__APPLE__) || defined(__DARWIN__) +static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags, + uint32_t position) +#else static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags) +#endif { +#if !(defined(__APPLE__) || defined(__DARWIN__)) + static const unsigned int position = 0U; +#else + BOOL is_resource_fork; +#endif + ntfs_inode *ni; ntfs_inode *dir_ni; ntfs_attr *na = NULL; @@ -3545,6 +3578,17 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, int namespace; struct SECURITY_CONTEXT security; +#if defined(__APPLE__) || defined(__DARWIN__) + /* If the attribute is not a resource fork attribute and the position + * parameter is non-zero, we return with EINVAL as requesting position + * is not permitted for non-resource fork attributes. */ + is_resource_fork = strcmp(name, XATTR_RESOURCEFORK_NAME) ? FALSE : TRUE; + if (position && !is_resource_fork) { + fuse_reply_err(req, EINVAL); + return; + } +#endif + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { /* @@ -3720,6 +3764,11 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, res = -errno; goto exit; } +#if defined(__APPLE__) || defined(__DARWIN__) + } else if (is_resource_fork) { + /* In macOS, the resource fork is a special case. It doesn't + * ever shrink (it would have to be removed and re-added). */ +#endif } else { /* currently compressed streams can only be wiped out */ if (ntfs_attr_truncate(na, (s64)0 /* size */)) { @@ -3731,8 +3780,8 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, res = 0; if (size) { do { - part = ntfs_attr_pwrite(na, total, size - total, - &value[total]); + part = ntfs_attr_pwrite(na, position + total, + size - total, &value[total]); if (part > 0) total += part; } while ((part > 0) && (total < size)); diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index d3537660..2eb197a8 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -3151,9 +3151,18 @@ exit: return ret; } +#if defined(__APPLE__) || defined(__DARWIN__) +static int ntfs_fuse_getxattr(const char *path, const char *name, + char *value, size_t size, uint32_t position) +#else static int ntfs_fuse_getxattr(const char *path, const char *name, char *value, size_t size) +#endif { +#if !(defined(__APPLE__) || defined(__DARWIN__)) + static const unsigned int position = 0U; +#endif + ntfs_inode *ni; ntfs_inode *dir_ni; ntfs_attr *na = NULL; @@ -3164,6 +3173,15 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, int namespace; struct SECURITY_CONTEXT security; +#if defined(__APPLE__) || defined(__DARWIN__) + /* If the attribute is not a resource fork attribute and the position + * parameter is non-zero, we return with EINVAL as requesting position + * is not permitted for non-resource fork attributes. */ + if (position && strcmp(name, XATTR_RESOURCEFORK_NAME)) { + return -EINVAL; + } +#endif + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) @@ -3269,9 +3287,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, && (na->data_flags & ATTR_IS_ENCRYPTED) && NAttrNonResident(na)) rsize = ((na->data_size + 511) & ~511) + 2; + rsize -= position; if (size) { if (size >= (size_t)rsize) { - res = ntfs_attr_pread(na, 0, rsize, value); + res = ntfs_attr_pread(na, position, rsize, value); if (res != rsize) res = -errno; } else @@ -3287,9 +3306,21 @@ exit: return res; } +#if defined(__APPLE__) || defined(__DARWIN__) +static int ntfs_fuse_setxattr(const char *path, const char *name, + const char *value, size_t size, int flags, + uint32_t position) +#else static int ntfs_fuse_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) +#endif { +#if !(defined(__APPLE__) || defined(__DARWIN__)) + static const unsigned int position = 0U; +#else + BOOL is_resource_fork; +#endif + ntfs_inode *ni; ntfs_inode *dir_ni; ntfs_attr *na = NULL; @@ -3301,6 +3332,16 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, int namespace; struct SECURITY_CONTEXT security; +#if defined(__APPLE__) || defined(__DARWIN__) + /* If the attribute is not a resource fork attribute and the position + * parameter is non-zero, we return with EINVAL as requesting position + * is not permitted for non-resource fork attributes. */ + is_resource_fork = strcmp(name, XATTR_RESOURCEFORK_NAME) ? FALSE : TRUE; + if (position && !is_resource_fork) { + return -EINVAL; + } +#endif + attr = ntfs_xattr_system_type(name,ctx->vol); if (attr != XATTR_UNMAPPED) { #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) @@ -3466,6 +3507,11 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = -errno; goto exit; } +#if defined(__APPLE__) || defined(__DARWIN__) + } else if (is_resource_fork) { + /* In macOS, the resource fork is a special case. It doesn't + * ever shrink (it would have to be removed and re-added). */ +#endif } else { /* currently compressed streams can only be wiped out */ if (ntfs_attr_truncate(na, (s64)0 /* size */)) { @@ -3477,8 +3523,8 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = 0; if (size) { do { - part = ntfs_attr_pwrite(na, total, size - total, - &value[total]); + part = ntfs_attr_pwrite(na, position + total, + size - total, &value[total]); if (part > 0) total += part; } while ((part > 0) && (total < size)); From 903db231ec0078b9b4efab1ed20b6fb63d88eb18 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Tue, 25 Aug 2020 11:29:42 +0300 Subject: [PATCH 70/87] Change default xattr access method to 'openxattr' for macOS builds. The namespaced way of accessing extended attributes doesn't make sense in macOS which doesn't use namespaces. --- src/ntfs-3g_common.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 397b72a0..6b24528b 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -427,7 +427,14 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx, } break; case OPT_USER_XATTR : +#if defined(__APPLE__) || defined(__DARWIN__) + /* macOS builds use non-namespaced extended + * attributes by default since it matches the + * standard behaviour of macOS filesystems. */ + ctx->streams = NF_STREAMS_INTERFACE_OPENXATTR; +#else ctx->streams = NF_STREAMS_INTERFACE_XATTR; +#endif break; case OPT_NOAUTO : /* Don't pass noauto option to fuse. */ From 7bcae8743fcecfa74ee7d0692c176dff1b057fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 18 Nov 2020 11:29:05 +0100 Subject: [PATCH 71/87] Allowed customization of sbin for tool directory Enabled the configure option --sbindir to define where a few ntfsprogs tools should be installed --- ntfsprogs/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ntfsprogs/Makefile.am b/ntfsprogs/Makefile.am index f4f9d1b6..08228322 100644 --- a/ntfsprogs/Makefile.am +++ b/ntfsprogs/Makefile.am @@ -165,8 +165,8 @@ extras: libs $(EXTRA_PROGRAMS) if ENABLE_MOUNT_HELPER install-exec-hook: - $(INSTALL) -d $(DESTDIR)/sbin - $(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)/sbin/mkfs.ntfs + $(INSTALL) -d $(DESTDIR)/$(sbindir) + $(LN_S) -f $(sbindir)/mkntfs $(DESTDIR)$(sbindir)/mkfs.ntfs install-data-hook: $(INSTALL) -d $(DESTDIR)$(man8dir) From 4b8a66000690c1e7c8393728a60ca78379d0b838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Wed, 18 Nov 2020 11:33:49 +0100 Subject: [PATCH 72/87] Fixed maintining the allocated size when updating sparse files When filling a hole in a sparse file, the beginning of the runlist does not need to be updated. However the allocated size is within the extent holding its beginning and it needs to be updated. --- libntfs-3g/attrib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c index 7d5d5374..a736a9a2 100644 --- a/libntfs-3g/attrib.c +++ b/libntfs-3g/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-2015 Jean-Pierre Andre + * Copyright (c) 2007-2020 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -5850,6 +5850,9 @@ retry: & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); if (spcomp) a->compressed_size = cpu_to_sle64(na->compressed_size); + /* Updating sizes taints the extent holding the attr */ + if (ctx->ntfs_ino) + NInoSetDirty(ctx->ntfs_ino); if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { na->ni->allocated_size = (spcomp From 02673bd04a2b91db74c219fcba5cabb9e7eff4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 73/87] Enabled actions on directories in reparse plugins The plugins triggered by reparse points can now act on a directory through link(2) unlink(2) and creat(2). --- include/ntfs-3g/plugin.h | 30 ++++++++++- libntfs-3g/dir.c | 7 +-- src/lowntfs-3g.c | 106 ++++++++++++++++++++++++++++----------- src/ntfs-3g.c | 100 ++++++++++++++++++++++++++---------- 4 files changed, 181 insertions(+), 62 deletions(-) diff --git a/include/ntfs-3g/plugin.h b/include/ntfs-3g/plugin.h index a9d56a5f..88e733c4 100644 --- a/include/ntfs-3g/plugin.h +++ b/include/ntfs-3g/plugin.h @@ -1,7 +1,7 @@ /* * plugin.h : define interface for plugin development * - * Copyright (c) 2015 Jean-Pierre Andre + * Copyright (c) 2015-2021 Jean-Pierre Andre * */ @@ -151,6 +151,34 @@ typedef struct plugin_operations { int (*readdir)(ntfs_inode *ni, const REPARSE_POINT *reparse, s64 *pos, void *fillctx, ntfs_filldir_t filldir, struct fuse_file_info *fi); + /* + * Create a new file of any type + * + * The returned value is a pointer to the inode created, or + * NULL if failed, with errno telling why. + */ + ntfs_inode *(*create)(ntfs_inode *dir_ni, const REPARSE_POINT *reparse, + le32 securid, ntfschar *name, int name_len, + mode_t type); + /* + * Link a new name to a file or directory + * Linking a directory is needed for renaming a directory + * The returned value is zero for success or a negative errno + * value for failure. + * If the returned value is zero, the modified time stamp + * will be updated after the call. + */ + int (*link)(ntfs_inode *dir_ni, const REPARSE_POINT *reparse, + ntfs_inode *ni, ntfschar *name, int name_len); + /* + * Unlink a name from a directory + * The argument pathname may be NULL + * The returned value is zero for success or a negative errno + * value for failure. + */ + int (*unlink)(ntfs_inode *dir_ni, const REPARSE_POINT *reparse, + const char *pathname, + ntfs_inode *ni, ntfschar *name, int name_len); } plugin_operations_t; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 5e5baf62..a517ece9 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1508,12 +1508,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, errno = EINVAL; return NULL; } - - if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - return NULL; - } - + ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); if (!ni) return NULL; diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index f78cb9c8..c9d4a062 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2020 Jean-Pierre Andre + * Copyright (c) 2007-2021 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -261,7 +261,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2020 Jean-Pierre Andre\n" +"Copyright (C) 2007-2021 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -2336,26 +2336,50 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, perm & ~security.umask, S_ISDIR(type)); #endif /* Create object specified in @type. */ - switch (type) { - case S_IFCHR: - case S_IFBLK: - ni = ntfs_create_device(dir_ni, securid, - uname, uname_len, type, dev); - break; - case S_IFLNK: - utarget_len = ntfs_mbstoucs(target, &utarget); - if (utarget_len < 0) { - res = -errno; - goto exit; - } - ni = ntfs_create_symlink(dir_ni, securid, - uname, uname_len, - utarget, utarget_len); - break; - default: - ni = ntfs_create(dir_ni, securid, uname, - uname_len, type); - break; + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + reparse = (REPARSE_POINT*)NULL; + ops = select_reparse_plugin(ctx, dir_ni, &reparse); + if (ops && ops->create) { + ni = (*ops->create)(dir_ni, reparse, + securid, uname, uname_len, type); + } else { + ni = (ntfs_inode*)NULL; + errno = EOPNOTSUPP; + } + free(reparse); +#else /* DISABLE_PLUGINS */ + ni = (ntfs_inode*)NULL; + errno = EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else { + switch (type) { + case S_IFCHR: + case S_IFBLK: + ni = ntfs_create_device(dir_ni, securid, + uname, uname_len, + type, dev); + break; + case S_IFLNK: + utarget_len = ntfs_mbstoucs(target, + &utarget); + if (utarget_len < 0) { + res = -errno; + goto exit; + } + ni = ntfs_create_symlink(dir_ni, + securid, + uname, uname_len, + utarget, utarget_len); + break; + default: + ni = ntfs_create(dir_ni, securid, uname, + uname_len, type); + break; + } } if (ni) { /* @@ -2525,10 +2549,25 @@ static int ntfs_fuse_newlink(fuse_req_t req __attribute__((unused)), #else ntfs_fuse_fill_security_context(req, &security); #endif - { - if (ntfs_link(ni, dir_ni, uname, uname_len)) { - res = -errno; + { + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + res = CALL_REPARSE_PLUGIN(dir_ni, link, + ni, uname, uname_len); + if (res < 0) + goto exit; +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; goto exit; +#endif /* DISABLE_PLUGINS */ + } else { + if (ntfs_link(ni, dir_ni, uname, uname_len)) { + res = -errno; + goto exit; + } } ntfs_inode_update_mbsname(dir_ni, newname, ni->mft_no); if (e) { @@ -2712,9 +2751,20 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name, goto exit; } } - if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni, - uname, uname_len)) - res = -errno; + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + res = CALL_REPARSE_PLUGIN(dir_ni, unlink, (char*)NULL, + ni, uname, uname_len); +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else + if (ntfs_delete(ctx->vol, (char*)NULL, ni, dir_ni, + uname, uname_len)) + res = -errno; /* ntfs_delete() always closes ni and dir_ni */ ni = dir_ni = NULL; exit: diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 2eb197a8..70effd7b 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 Yura Pakhuchiy * Copyright (c) 2005 Yuval Fledel * Copyright (c) 2006-2009 Szabolcs Szakacsits - * Copyright (c) 2007-2020 Jean-Pierre Andre + * Copyright (c) 2007-2021 Jean-Pierre Andre * Copyright (c) 2009 Erik Larsson * * This file is originated from the Linux-NTFS project. @@ -196,7 +196,7 @@ static const char *usage_msg = "\n" "Copyright (C) 2005-2007 Yura Pakhuchiy\n" "Copyright (C) 2006-2009 Szabolcs Szakacsits\n" -"Copyright (C) 2007-2020 Jean-Pierre Andre\n" +"Copyright (C) 2007-2021 Jean-Pierre Andre\n" "Copyright (C) 2009 Erik Larsson\n" "\n" "Usage: %s [-o option[,...]] \n" @@ -2067,26 +2067,47 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev, perm & ~security.umask, S_ISDIR(type)); #endif /* Create object specified in @type. */ - switch (type) { - case S_IFCHR: - case S_IFBLK: - ni = ntfs_create_device(dir_ni, securid, + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + reparse = (REPARSE_POINT*)NULL; + ops = select_reparse_plugin(ctx, dir_ni, &reparse); + if (ops && ops->create) { + ni = (*ops->create)(dir_ni, reparse, + securid, uname, uname_len, type); + } else { + ni = (ntfs_inode*)NULL; + errno = EOPNOTSUPP; + } + free(reparse); +#else /* DISABLE_PLUGINS */ + errno = EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else { + switch (type) { + case S_IFCHR: + case S_IFBLK: + ni = ntfs_create_device(dir_ni, securid, uname, uname_len, type, dev); - break; - case S_IFLNK: - utarget_len = ntfs_mbstoucs(target, &utarget); - if (utarget_len < 0) { - res = -errno; - goto exit; - } - ni = ntfs_create_symlink(dir_ni, securid, - uname, uname_len, + break; + case S_IFLNK: + utarget_len = ntfs_mbstoucs(target, + &utarget); + if (utarget_len < 0) { + res = -errno; + goto exit; + } + ni = ntfs_create_symlink(dir_ni, + securid, uname, uname_len, utarget, utarget_len); - break; - default: - ni = ntfs_create(dir_ni, securid, uname, - uname_len, type); - break; + break; + default: + ni = ntfs_create(dir_ni, securid, + uname, uname_len, type); + break; + } } if (ni) { /* @@ -2309,10 +2330,24 @@ static int ntfs_fuse_link(const char *old_path, const char *new_path) else #endif { - if (ntfs_link(ni, dir_ni, uname, uname_len)) { - res = -errno; - goto exit; - } + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + res = CALL_REPARSE_PLUGIN(dir_ni, link, + ni, uname, uname_len); +#else /* DISABLE_PLUGINS */ + errno = EOPNOTSUPP; + res = -errno; +#endif /* DISABLE_PLUGINS */ + if (res) + goto exit; + } else + if (ntfs_link(ni, dir_ni, uname, uname_len)) { + res = -errno; + goto exit; + } set_archive(ni); ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); @@ -2384,9 +2419,20 @@ static int ntfs_fuse_rm(const char *org_path) || ntfs_allowed_dir_access(&security, org_path, dir_ni, ni, S_IEXEC + S_IWRITE + S_ISVTX)) { #endif - if (ntfs_delete(ctx->vol, org_path, ni, dir_ni, - uname, uname_len)) - res = -errno; + if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) { +#ifndef DISABLE_PLUGINS + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + res = CALL_REPARSE_PLUGIN(dir_ni, unlink, + org_path, ni, uname, uname_len); +#else /* DISABLE_PLUGINS */ + res = -EOPNOTSUPP; +#endif /* DISABLE_PLUGINS */ + } else + if (ntfs_delete(ctx->vol, org_path, ni, dir_ni, + uname, uname_len)) + res = -errno; /* ntfs_delete() always closes ni and dir_ni */ ni = dir_ni = NULL; #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) From b086c9ef7327910c85ae9a937537b72c39b5b0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 74/87] Inserted the reparse tag in the bad reparse symlink The reparse tag is not quoted in the "unsupported reparse point" fake symlink shown when a reparse point cannot be processed. The tag is useful to determine which plugin, if any, is missing. --- src/lowntfs-3g.c | 76 +++++++++++++++++++++++++++++++++--------------- src/ntfs-3g.c | 60 ++++++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 39 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index c9d4a062..d20a5dd1 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -277,7 +277,9 @@ static const char *usage_msg = #endif /* PLUGIN_DIR */ "%s"; -static const char ntfs_bad_reparse[] = "unsupported reparse point"; +static const char ntfs_bad_reparse[] = "unsupported reparse tag 0x%08lx"; + /* exact length of target text, without the terminator */ +#define ntfs_bad_reparse_lth (sizeof(ntfs_bad_reparse) + 2) #ifdef FUSE_INTERNAL int drop_privs(void); @@ -664,7 +666,7 @@ static int junction_getstat(ntfs_inode *ni, if (target) stbuf->st_size = strlen(target); else - stbuf->st_size = sizeof(ntfs_bad_reparse) - 1; + stbuf->st_size = ntfs_bad_reparse_lth; stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_mode = S_IFLNK; free(target); @@ -722,8 +724,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, if (!res) { apply_umask(stbuf); } else { - stbuf->st_size = - sizeof(ntfs_bad_reparse) - 1; + stbuf->st_size = ntfs_bad_reparse_lth; stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_mode = S_IFLNK; @@ -744,8 +745,7 @@ static int ntfs_fuse_getstat(struct SECURITY_CONTEXT *scx, if (target) stbuf->st_size = strlen(target); else - stbuf->st_size = - sizeof(ntfs_bad_reparse) - 1; + stbuf->st_size = ntfs_bad_reparse_lth; stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_nlink = @@ -1035,19 +1035,33 @@ static void ntfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent, */ static int junction_readlink(ntfs_inode *ni, - const REPARSE_POINT *reparse __attribute__((unused)), - char **pbuf) + const REPARSE_POINT *reparse, char **pbuf) { int res; + le32 tag; + int lth; errno = 0; res = 0; *pbuf = ntfs_make_symlink(ni, ctx->abs_mnt_point); if (!*pbuf) { if (errno == EOPNOTSUPP) { - *pbuf = strdup(ntfs_bad_reparse); - if (!*pbuf) - res = -errno; + *pbuf = (char*)ntfs_malloc(ntfs_bad_reparse_lth + 1); + if (*pbuf) { + if (reparse) + tag = reparse->reparse_tag; + else + tag = const_cpu_to_le32(0); + lth = snprintf(*pbuf, ntfs_bad_reparse_lth + 1, + ntfs_bad_reparse, + (long)le32_to_cpu(tag)); + if (lth != ntfs_bad_reparse_lth) { + free(*pbuf); + *pbuf = (char*)NULL; + res = -errno; + } + } else + res = -ENOMEM; } else res = -errno; } @@ -1074,26 +1088,41 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino) * Reparse point : analyze as a junction point */ if (ni->flags & FILE_ATTR_REPARSE_POINT) { + REPARSE_POINT *reparse; + le32 tag; + int lth; #ifndef DISABLE_PLUGINS const plugin_operations_t *ops; - REPARSE_POINT *reparse; res = CALL_REPARSE_PLUGIN(ni, readlink, &buf); - if (res || !buf) { - buf = strdup(ntfs_bad_reparse); - res = (buf ? 0 : -errno); - } + if (res && (errno == ELIBACC)) + errno = EOPNOTSUPP; #else /* DISABLE_PLUGINS */ errno = 0; res = 0; buf = ntfs_make_symlink(ni, ctx->abs_mnt_point); - if (!buf) { - if (errno == EOPNOTSUPP) - buf = strdup(ntfs_bad_reparse); - if (!buf) - res = -errno; - } #endif /* DISABLE_PLUGINS */ + if (!buf && (errno == EOPNOTSUPP)) { + buf = (char*)malloc(ntfs_bad_reparse_lth + 1); + if (buf) { + reparse = ntfs_get_reparse_point(ni); + if (reparse) { + tag = reparse->reparse_tag; + free(reparse); + } else + tag = const_cpu_to_le32(0); + lth = snprintf(buf, ntfs_bad_reparse_lth + 1, + ntfs_bad_reparse, + (long)le32_to_cpu(tag)); + res = 0; + if (lth != ntfs_bad_reparse_lth) { + free(buf); + buf = (char*)NULL; + } + } + } + if (!buf) + res = -errno; goto exit; } /* Sanity checks. */ @@ -1149,8 +1178,7 @@ exit: fuse_reply_err(req, -res); else fuse_reply_readlink(req, buf); - if (buf != ntfs_bad_reparse) - free(buf); + free(buf); } static int ntfs_fuse_filler(ntfs_fuse_fill_context_t *fill_ctx, diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 70effd7b..a1788512 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -212,7 +212,9 @@ static const char *usage_msg = #endif /* PLUGIN_DIR */ "%s"; -static const char ntfs_bad_reparse[] = "unsupported reparse point"; +static const char ntfs_bad_reparse[] = "unsupported reparse tag 0x%08lx"; + /* exact length of target text, without the terminator */ +#define ntfs_bad_reparse_lth (sizeof(ntfs_bad_reparse) + 2) #ifdef FUSE_INTERNAL int drop_privs(void); @@ -722,7 +724,7 @@ static int junction_getattr(ntfs_inode *ni, if (target) stbuf->st_size = strlen(target); else - stbuf->st_size = sizeof(ntfs_bad_reparse) - 1; + stbuf->st_size = ntfs_bad_reparse_lth; stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_mode = S_IFLNK; free(target); @@ -807,8 +809,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) apply_umask(stbuf); goto ok; } else { - stbuf->st_size = - sizeof(ntfs_bad_reparse) - 1; + stbuf->st_size = ntfs_bad_reparse_lth; stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_mode = S_IFLNK; @@ -830,7 +831,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf) if (target) stbuf->st_size = strlen(target); else - stbuf->st_size = sizeof(ntfs_bad_reparse) - 1; + stbuf->st_size = ntfs_bad_reparse_lth; stbuf->st_blocks = (ni->allocated_size + 511) >> 9; stbuf->st_nlink = le16_to_cpu(ni->mrec->link_count); stbuf->st_mode = S_IFLNK; @@ -1052,15 +1053,30 @@ static int junction_readlink(ntfs_inode *ni, char **pbuf) { int res; + le32 tag; + int lth; errno = 0; res = 0; *pbuf = ntfs_make_symlink(ni, ctx->abs_mnt_point); if (!*pbuf) { if (errno == EOPNOTSUPP) { - *pbuf = strdup(ntfs_bad_reparse); - if (!*pbuf) - res = -errno; + *pbuf = (char*)ntfs_malloc(ntfs_bad_reparse_lth + 1); + if (*pbuf) { + if (reparse) + tag = reparse->reparse_tag; + else + tag = const_cpu_to_le32(0); + lth = snprintf(*pbuf, ntfs_bad_reparse_lth + 1, + ntfs_bad_reparse, + (long)le32_to_cpu(tag)); + if (lth != ntfs_bad_reparse_lth) { + free(*pbuf); + *pbuf = (char*)NULL; + res = -errno; + } + } else + res = -ENOMEM; } else res = -errno; } @@ -1077,6 +1093,9 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size) ntfs_attr *na = NULL; INTX_FILE *intx_file = NULL; int stream_name_len, res = 0; + REPARSE_POINT *reparse; + le32 tag; + int lth; /* Get inode. */ stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); @@ -1098,16 +1117,16 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size) #ifndef DISABLE_PLUGINS char *gotlink; const plugin_operations_t *ops; - REPARSE_POINT *reparse; gotlink = (char*)NULL; res = CALL_REPARSE_PLUGIN(ni, readlink, &gotlink); if (gotlink) { strncpy(buf, gotlink, buf_size); free(gotlink); - } else { - strncpy(buf, ntfs_bad_reparse, buf_size); res = 0; + } else { + errno = EOPNOTSUPP; + res = -EOPNOTSUPP; } #else /* DISABLE_PLUGINS */ char *target; @@ -1119,11 +1138,22 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size) strncpy(buf,target,buf_size); free(target); } else - if (errno == EOPNOTSUPP) - strcpy(buf,ntfs_bad_reparse); - else - res = -errno; + res = -errno; #endif /* DISABLE_PLUGINS */ + if (res == -EOPNOTSUPP) { + reparse = ntfs_get_reparse_point(ni); + if (reparse) { + tag = reparse->reparse_tag; + free(reparse); + } else + tag = const_cpu_to_le32(0); + lth = snprintf(buf, ntfs_bad_reparse_lth + 1, + ntfs_bad_reparse, + (long)le32_to_cpu(tag)); + res = 0; + if (lth != ntfs_bad_reparse_lth) + res = -errno; + } goto exit; } /* Sanity checks. */ From c3c5c77be3b1639efbec365c5094daa5b258ad57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 75/87] Checked file type on OpenIndiana when deleting a file with a reparse point On OpenIndiana a check is needed to ensure directories are not removed by rm(1) and files not removed by rmdir(1) --- src/lowntfs-3g.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index d20a5dd1..41049162 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -2650,6 +2650,9 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name, #if !KERNELPERMS | (POSIXACLS & !KERNELACLS) struct SECURITY_CONTEXT security; #endif +#if defined(__sun) && defined (__SVR4) + int isdir; +#endif /* defined(__sun) && defined (__SVR4) */ /* Deny removing from $Extend */ if (parent == FILE_Extend) { @@ -2689,9 +2692,32 @@ static int ntfs_fuse_rm(fuse_req_t req, fuse_ino_t parent, const char *name, #if defined(__sun) && defined (__SVR4) /* on Solaris : deny unlinking directories */ - if (rm_type - == (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY ? RM_LINK : RM_DIR)) { - errno = EPERM; + isdir = ni->mrec->flags & MFT_RECORD_IS_DIRECTORY; +#ifndef DISABLE_PLUGINS + /* get emulated type from plugin if available */ + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + struct stat st; + const plugin_operations_t *ops; + REPARSE_POINT *reparse; + + /* Avoid double opening of parent directory */ + res = ntfs_inode_close(dir_ni); + if (res) + goto exit; + dir_ni = (ntfs_inode*)NULL; + res = CALL_REPARSE_PLUGIN(ni, getattr, &st); + if (res) + goto exit; + isdir = S_ISDIR(st.st_mode); + dir_ni = ntfs_inode_open(ctx->vol, INODE(parent)); + if (!dir_ni) { + res = -errno; + goto exit; + } + } +#endif /* DISABLE_PLUGINS */ + if (rm_type == (isdir ? RM_LINK : RM_DIR)) { + errno = (isdir ? EISDIR : ENOTDIR); res = -errno; goto exit; } From 76fe04d03ddf0e75e69c1568807818b078ae03d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 76/87] Built reparse symlinks from mount point recorded in volume When building a symlink to emulate a Windows junction or absolute symlink, use the mount point recorded in the volume attributes. This enables the plugins to emulate object as symlinks. --- libntfs-3g/reparse.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index c1d226be..362d539a 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -604,8 +604,9 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, * or NULL if there were some problem, as described by errno */ -char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, - int count, const char *mnt_point, BOOL isdir) +char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, int count, + const char *mnt_point __attribute__((unused)), + BOOL isdir) { char *target; char *fulltarget; @@ -651,10 +652,11 @@ char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, target = search_absolute(vol, &junction[3], count - 3, isdir); if (target) { - fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + fulltarget = (char*)ntfs_malloc( + strlen(vol->abs_mnt_point) + strlen(target) + 2); if (fulltarget) { - strcpy(fulltarget,mnt_point); + strcpy(fulltarget,vol->abs_mnt_point); strcat(fulltarget,"/"); strcat(fulltarget,target); } @@ -679,10 +681,11 @@ char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, && (target[0] >= 'a') && (target[0] <= 'z')) target[0] += 'A' - 'a'; - fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + fulltarget = (char*)ntfs_malloc( + strlen(vol->abs_mnt_point) + sizeof(mappingdir) + strlen(target) + 1); if (fulltarget) { - strcpy(fulltarget,mnt_point); + strcpy(fulltarget,vol->abs_mnt_point); strcat(fulltarget,"/"); strcat(fulltarget,mappingdir); strcat(fulltarget,target); From 8fa3dd3f224a12a60dfd23780afe658e69dcdc4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 77/87] Defined ntfs_realloc() and ntfs_free() Currently memory allocations are done through ntfs_malloc() and ntfs_calloc(), but releases are done through free(3). Defining an ntfs_free() relay facilitates the debugging of memory leaks in plugins. --- include/ntfs-3g/misc.h | 2 ++ libntfs-3g/misc.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/ntfs-3g/misc.h b/include/ntfs-3g/misc.h index a03e964e..c5568b39 100644 --- a/include/ntfs-3g/misc.h +++ b/include/ntfs-3g/misc.h @@ -25,6 +25,8 @@ void *ntfs_calloc(size_t size); void *ntfs_malloc(size_t size); +void *ntfs_realloc(void *ptr, size_t size); +void ntfs_free(void *ptr); #endif /* _NTFS_MISC_H_ */ diff --git a/libntfs-3g/misc.c b/libntfs-3g/misc.c index b2e17cbf..03fb592a 100644 --- a/libntfs-3g/misc.c +++ b/libntfs-3g/misc.c @@ -59,3 +59,19 @@ void *ntfs_malloc(size_t size) ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); return p; } + +void *ntfs_realloc(void *ptr, size_t size) +{ + void *p; + + p = realloc(ptr, size); + if (!p) + ntfs_log_perror("Failed to realloc %lld bytes", + (long long)size); + return p; +} + +void ntfs_free(void *p) +{ + free(p); +} From a67746c8a80ebbf8b14da2821cff186a967bfe27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 78/87] Relocated the mount point field in volume The location of the mount point field in the volume attributes was dependent on compiling options, thus creating an uneasy dependency for plugins. So relocate the field to be independent on options. --- include/ntfs-3g/volume.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index bbb61203..67fc49a8 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -256,6 +256,7 @@ struct _ntfs_volume { s64 free_mft_records; /* Same for free mft records (see above) */ BOOL efs_raw; /* volume is mounted for raw access to efs-encrypted files */ + const char *abs_mnt_point; /* Mount point */ #ifdef XATTR_MAPPINGS struct XATTRMAPPING *xattr_mapping; #endif /* XATTR_MAPPINGS */ @@ -274,7 +275,6 @@ struct _ntfs_volume { #if CACHE_LEGACY_SIZE struct CACHE_HEADER *legacy_cache; #endif - const char *abs_mnt_point; /* Mount point */ }; extern const char *ntfs_home; From 8073ab67644161b733cc6f1101b329f72f72e8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 79/87] Supported use of WSL special file The Windows Subsystem for Linux (WSL) of Windows 10 uses reparse points to record special files (symlinks, fifos, sockets, char or block devices). Honor such reparse points with the same meaning as WSL. --- include/ntfs-3g/ea.h | 4 +- include/ntfs-3g/layout.h | 12 ++++-- include/ntfs-3g/reparse.h | 2 + libntfs-3g/ea.c | 76 ++++++++++++++++++++++++++++++++++++- libntfs-3g/reparse.c | 79 +++++++++++++++++++++++++++++++++++++-- ntfsprogs/ntfsinfo.c | 16 ++++++++ src/lowntfs-3g.c | 60 ++++++++++++++++++++++++++++- src/ntfs-3g.c | 60 +++++++++++++++++++++++++++++ 8 files changed, 300 insertions(+), 9 deletions(-) diff --git a/include/ntfs-3g/ea.h b/include/ntfs-3g/ea.h index 1936aab4..b96e40b4 100644 --- a/include/ntfs-3g/ea.h +++ b/include/ntfs-3g/ea.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2014 Jean-Pierre Andre + * Copyright (c) 2014-2021 Jean-Pierre Andre * */ @@ -24,6 +24,8 @@ #ifndef EA_H #define EA_H +int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp); + int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size); int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags); diff --git a/include/ntfs-3g/layout.h b/include/ntfs-3g/layout.h index 6d4d589a..d2022628 100644 --- a/include/ntfs-3g/layout.h +++ b/include/ntfs-3g/layout.h @@ -850,8 +850,10 @@ typedef enum { FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000), FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000), FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000), + /* Supposed to mean no data locally, possibly repurposed */ + FILE_ATTRIBUTE_RECALL_ON_OPEN = const_cpu_to_le32(0x00040000), - FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7), + FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00047fb7), /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the FILE_ATTR_DEVICE and preserves everything else. This mask is used to obtain all flags that are valid for reading. */ @@ -2444,8 +2446,12 @@ typedef enum { IO_REPARSE_TAG_WCI = const_cpu_to_le32(0x80000018), IO_REPARSE_TAG_CLOUD = const_cpu_to_le32(0x9000001A), IO_REPARSE_TAG_APPEXECLINK = const_cpu_to_le32(0x8000001B), - IO_REPARSE_TAG_GVFS = const_cpu_to_le32(0x9000001C), - IO_REPARSE_TAG_LX_SYMLINK = const_cpu_to_le32(0xA000001D), + IO_REPARSE_TAG_GVFS = const_cpu_to_le32(0x9000001C), + IO_REPARSE_TAG_LX_SYMLINK = const_cpu_to_le32(0xA000001D), + IO_REPARSE_TAG_AF_UNIX = const_cpu_to_le32(0x80000023), + IO_REPARSE_TAG_LX_FIFO = const_cpu_to_le32(0x80000024), + IO_REPARSE_TAG_LX_CHR = const_cpu_to_le32(0x80000025), + IO_REPARSE_TAG_LX_BLK = const_cpu_to_le32(0x80000026), IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xf000ffff), IO_REPARSE_PLUGIN_SELECT = const_cpu_to_le32(0xffff0fff), diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 9109762b..0813ad84 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -35,6 +35,8 @@ char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction, REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni); +int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse); + int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, size_t size, int flags); int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index 0a7b20e9..32cccdad 100644 --- a/libntfs-3g/ea.c +++ b/libntfs-3g/ea.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2014 Jean-Pierre Andre + * Copyright (c) 2014-2021 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -43,6 +43,12 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS +#include +#endif #include "types.h" #include "param.h" @@ -55,6 +61,9 @@ #include "logging.h" #include "xattrs.h" +static const char lxdev[] = "$LXDEV"; + + /* * Create a needed attribute (EA or EA_INFORMATION) * @@ -393,3 +402,68 @@ int ntfs_remove_ntfs_ea(ntfs_inode *ni) } return (res ? -1 : 0); } + +/* + * Check for the presence of an EA "$LXDEV" (used by WSL) + * and return its value as a device address + * + * Returns zero if successful + * -1 if failed, with errno set + */ + +int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp) +{ + const EA_ATTR *p_ea; + int bufsize; + char *buf; + int lth; + int res; + int offset; + int next; + BOOL found; + struct { + le32 major; + le32 minor; + } device; + + res = -EOPNOTSUPP; + bufsize = 256; /* expected to be enough */ + buf = (char*)malloc(bufsize); + if (buf) { + lth = ntfs_get_ntfs_ea(ni, buf, bufsize); + /* retry if short buf */ + if (lth > bufsize) { + free(buf); + bufsize = lth; + buf = (char*)malloc(bufsize); + if (buf) + lth = ntfs_get_ntfs_ea(ni, buf, bufsize); + } + } + if (buf && (lth > 0) && (lth <= bufsize)) { + offset = 0; + found = FALSE; + do { + p_ea = (const EA_ATTR*)&buf[offset]; + next = le32_to_cpu(p_ea->next_entry_offset); + found = ((next > (int)(sizeof(lxdev) + sizeof(device))) + && (p_ea->name_length == (sizeof(lxdev) - 1)) + && (p_ea->value_length + == const_cpu_to_le16(sizeof(device))) + && !memcmp(p_ea->name, lxdev, sizeof(lxdev))); + if (!found) + offset += next; + } while (!found && (next > 0) && (offset < lth)); + if (found) { + /* beware of alignment */ + memcpy(&device, &p_ea->name[p_ea->name_length + 1], + sizeof(device)); + *rdevp = makedev(le32_to_cpu(device.major), + le32_to_cpu(device.minor)); + res = 0; + } + } + free(buf); + return (res); +} + diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index 362d539a..d8415aa5 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2008-2016 Jean-Pierre Andre + * Copyright (c) 2008-2021 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -37,7 +37,10 @@ #ifdef HAVE_SYS_STAT_H #include #endif -#ifdef HAVE_SYS_SYSMACROS_H +#ifdef MAJOR_IN_MKDEV +#include +#endif +#ifdef MAJOR_IN_SYSMACROS #include #endif @@ -56,6 +59,7 @@ #include "misc.h" #include "reparse.h" #include "xattrs.h" +#include "ea.h" struct MOUNT_POINT_REPARSE_DATA { /* reparse data for junctions */ le16 subst_name_offset; @@ -74,6 +78,11 @@ struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ char path_buffer[0]; /* above data assume this is char array */ } ; +struct WSL_LINK_REPARSE_DATA { + le32 type; + char link[0]; +} ; + struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ INDEX_ENTRY_HEADER header; REPARSE_INDEX_KEY key; @@ -415,6 +424,35 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) return (ret); } +/* + * Check whether reparse data describes a valid wsl special file + * which is either a socket, a fifo, or a character or block device + * + * Return zero if valid, otherwise returns a negative error code + */ + +int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse) +{ + int res; + + res = -EOPNOTSUPP; + switch (reparse->reparse_tag) { + case IO_REPARSE_TAG_AF_UNIX : + case IO_REPARSE_TAG_LX_FIFO : + case IO_REPARSE_TAG_LX_CHR : + case IO_REPARSE_TAG_LX_BLK : + if (!reparse->reparse_data_length + && (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) + res = 0; + break; + default : + break; + } + if (res) + errno = EOPNOTSUPP; + return (res); +} + /* * Do some sanity checks on reparse data * @@ -435,6 +473,7 @@ static BOOL valid_reparse_data(ntfs_inode *ni, unsigned int lth; const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; const struct SYMLINK_REPARSE_DATA *symlink_data; + const struct WSL_LINK_REPARSE_DATA *wsl_reparse_data; ok = ni && reparse_attr && (size >= sizeof(REPARSE_POINT)) @@ -477,6 +516,22 @@ static BOOL valid_reparse_data(ntfs_inode *ni, + offs + lth)) > size) ok = FALSE; break; + case IO_REPARSE_TAG_LX_SYMLINK : + wsl_reparse_data = (const struct WSL_LINK_REPARSE_DATA*) + reparse_attr->reparse_data; + if ((le16_to_cpu(reparse_attr->reparse_data_length) + <= sizeof(wsl_reparse_data->type)) + || (wsl_reparse_data->type != const_cpu_to_le32(2))) + ok = FALSE; + break; + case IO_REPARSE_TAG_AF_UNIX : + case IO_REPARSE_TAG_LX_FIFO : + case IO_REPARSE_TAG_LX_CHR : + case IO_REPARSE_TAG_LX_BLK : + if (reparse_attr->reparse_data_length + || !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN)) + ok = FALSE; + break; default : break; } @@ -737,6 +792,7 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point) REPARSE_POINT *reparse_attr; struct MOUNT_POINT_REPARSE_DATA *mount_point_data; struct SYMLINK_REPARSE_DATA *symlink_data; + struct WSL_LINK_REPARSE_DATA *wsl_link_data; enum { FULL_TARGET, ABS_TARGET, REL_TARGET } kind; ntfschar *p; BOOL bad; @@ -819,6 +875,22 @@ char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point) break; } break; + case IO_REPARSE_TAG_LX_SYMLINK : + wsl_link_data = (struct WSL_LINK_REPARSE_DATA*) + reparse_attr->reparse_data; + if (wsl_link_data->type == const_cpu_to_le32(2)) { + lth = le16_to_cpu( + reparse_attr->reparse_data_length) + - sizeof(wsl_link_data->type); + target = (char*)ntfs_malloc(lth + 1); + if (target) { + memcpy(target, wsl_link_data->link, + lth); + target[lth] = 0; + bad = FALSE; + } + } + break; } free(reparse_attr); } @@ -848,6 +920,7 @@ BOOL ntfs_possible_symlink(ntfs_inode *ni) switch (reparse_attr->reparse_tag) { case IO_REPARSE_TAG_MOUNT_POINT : case IO_REPARSE_TAG_SYMLINK : + case IO_REPARSE_TAG_LX_SYMLINK : possible = TRUE; default : ; } @@ -1279,7 +1352,7 @@ REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni) && !valid_reparse_data(ni, reparse_attr, attr_size)) { free(reparse_attr); reparse_attr = (REPARSE_POINT*)NULL; - errno = ENOENT; + errno = EINVAL; } } else errno = EINVAL; diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index 0a6364c7..5a22f9c3 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -439,6 +439,18 @@ static const char *reparse_type_name(le32 tag) case IO_REPARSE_TAG_LX_SYMLINK : name = " (Linux symlink)"; break; + case IO_REPARSE_TAG_LX_FIFO : + name = " (Linux fifo)"; + break; + case IO_REPARSE_TAG_LX_CHR : + name = " (Linux character device)"; + break; + case IO_REPARSE_TAG_LX_BLK : + name = " (Linux block device)"; + break; + case IO_REPARSE_TAG_AF_UNIX : + name = " (Unix socket)"; + break; case IO_REPARSE_TAG_APPEXECLINK : name = " (Exec link)"; break; @@ -622,6 +634,10 @@ static void ntfs_dump_flags(const char *indent, ATTR_TYPES type, le32 flags) printf(" VIEW_INDEX"); flags &= ~FILE_ATTR_VIEW_INDEX_PRESENT; } + if (flags & FILE_ATTRIBUTE_RECALL_ON_OPEN) { + printf(" RECALL_ON_OPEN"); + flags &= ~FILE_ATTRIBUTE_RECALL_ON_OPEN; + } if (flags) printf(" UNKNOWN: 0x%08x", (unsigned int)le32_to_cpu(flags)); /* Print all the flags in hex. */ diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 41049162..5cbf7f94 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -102,6 +102,7 @@ #include "ntfstime.h" #include "security.h" #include "reparse.h" +#include "ea.h" #include "object_id.h" #include "efs.h" #include "logging.h" @@ -677,6 +678,49 @@ static int junction_getstat(ntfs_inode *ni, return (res); } +static int wsl_getstat(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct stat *stbuf) +{ + dev_t rdev; + int res; + + res = ntfs_reparse_check_wsl(ni, reparse); + if (!res) { + switch (reparse->reparse_tag) { + case IO_REPARSE_TAG_AF_UNIX : + stbuf->st_mode = S_IFSOCK; + break; + case IO_REPARSE_TAG_LX_FIFO : + stbuf->st_mode = S_IFIFO; + break; + case IO_REPARSE_TAG_LX_CHR : + stbuf->st_mode = S_IFCHR; + res = ntfs_ea_check_wsldev(ni, &rdev); + stbuf->st_rdev = rdev; + break; + case IO_REPARSE_TAG_LX_BLK : + stbuf->st_mode = S_IFBLK; + res = ntfs_ea_check_wsldev(ni, &rdev); + stbuf->st_rdev = rdev; + break; + default : + stbuf->st_size = ntfs_bad_reparse_lth; + stbuf->st_mode = S_IFLNK; + break; + } + } + /* + * If the reparse point is not a valid wsl special file + * we display as a symlink + */ + if (res) { + stbuf->st_size = ntfs_bad_reparse_lth; + stbuf->st_mode = S_IFLNK; + res = 0; + } + return (res); +} + /* * Apply permission masks to st_mode returned by reparse handler */ @@ -1095,7 +1139,8 @@ static void ntfs_fuse_readlink(fuse_req_t req, fuse_ino_t ino) const plugin_operations_t *ops; res = CALL_REPARSE_PLUGIN(ni, readlink, &buf); - if (res && (errno == ELIBACC)) + /* plugin missing or reparse tag failing the check */ + if (res && ((errno == ELIBACC) || (errno == EINVAL))) errno = EOPNOTSUPP; #else /* DISABLE_PLUGINS */ errno = 0; @@ -4131,10 +4176,23 @@ static void register_internal_reparse_plugins(void) .getattr = junction_getstat, .readlink = junction_readlink, } ; + static const plugin_operations_t wsl_ops = { + .getattr = wsl_getstat, + } ; register_reparse_plugin(ctx, IO_REPARSE_TAG_MOUNT_POINT, &ops, (void*)NULL); register_reparse_plugin(ctx, IO_REPARSE_TAG_SYMLINK, &ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_SYMLINK, + &ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_AF_UNIX, + &wsl_ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_FIFO, + &wsl_ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_CHR, + &wsl_ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_BLK, + &wsl_ops, (void*)NULL); } #endif /* DISABLE_PLUGINS */ diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a1788512..a4c77eff 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -100,6 +100,7 @@ #include "ntfstime.h" #include "security.h" #include "reparse.h" +#include "ea.h" #include "object_id.h" #include "efs.h" #include "logging.h" @@ -735,6 +736,49 @@ static int junction_getattr(ntfs_inode *ni, return (res); } +static int wsl_getattr(ntfs_inode *ni, const REPARSE_POINT *reparse, + struct stat *stbuf) +{ + dev_t rdev; + int res; + + res = ntfs_reparse_check_wsl(ni, reparse); + if (!res) { + switch (reparse->reparse_tag) { + case IO_REPARSE_TAG_AF_UNIX : + stbuf->st_mode = S_IFSOCK; + break; + case IO_REPARSE_TAG_LX_FIFO : + stbuf->st_mode = S_IFIFO; + break; + case IO_REPARSE_TAG_LX_CHR : + stbuf->st_mode = S_IFCHR; + res = ntfs_ea_check_wsldev(ni, &rdev); + stbuf->st_rdev = rdev; + break; + case IO_REPARSE_TAG_LX_BLK : + stbuf->st_mode = S_IFBLK; + res = ntfs_ea_check_wsldev(ni, &rdev); + stbuf->st_rdev = rdev; + break; + default : + stbuf->st_size = ntfs_bad_reparse_lth; + stbuf->st_mode = S_IFLNK; + break; + } + } + /* + * If the reparse point is not a valid wsl special file + * we display as a symlink + */ + if (res) { + stbuf->st_size = ntfs_bad_reparse_lth; + stbuf->st_mode = S_IFLNK; + res = 0; + } + return (res); +} + /* * Apply permission masks to st_mode returned by a reparse handler */ @@ -3832,10 +3876,26 @@ static void register_internal_reparse_plugins(void) .getattr = junction_getattr, .readlink = junction_readlink, } ; + static const plugin_operations_t wsl_ops = { + .getattr = wsl_getattr, + } ; + register_reparse_plugin(ctx, IO_REPARSE_TAG_MOUNT_POINT, &ops, (void*)NULL); register_reparse_plugin(ctx, IO_REPARSE_TAG_SYMLINK, &ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_SYMLINK, + &ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_SYMLINK, + &ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_AF_UNIX, + &wsl_ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_FIFO, + &wsl_ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_CHR, + &wsl_ops, (void*)NULL); + register_reparse_plugin(ctx, IO_REPARSE_TAG_LX_BLK, + &wsl_ops, (void*)NULL); } #endif /* DISABLE_PLUGINS */ From 172da099472e384fefde44176e8d028f427b6557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:17 +0100 Subject: [PATCH 80/87] Dropped rejecting having both EA and reparse data Windows traditionally rejected having both EA and reparse data assigned to a file, but Windows 10 has dropped the constraint and it uses this condition massively, so do the same. Note that pre-Windows 10 chkdsk.exe removes the EA' on reparse points, potentially damaging more recent volumes. --- libntfs-3g/ea.c | 9 ++++----- libntfs-3g/reparse.c | 11 ++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index 32cccdad..f03378bc 100644 --- a/libntfs-3g/ea.c +++ b/libntfs-3g/ea.c @@ -293,12 +293,11 @@ int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags) } } /* - * EA and REPARSE_POINT exclude each other - * see http://msdn.microsoft.com/en-us/library/windows/desktop/aa364404(v=vs.85).aspx - * Also return EINVAL if REPARSE_POINT is present. + * EA and REPARSE_POINT compatibility not checked any more, + * required by Windows 10, but having both may lead to + * problems with earlier versions. */ - if (ok - && !ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED,0)) { + if (ok) { ea_info.ea_length = cpu_to_le16(ea_packed); ea_info.need_ea_count = cpu_to_le16(ea_count); ea_info.ea_query_length = cpu_to_le32(nextoffs); diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index d8415aa5..fbdc7ca1 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -1197,11 +1197,12 @@ int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, ntfs_index_context *xr; res = 0; - /* reparse data is not compatible with EA */ - if (ni - && !ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0) - && !ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0) - && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { + /* + * reparse data compatibily with EA is not checked + * any more, it is required by Windows 10, but may + * lead to problems with earlier versions. + */ + if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { xr = open_reparse_index(ni->vol); if (xr) { if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, From 5d46b32b91d2641c637c015e39b7d1c6641f90c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 81/87] Enabled Recording the special files the same way as WSL Optionally record the special files (symlinks, fifos, sockets, character and block devices) using reparse points instead of using Interix representation. Doing so, the special files are interoperable with Windows Subsystem for linux (WSL). --- include/ntfs-3g/ea.h | 2 + include/ntfs-3g/reparse.h | 5 ++ include/ntfs-3g/volume.h | 6 ++ libntfs-3g/dir.c | 127 ++++++++++++++++++++++++++++++-------- libntfs-3g/ea.c | 51 +++++++++++++++ libntfs-3g/reparse.c | 86 ++++++++++++++++++++++++++ src/lowntfs-3g.c | 7 ++- src/ntfs-3g.8.in | 8 +++ src/ntfs-3g.c | 7 ++- src/ntfs-3g_common.c | 14 ++++- src/ntfs-3g_common.h | 2 + 11 files changed, 285 insertions(+), 30 deletions(-) diff --git a/include/ntfs-3g/ea.h b/include/ntfs-3g/ea.h index b96e40b4..cd61fa2f 100644 --- a/include/ntfs-3g/ea.h +++ b/include/ntfs-3g/ea.h @@ -26,6 +26,8 @@ int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp); +int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode, dev_t dev); + int ntfs_get_ntfs_ea(ntfs_inode *ni, char *value, size_t size); int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags); diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 0813ad84..c617951e 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -37,6 +37,11 @@ REPARSE_POINT *ntfs_get_reparse_point(ntfs_inode *ni); int ntfs_reparse_check_wsl(ntfs_inode *ni, const REPARSE_POINT *reparse); +int ntfs_reparse_set_wsl_symlink(ntfs_inode *ni, + const ntfschar *target, int target_len); + +int ntfs_reparse_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode); + int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, size_t size, int flags); int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index 67fc49a8..30e07906 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -98,6 +98,11 @@ typedef enum { NTFS_VOLUME_INSECURE = 22 } ntfs_volume_status; +typedef enum { + NTFS_FILES_INTERIX, + NTFS_FILES_WSL, +} ntfs_volume_special_files; + /** * enum ntfs_volume_state_bits - * @@ -256,6 +261,7 @@ struct _ntfs_volume { s64 free_mft_records; /* Same for free mft records (see above) */ BOOL efs_raw; /* volume is mounted for raw access to efs-encrypted files */ + ntfs_volume_special_files special_files; /* Implementation of special files */ const char *abs_mnt_point; /* Mount point */ #ifdef XATTR_MAPPINGS struct XATTRMAPPING *xattr_mapping; diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index a517ece9..e85c3c52 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2008-2020 Jean-Pierre Andre + * Copyright (c) 2008-2021 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -68,6 +68,7 @@ #include "reparse.h" #include "object_id.h" #include "xattrs.h" +#include "ea.h" /* * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O" @@ -1496,9 +1497,11 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, { ntfs_inode *ni; int rollback_data = 0, rollback_sd = 0; + int rollback_dir = 0; FILE_NAME_ATTR *fn = NULL; STANDARD_INFORMATION *si = NULL; int err, fn_len, si_len; + ntfs_volume_special_files special_files; ntfs_log_trace("Entering.\n"); @@ -1515,6 +1518,7 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, #if CACHE_NIDATA_SIZE ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); #endif + special_files = dir_ni->vol->special_files; /* * Create STANDARD_INFORMATION attribute. * JPA Depending on available inherited security descriptor, @@ -1542,8 +1546,19 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, } else clear_nino_flag(ni, v3_Extensions); if (!S_ISREG(type) && !S_ISDIR(type)) { - si->file_attributes = FILE_ATTR_SYSTEM; - ni->flags = FILE_ATTR_SYSTEM; + switch (special_files) { + case NTFS_FILES_WSL : + if (!S_ISLNK(type)) { + si->file_attributes + = FILE_ATTRIBUTE_RECALL_ON_OPEN; + ni->flags = FILE_ATTRIBUTE_RECALL_ON_OPEN; + } + break; + default : + si->file_attributes = FILE_ATTR_SYSTEM; + ni->flags = FILE_ATTR_SYSTEM; + break; + } } ni->flags |= FILE_ATTR_ARCHIVE; if (NVolHideDotFiles(dir_ni->vol) @@ -1576,8 +1591,8 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err = errno; goto err_out; } + rollback_sd = 1; } - rollback_sd = 1; if (S_ISDIR(type)) { INDEX_ROOT *ir = NULL; @@ -1626,34 +1641,58 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, switch (type) { case S_IFBLK: case S_IFCHR: - data_len = offsetof(INTX_FILE, device_end); - data = ntfs_malloc(data_len); - if (!data) { - err = errno; - goto err_out; + switch (special_files) { + case NTFS_FILES_WSL : + data_len = 0; + data = (INTX_FILE*)NULL; + break; + default : + data_len = offsetof(INTX_FILE, + device_end); + data = (INTX_FILE*)ntfs_malloc( + data_len); + if (!data) { + err = errno; + goto err_out; + } + data->major = cpu_to_le64(major(dev)); + data->minor = cpu_to_le64(minor(dev)); + if (type == S_IFBLK) + data->magic + = INTX_BLOCK_DEVICE; + if (type == S_IFCHR) + data->magic + = INTX_CHARACTER_DEVICE; + break; } - data->major = cpu_to_le64(major(dev)); - data->minor = cpu_to_le64(minor(dev)); - if (type == S_IFBLK) - data->magic = INTX_BLOCK_DEVICE; - if (type == S_IFCHR) - data->magic = INTX_CHARACTER_DEVICE; break; case S_IFLNK: - data_len = sizeof(INTX_FILE_TYPES) + + switch (special_files) { + case NTFS_FILES_WSL : + data_len = 0; + data = (INTX_FILE*)NULL; + break; + default : + data_len = sizeof(INTX_FILE_TYPES) + target_len * sizeof(ntfschar); - data = ntfs_malloc(data_len); - if (!data) { - err = errno; - goto err_out; - } - data->magic = INTX_SYMBOLIC_LINK; - memcpy(data->target, target, + data = (INTX_FILE*)ntfs_malloc( + data_len); + if (!data) { + err = errno; + goto err_out; + } + data->magic = INTX_SYMBOLIC_LINK; + memcpy(data->target, target, target_len * sizeof(ntfschar)); + break; + } break; case S_IFSOCK: data = NULL; - data_len = 1; + if (special_files == NTFS_FILES_WSL) + data_len = 0; + else + data_len = 1; break; default: /* FIFO or regular file. */ data = NULL; @@ -1684,9 +1723,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, fn->file_name_type = FILE_NAME_POSIX; if (S_ISDIR(type)) fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT; - if (!S_ISREG(type) && !S_ISDIR(type)) - fn->file_attributes = FILE_ATTR_SYSTEM; - else + if (!S_ISREG(type) && !S_ISDIR(type)) { + if (special_files == NTFS_FILES_INTERIX) + fn->file_attributes = FILE_ATTR_SYSTEM; + } else fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; fn->file_attributes |= FILE_ATTR_ARCHIVE; fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; @@ -1714,10 +1754,40 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, ntfs_log_perror("Failed to add entry to the index"); goto err_out; } + rollback_dir = 1; /* Set hard links count and directory flag. */ ni->mrec->link_count = const_cpu_to_le16(1); if (S_ISDIR(type)) ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY; + /* Add reparse data */ + if (special_files == NTFS_FILES_WSL) { + switch (type) { + case S_IFLNK : + err = ntfs_reparse_set_wsl_symlink(ni, target, + target_len); + break; + case S_IFIFO : + case S_IFSOCK : + case S_IFCHR : + case S_IFBLK : + err = ntfs_reparse_set_wsl_not_symlink(ni, + type); + if (!err) { + err = ntfs_ea_set_wsl_not_symlink(ni, + type, dev); + if (err) + ntfs_remove_ntfs_reparse_data(ni); + } + break; + default : + err = 0; + break; + } + if (err) { + err = errno; + goto err_out; + } + } ntfs_inode_mark_dirty(ni); /* Done! */ free(fn); @@ -1727,6 +1797,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err_out: ntfs_log_trace("Failed.\n"); + if (rollback_dir) + ntfs_index_remove(dir_ni, ni, fn, fn_len); + if (rollback_sd) ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c index f03378bc..b86930ab 100644 --- a/libntfs-3g/ea.c +++ b/libntfs-3g/ea.c @@ -62,6 +62,7 @@ #include "xattrs.h" static const char lxdev[] = "$LXDEV"; +static const char lxmod[] = "$LXMOD"; /* @@ -466,3 +467,53 @@ int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp) return (res); } +int ntfs_ea_set_wsl_not_symlink(ntfs_inode *ni, mode_t type, dev_t dev) +{ + le32 mode; + struct { + le32 major; + le32 minor; + } device; + struct EA_WSL { + struct EA_LXMOD { /* always inserted */ + EA_ATTR base; + char name[sizeof(lxmod)]; + char value[sizeof(mode)]; + char stuff[3 & -(sizeof(lxmod) + sizeof(mode))]; + } mod; + struct EA_LXDEV { /* char or block devices only */ + EA_ATTR base; + char name[sizeof(lxdev)]; + char value[sizeof(device)]; + char stuff[3 & -(sizeof(lxdev) + sizeof(device))]; + } dev; + } attr; + int len; + int res; + + memset(&attr, 0, sizeof(attr)); + mode = cpu_to_le32((u32)(type | 0644)); + attr.mod.base.next_entry_offset + = const_cpu_to_le32(sizeof(attr.mod)); + attr.mod.base.flags = 0; + attr.mod.base.name_length = sizeof(lxmod) - 1; + attr.mod.base.value_length = const_cpu_to_le16(sizeof(mode)); + memcpy(attr.mod.name, lxmod, sizeof(lxmod)); + memcpy(attr.mod.value, &mode, sizeof(mode)); + len = sizeof(attr.mod); + + if (S_ISCHR(type) || S_ISBLK(type)) { + device.major = cpu_to_le32(major(dev)); + device.minor = cpu_to_le32(minor(dev)); + attr.dev.base.next_entry_offset + = const_cpu_to_le32(sizeof(attr.dev)); + attr.dev.base.flags = 0; + attr.dev.base.name_length = sizeof(lxdev) - 1; + attr.dev.base.value_length = const_cpu_to_le16(sizeof(device)); + memcpy(attr.dev.name, lxdev, sizeof(lxdev)); + memcpy(attr.dev.value, &device, sizeof(device)); + len += sizeof(attr.dev); + } + res = ntfs_set_ntfs_ea(ni, (char*)&attr, len, 0); + return (res); +} diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index fbdc7ca1..76dbe2f1 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -1331,6 +1331,92 @@ int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) return (res ? -1 : 0); } +/* + * Set reparse data for a WSL type symlink + */ + +int ntfs_reparse_set_wsl_symlink(ntfs_inode *ni, + const ntfschar *target, int target_len) +{ + int res; + int len; + int reparse_len; + char *utarget; + REPARSE_POINT *reparse; + struct WSL_LINK_REPARSE_DATA *data; + + res = -1; + utarget = (char*)NULL; + len = ntfs_ucstombs(target, target_len, &utarget, 0); + if (len > 0) { + reparse_len = sizeof(REPARSE_POINT) + sizeof(data->type) + len; + reparse = (REPARSE_POINT*)malloc(reparse_len); + if (reparse) { + data = (struct WSL_LINK_REPARSE_DATA*) + reparse->reparse_data; + reparse->reparse_tag = IO_REPARSE_TAG_LX_SYMLINK; + reparse->reparse_data_length + = cpu_to_le16(sizeof(data->type) + len); + reparse->reserved = const_cpu_to_le16(0); + data->type = const_cpu_to_le32(2); + memcpy(data->link, utarget, len); + res = ntfs_set_ntfs_reparse_data(ni, + (char*)reparse, reparse_len, 0); + free(reparse); + } + } + free(utarget); + return (res); +} + +/* + * Set reparse data for a WSL special file other than a symlink + * (socket, fifo, character or block device) + */ + +int ntfs_reparse_set_wsl_not_symlink(ntfs_inode *ni, mode_t mode) +{ + int res; + int len; + int reparse_len; + le32 reparse_tag; + REPARSE_POINT *reparse; + + res = -1; + len = 0; + switch (mode) { + case S_IFSOCK : + reparse_tag = IO_REPARSE_TAG_AF_UNIX; + break; + case S_IFIFO : + reparse_tag = IO_REPARSE_TAG_LX_FIFO; + break; + case S_IFCHR : + reparse_tag = IO_REPARSE_TAG_LX_CHR; + break; + case S_IFBLK : + reparse_tag = IO_REPARSE_TAG_LX_BLK; + break; + default : + len = -1; + errno = EOPNOTSUPP; + break; + } + if (len >= 0) { + reparse_len = sizeof(REPARSE_POINT) + len; + reparse = (REPARSE_POINT*)malloc(reparse_len); + if (reparse) { + reparse->reparse_tag = reparse_tag; + reparse->reparse_data_length = cpu_to_le16(len); + reparse->reserved = const_cpu_to_le16(0); + res = ntfs_set_ntfs_reparse_data(ni, + (char*)reparse, reparse_len, 0); + free(reparse); + } + } + return (res); +} + /* * Get the reparse data into a buffer diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index 5cbf7f94..eea4e6da 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -2385,7 +2385,11 @@ static int ntfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, perm = (typemode & ~ctx->dmask & 0777) | (dsetgid & S_ISGID); else - perm = typemode & ~ctx->fmask & 0777; + if ((ctx->special_files == NTFS_FILES_WSL) + && S_ISLNK(type)) + perm = typemode | 0777; + else + perm = typemode & ~ctx->fmask & 0777; /* * Try to get a security id available for * file creation (from inheritance or argument). @@ -4737,6 +4741,7 @@ int main(int argc, char *argv[]) goto err_out; ctx->vol->abs_mnt_point = ctx->abs_mnt_point; + ctx->vol->special_files = ctx->special_files; ctx->security.vol = ctx->vol; ctx->vol->secure_flags = ctx->secure_flags; #ifdef HAVE_SETXATTR /* extended attributes interface required */ diff --git a/src/ntfs-3g.8.in b/src/ntfs-3g.8.in index 2d18fb4b..d4338a9c 100644 --- a/src/ntfs-3g.8.in +++ b/src/ntfs-3g.8.in @@ -316,6 +316,14 @@ data streams are mapped to xattrs and user can manipulate them using .B user_xattr Same as \fBstreams_interface=\fP\fIxattr\fP. .TP +.BI special_files= value +This option selects a mode for representing a special file to be created +(symbolic link, socket, fifo, character or block device). The mode can +be \fBinterix\fR or \fBwsl\fR, and existing files in either mode are +recognized irrespective of the selected mode. Interix is the traditional +mode, used by default, and wsl is interoperable with Windows WSL, but +it is not compatible with Windows versions earlier than Windows 10. +.TP .B efs_raw This option should only be used in backup or restore situation. It changes the apparent size of files and the behavior of read and diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index a4c77eff..6ec2020a 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2117,7 +2117,11 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev, perm = (typemode & ~ctx->dmask & 0777) | (dsetgid & S_ISGID); else - perm = typemode & ~ctx->fmask & 0777; + if ((ctx->special_files == NTFS_FILES_WSL) + && S_ISLNK(type)) + perm = typemode | 0777; + else + perm = typemode & ~ctx->fmask & 0777; /* * Try to get a security id available for * file creation (from inheritance or argument). @@ -4464,6 +4468,7 @@ int main(int argc, char *argv[]) ctx->vol->abs_mnt_point = ctx->abs_mnt_point; ctx->security.vol = ctx->vol; ctx->vol->secure_flags = ctx->secure_flags; + ctx->vol->special_files = ctx->special_files; #ifdef HAVE_SETXATTR /* extended attributes interface required */ ctx->vol->efs_raw = ctx->efs_raw; #endif /* HAVE_SETXATTR */ diff --git a/src/ntfs-3g_common.c b/src/ntfs-3g_common.c index 6b24528b..7e3e93d2 100644 --- a/src/ntfs-3g_common.c +++ b/src/ntfs-3g_common.c @@ -1,7 +1,7 @@ /** * ntfs-3g_common.c - Common definitions for ntfs-3g and lowntfs-3g. * - * Copyright (c) 2010-2020 Jean-Pierre Andre + * Copyright (c) 2010-2021 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -127,6 +127,7 @@ const struct DEFOPTION optionlist[] = { { "xattrmapping", OPT_XATTRMAPPING, FLGOPT_STRING }, { "efs_raw", OPT_EFS_RAW, FLGOPT_BOGUS }, { "posix_nlink", OPT_POSIX_NLINK, FLGOPT_BOGUS }, + { "special_files", OPT_SPECIAL_FILES, FLGOPT_STRING }, { (const char*)NULL, 0, 0 } /* end marker */ } ; @@ -503,6 +504,17 @@ char *parse_mount_options(ntfs_fuse_context_t *ctx, case OPT_POSIX_NLINK : ctx->posix_nlink = TRUE; break; + case OPT_SPECIAL_FILES : + if (!strcmp(val, "interix")) + ctx->special_files = NTFS_FILES_INTERIX; + else if (!strcmp(val, "wsl")) + ctx->special_files = NTFS_FILES_WSL; + else { + ntfs_log_error("Invalid special_files" + " mode.\n"); + goto err_exit; + } + break; case OPT_FSNAME : /* Filesystem name. */ /* * We need this to be able to check whether filesystem diff --git a/src/ntfs-3g_common.h b/src/ntfs-3g_common.h index 8c2ecf39..4ed256a3 100644 --- a/src/ntfs-3g_common.h +++ b/src/ntfs-3g_common.h @@ -93,6 +93,7 @@ enum { OPT_XATTRMAPPING, OPT_EFS_RAW, OPT_POSIX_NLINK, + OPT_SPECIAL_FILES, } ; /* Option flags */ @@ -155,6 +156,7 @@ typedef struct { BOOL blkdev; BOOL mounted; BOOL posix_nlink; + ntfs_volume_special_files special_files; #ifdef HAVE_SETXATTR /* extended attributes interface required */ BOOL efs_raw; #ifdef XATTR_MAPPINGS From 894b7dd36e56ce7b52cfef9a105ef3eed07dc307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 82/87] Checked the locations of MFT and MFTMirr at startup On startup make sure the lcns of the MFT and the MFTMirr are not null and they are different, so that the mounting is denied gracefully if they are. --- libntfs-3g/bootsect.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libntfs-3g/bootsect.c b/libntfs-3g/bootsect.c index e9eb0b15..483473e6 100644 --- a/libntfs-3g/bootsect.c +++ b/libntfs-3g/bootsect.c @@ -154,6 +154,12 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b) } } + /* MFT and MFTMirr may not overlap the boot sector or be the same */ + if (!b->mft_lcn || !b->mftmirr_lcn || (b->mft_lcn == b->mftmirr_lcn)) { + ntfs_log_error("Invalid location of MFT or MFTMirr.\n"); + goto not_ntfs; + } + if (b->end_of_sector_marker != const_cpu_to_le16(0xaa55)) ntfs_log_debug("Warning: Bootsector has invalid end of sector " "marker.\n"); From 094f9b3f2de7718ebfad00431ed9a5ddbc97583c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 83/87] Fixed a minor endianness ajustment bug The endianness ajustment was the wrong one though it did the correct thing. --- libntfs-3g/index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index 6fc79d5d..006a6ecd 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -1584,7 +1584,7 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, freed_space = le32_to_cpu(ih->allocated_size) - le32_to_cpu(ih->index_length); if (full && (freed_space > 0) && !(freed_space & 7)) { - ntfs_ir_truncate(icx, cpu_to_le32(ih->index_length)); + ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); /* do nothing if truncation fails */ } ntfs_inode_mark_dirty(icx->actx->ntfs_ino); From 56b8e713d56895944d1ff89b4ff2dee4c37393ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= Date: Tue, 26 Jan 2021 10:06:18 +0100 Subject: [PATCH 84/87] Fixed a constant string concatenation Adjust for recent compilers requiring a space between concatenated strings. --- ntfsprogs/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ntfsprogs/utils.c b/ntfsprogs/utils.c index 393c34a4..31522da3 100644 --- a/ntfsprogs/utils.c +++ b/ntfsprogs/utils.c @@ -75,7 +75,7 @@ #include "logging.h" #include "misc.h" -const char *ntfs_bugs = "Developers' email address: "NTFS_DEV_LIST"\n"; +const char *ntfs_bugs = "Developers' email address: " NTFS_DEV_LIST "\n"; const char *ntfs_gpl = "This program is free software, released under the GNU " "General Public License\nand you are welcome to redistribute it under " "certain conditions. It comes with\nABSOLUTELY NO WARRANTY; for " From da1b61ec225eaf5a78ea5ae1105defb43708827b Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Thu, 25 Feb 2021 17:13:24 +0200 Subject: [PATCH 85/87] lowntfs-3g.c: Fix compile error when building with libfuse < 2.8.0. The libfuse function 'fuse_lowlevel_notify_inval_inode' is only available starting with libfuse 2.8.0 while we advertise compatibility with FUSE 2.6.0 as the baseline. So add a preprocessor check to exclude this code from libfuse < 2.8.0. --- src/lowntfs-3g.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lowntfs-3g.c b/src/lowntfs-3g.c index eea4e6da..badf0529 100644 --- a/src/lowntfs-3g.c +++ b/src/lowntfs-3g.c @@ -3809,7 +3809,7 @@ static void ntfs_fuse_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, } else res = -errno; #endif -#if CACHEING && !defined(FUSE_INTERNAL) +#if CACHEING && !defined(FUSE_INTERNAL) && FUSE_VERSION >= 28 /* * Most of system xattr settings cause changes to some * file attribute (st_mode, st_nlink, st_mtime, etc.), @@ -4056,7 +4056,7 @@ static void ntfs_fuse_removexattr(fuse_req_t req, fuse_ino_t ino, const char *na } else res = -errno; #endif -#if CACHEING && !defined(FUSE_INTERNAL) +#if CACHEING && !defined(FUSE_INTERNAL) && FUSE_VERSION >= 28 /* * Some allowed system xattr removals cause changes to * some file attribute (st_mode, st_nlink, etc.), From bcd42e5ef3e4bada89e889c7e82a04b0d3a9e5cd Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 26 Feb 2021 08:09:18 +0200 Subject: [PATCH 86/87] configure.ac: Respect 'with_fuse' user setting on non-Linux/Solaris. Previously the configure script simply disregarded the value of the 'with_fuse' flag on non-Linux/Solaris systems. This is not unreasonable since the 'internal' option doesn't work on those systems, however in some situations we want to specify the libfuse CFLAGS/LDFLAGS manually and bypass the pkg-config check with '--with-fuse=none' and this was not possible with this logic. So add a special test to bypass the automatic determination when the user has specified a 'with_fuse' value. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 88733eb5..0b483c20 100644 --- a/configure.ac +++ b/configure.ac @@ -226,7 +226,9 @@ case "${target}" in ;; esac -if test "x${enable_ntfs_3g}" = "xyes"; then +if test "x${enable_ntfs_3g}" != "xyes"; then + with_fuse="none" +elif test "x${with_fuse}" == "x"; then AC_MSG_CHECKING([fuse compatibility]) case "${target_os}" in linux*|solaris*) @@ -248,8 +250,6 @@ if test "x${enable_ntfs_3g}" = "xyes"; then ;; esac AC_MSG_RESULT([${with_fuse}]) -else - with_fuse="none" fi case "${target_os}" in From dd75ea7420b8a533d6923bb962b7efea42fa60b6 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Wed, 3 Mar 2021 16:39:45 +0200 Subject: [PATCH 87/87] configure.ac: Fix obsolete 2-argument AM_INIT_AUTOMAKE invocation. The 2-argument form has been replaced with fetching the package/version information from autoconf (AC_INIT). Instead the 1-argument form takes a list of automake options but we currently do not need to specify any. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0b483c20..7d8324f5 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ AC_CANONICAL_HOST AC_CANONICAL_TARGET # Automake -AM_INIT_AUTOMAKE([${PACKAGE_NAME}], [${PACKAGE_VERSION}]) +AM_INIT_AUTOMAKE([]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AM_MAINTAINER_MODE