From a38b79897df9c7d07f509f729e28f22f8c027921 Mon Sep 17 00:00:00 2001 From: jpandre Date: Mon, 18 Feb 2008 15:15:13 +0000 Subject: [PATCH] Adapted to ntfs-3g.1.2216 --- include/ntfs-3g/index.h | 9 +- include/ntfs-3g/misc.h | 23 +++ include/ntfs-3g/volume.h | 8 +- libntfs-3g/dir.c | 32 +-- libntfs-3g/index.c | 158 ++++++++------- libntfs-3g/logging.c | 2 +- libntfs-3g/volume.c | 15 +- src/ntfs-3g.c | 412 ++++++++++++++++++--------------------- src/utils.c | 19 +- 9 files changed, 335 insertions(+), 343 deletions(-) diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h index e60eee22..f43504b9 100644 --- a/include/ntfs-3g/index.h +++ b/include/ntfs-3g/index.h @@ -48,7 +48,6 @@ * @actx: attribute search context if in root or NULL otherwise * @ia: index block if @is_in_root is FALSE or NULL otherwise * @ia_na: opened INDEX_ALLOCATION attribute - * @ib_vcn: VCN from which @ia where read from * @parent_pos: parent entries' positions in the index block * @parent_vcn: entry's parent node or VCN_INDEX_ROOT_PARENT * @new_vcn: new VCN if we need to create a new index block @@ -64,11 +63,11 @@ * simply points into @entry. This is probably what the user is interested in. * * If @is_in_root is TRUE, @entry is in the index root attribute @ir described - * by the attribute search context @actx and inode @ni. @ia, @ib_vcn and + * by the attribute search context @actx and inode @ni. @ia and * @ib_dirty are undefined in this case. * * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia - * and @ib_vcn point to the index allocation block and VCN where it's placed, + * point to the index allocation block and VCN where it's placed, * respectively. @ir and @actx are NULL in this case. @ia_na is opened * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and * FALSE otherwise. @@ -96,9 +95,7 @@ typedef struct { INDEX_BLOCK *ib; ntfs_attr *ia_na; int parent_pos[MAX_PARENT_VCN]; /* parent entries' positions */ - VCN ib_vcn; VCN parent_vcn[MAX_PARENT_VCN]; /* entry's parent nodes */ - int max_depth; /* number of the parent nodes */ int pindex; /* maximum it's the number of the parent nodes */ BOOL ib_dirty; u32 block_size; @@ -118,7 +115,7 @@ extern INDEX_ENTRY *ntfs_index_next(INDEX_ENTRY *ie, extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref); -extern int ntfs_index_rm(ntfs_index_context *ictx); +extern int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen); extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr); diff --git a/include/ntfs-3g/misc.h b/include/ntfs-3g/misc.h index 5928ddda..fded6555 100644 --- a/include/ntfs-3g/misc.h +++ b/include/ntfs-3g/misc.h @@ -1,3 +1,26 @@ +/* + * misc.h : miscellaneous exports + * - memory allocation + * - LRU caches + * + * Copyright (c) 2008 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 + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + #ifndef _NTFS_MISC_H_ #define _NTFS_MISC_H_ diff --git a/include/ntfs-3g/volume.h b/include/ntfs-3g/volume.h index ae985f56..fff7f547 100644 --- a/include/ntfs-3g/volume.h +++ b/include/ntfs-3g/volume.h @@ -4,7 +4,7 @@ * Copyright (c) 2000-2004 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * 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 @@ -60,6 +60,8 @@ #define MS_FORCE 0x10000000 #endif +#define MS_IGNORE_HIBERFILE 0x20000000 + /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; @@ -95,7 +97,8 @@ typedef enum { NTFS_VOLUME_UNKNOWN_REASON = 18, NTFS_VOLUME_NO_PRIVILEGE = 19, NTFS_VOLUME_OUT_OF_MEMORY = 20, - NTFS_VOLUME_FUSE_ERROR = 21 + NTFS_VOLUME_FUSE_ERROR = 21, + NTFS_VOLUME_INSECURE = 22 } ntfs_volume_status; /** @@ -254,6 +257,7 @@ extern ntfs_volume *ntfs_mount(const char *name, unsigned long flags); extern int ntfs_umount(ntfs_volume *vol, const BOOL force); extern int ntfs_version_is_supported(ntfs_volume *vol); +extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); extern int ntfs_logfile_reset(ntfs_volume *vol); extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index cdca9141..db21afbe 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -1493,7 +1493,6 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname, ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) { ntfs_attr_search_ctx *actx = NULL; - ntfs_index_context *ictx = NULL; FILE_NAME_ATTR *fn = NULL; BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; @@ -1593,19 +1592,7 @@ search: if (ntfs_check_unlinkable_dir(ni, fn) < 0) goto err_out; - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) - goto err_out; - if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->value_length), ictx)) - goto err_out; - - if (((FILE_NAME_ATTR*)ictx->data)->file_attributes & - FILE_ATTR_REPARSE_POINT) { - errno = EOPNOTSUPP; - goto err_out; - } - - if (ntfs_index_rm(ictx)) + if (ntfs_index_remove(dir_ni, fn, le32_to_cpu(actx->attr->value_length))) goto err_out; if (ntfs_attr_record_rm(actx)) @@ -1676,8 +1663,6 @@ ok: out: if (actx) ntfs_attr_put_search_ctx(actx); - if (ictx) - ntfs_index_ctx_put(ictx); if (ntfs_inode_close(dir_ni) && !err) err = errno; if (ntfs_inode_close(ni) && !err) @@ -1774,22 +1759,11 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) } /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { - ntfs_index_context *ictx; - - err = errno; ntfs_log_error("Failed to add FILE_NAME attribute.\n"); + err = errno; /* Try to remove just added attribute from index. */ - ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4); - if (!ictx) + if (ntfs_index_remove(dir_ni, fn, fn_len)) goto rollback_failed; - if (ntfs_index_lookup(fn, fn_len, ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } - if (ntfs_index_rm(ictx)) { - ntfs_index_ctx_put(ictx); - goto rollback_failed; - } goto err_out; } /* Increment hard links count. */ diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c index f20f9f67..0e6a7c55 100644 --- a/libntfs-3g/index.c +++ b/libntfs-3g/index.c @@ -4,8 +4,8 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2005-2006 Yura Pakhuchiy - * Copyright (c) 2005-2006 Szabolcs Szakacsits - * Copyright (c) 2007-2008 Jean-Pierre Andre + * Copyright (c) 2005-2008 Szabolcs Szakacsits + * Copyright (c) 2007 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 @@ -79,14 +79,14 @@ static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos) return pos >> icx->vcn_size_bits; } -static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) +static int ntfs_ib_write(ntfs_index_context *icx, INDEX_BLOCK *ib) { - s64 ret; + s64 ret, vcn = sle64_to_cpu(ib->index_block_vcn); ntfs_log_trace("vcn: %lld\n", vcn); ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn), - 1, icx->block_size, buf); + 1, icx->block_size, ib); if (ret != 1) { ntfs_log_perror("Failed to write index block %lld, inode %llu", (long long)vcn, (unsigned long long)icx->ni->mft_no); @@ -98,7 +98,7 @@ static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf) static int ntfs_icx_ib_write(ntfs_index_context *icx) { - if (ntfs_ib_write(icx, icx->ib_vcn, icx->ib)) + if (ntfs_ib_write(icx, icx->ib)) return STATUS_ERROR; icx->ib_dirty = FALSE; @@ -148,18 +148,14 @@ static void ntfs_index_ctx_free(ntfs_index_context *icx) if (icx->actx) ntfs_attr_put_search_ctx(icx->actx); - if (icx->is_in_root) { - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); - return; - } - - if (icx->ib_dirty) { - /* FIXME: Error handling!!! */ - ntfs_ib_write(icx, icx->ib_vcn, icx->ib); + if (!icx->is_in_root) { + if (icx->ib_dirty) { + /* FIXME: Error handling!!! */ + ntfs_ib_write(icx, icx->ib); + } + free(icx->ib); } - free(icx->ib); ntfs_attr_close(icx->ia_na); } @@ -452,8 +448,10 @@ static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); err_out: - if (!ir) + if (!ir) { ntfs_attr_put_search_ctx(*ctx); + *ctx = NULL; + } return ir; } @@ -660,15 +658,13 @@ static int ntfs_icx_parent_dec(ntfs_index_context *icx) * the call to ntfs_index_ctx_put() to ensure that the changes are written * to disk. */ -int ntfs_index_lookup(const void *key, const int key_len, - ntfs_index_context *icx) +int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *icx) { VCN old_vcn, vcn; ntfs_inode *ni = icx->ni; INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_BLOCK *ib = NULL; - ntfs_attr_search_ctx *actx; int ret, err = 0; ntfs_log_trace("Entering\n"); @@ -679,7 +675,7 @@ int ntfs_index_lookup(const void *key, const int key_len, return -1; } - ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &actx); + ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &icx->actx); if (!ir) { if (errno == ENOENT) errno = EIO; @@ -691,7 +687,7 @@ int ntfs_index_lookup(const void *key, const int key_len, errno = EINVAL; ntfs_log_perror("Index block size (%d) is smaller than the " "sector size (%d)", icx->block_size, NTFS_BLOCK_SIZE); - return -1; + goto err_out; } if (ni->vol->cluster_size <= icx->block_size) @@ -718,7 +714,6 @@ int ntfs_index_lookup(const void *key, const int key_len, goto err_out; } - icx->actx = actx; icx->ir = ir; if (ret != STATUS_KEEP_SEARCHING) { @@ -764,7 +759,7 @@ descend_into_child_node: /* STATUS_OK or STATUS_NOT_FOUND */ icx->is_in_root = FALSE; icx->ib = ib; - icx->parent_vcn[icx->pindex] = icx->ib_vcn = vcn; + icx->parent_vcn[icx->pindex] = vcn; goto done; } @@ -777,20 +772,15 @@ descend_into_child_node: goto descend_into_child_node; err_out: - if (icx->ia_na) - ntfs_attr_close(icx->ia_na); free(ib); if (!err) err = EIO; - if (actx) - ntfs_attr_put_search_ctx(actx); errno = err; return -1; done: icx->entry = ie; icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key); icx->data_len = le16_to_cpu(ie->key_length); - icx->max_depth = icx->pindex; ntfs_log_trace("Done.\n"); if (err) { errno = err; @@ -1061,13 +1051,13 @@ static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src, dst->index.index_length = cpu_to_le32(tail_size + le32_to_cpu(dst->index.entries_offset)); - ret = ntfs_ib_write(icx, new_vcn, dst); + ret = ntfs_ib_write(icx, dst); free(dst); return ret; } -static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, +static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *ib, INDEX_ENTRY *ie) { char *ies_start, *ies_end; @@ -1075,8 +1065,8 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, ntfs_log_trace("Entering\n"); - ies_start = (char *)ntfs_ie_get_first(&src->index); - ies_end = (char *)ntfs_ie_get_end(&src->index); + ies_start = (char *)ntfs_ie_get_first(&ib->index); + ies_end = (char *)ntfs_ie_get_end(&ib->index); ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end); if (ie_last->ie_flags & INDEX_ENTRY_NODE) @@ -1084,10 +1074,10 @@ static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src, memcpy(ie, ie_last, le16_to_cpu(ie_last->length)); - src->index.index_length = cpu_to_le32(((char *)ie - ies_start) + - le16_to_cpu(ie->length) + le32_to_cpu(src->index.entries_offset)); + ib->index.index_length = cpu_to_le32(((char *)ie - ies_start) + + le16_to_cpu(ie->length) + le32_to_cpu(ib->index.entries_offset)); - if (ntfs_ib_write(icx, icx->parent_vcn[icx->pindex + 1], src)) + if (ntfs_ib_write(icx, ib)) return STATUS_ERROR; return STATUS_OK; @@ -1149,7 +1139,7 @@ static int ntfs_ir_reparent(ntfs_index_context *icx) goto clear_bmp; } - if (ntfs_ib_write(icx, new_ib_vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto clear_bmp; ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx); @@ -1376,7 +1366,7 @@ static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn) if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx))) goto err_out; - if (ntfs_ib_write(icx, old_vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto err_out; err = STATUS_OK; @@ -1386,7 +1376,7 @@ err_out: } /** - * ntfs_ib_split - Split index allocation attribute + * ntfs_ib_split - Split an index block * * On success return STATUS_OK or STATUS_KEEP_SEARCHING. * On error return is STATUS_ERROR. @@ -1417,8 +1407,6 @@ static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib) else ret = ntfs_ib_insert(icx, median, new_vcn); - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - if (ret != STATUS_OK) { ntfs_ibm_clear(icx, new_vcn); return ret; @@ -1557,7 +1545,7 @@ static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih, if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) ntfs_inode_mark_dirty(icx->actx->ntfs_ino); else - if (ntfs_ib_write(icx, ntfs_icx_parent_vcn(icx), ib)) + if (ntfs_ib_write(icx, ib)) goto out; ntfs_index_ctx_reinit(icx); @@ -1587,9 +1575,6 @@ static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih) /* Not fatal error */ ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length)); - - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); - ntfs_index_ctx_reinit(icx); } /** @@ -1667,7 +1652,7 @@ out: static int ntfs_index_rm_node(ntfs_index_context *icx) { - int entry_pos; + int entry_pos, pindex; VCN vcn; INDEX_BLOCK *ib = NULL; INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry; @@ -1689,6 +1674,7 @@ static int ntfs_index_rm_node(ntfs_index_context *icx) ie_succ = ntfs_ie_get_next(icx->entry); entry_pos = icx->parent_pos[icx->pindex]++; + pindex = icx->pindex; descend: vcn = ntfs_ie_get_vcn(ie_succ); if (ntfs_ib_read(icx, vcn, ib)) @@ -1706,9 +1692,8 @@ descend: goto descend; if (ntfs_ih_zero_entry(&ib->index)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Failed to find any entry in an index block. " - "Please run chkdsk."); + errno = EIO; + ntfs_log_perror("Empty index block"); goto out; } @@ -1730,18 +1715,18 @@ descend: new_size = le32_to_cpu(ih->index_length) + delta; if (delta > 0) { if (icx->is_in_root) { - if (ntfs_ir_truncate(icx, new_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to truncate INDEX ROOT during entry removal"); + ret = ntfs_ir_make_space(icx, new_size); + if (ret != STATUS_OK) goto out2; - } ih = &icx->ir->index; entry = ntfs_ie_get_by_pos(ih, entry_pos); } else if (new_size > le32_to_cpu(ih->allocated_size)) { - errno = EOPNOTSUPP; - ntfs_log_perror("Denied to split INDEX BLOCK during entry removal"); + icx->pindex = pindex; + ret = ntfs_ib_split(icx, icx->ib); + if (ret == STATUS_OK) + ret = STATUS_KEEP_SEARCHING; goto out2; } } @@ -1752,7 +1737,6 @@ descend: if (icx->is_in_root) { if (ntfs_ir_truncate(icx, new_size)) goto out2; - ntfs_inode_mark_dirty(icx->actx->ntfs_ino); } else if (ntfs_icx_ib_write(icx)) goto out2; @@ -1763,7 +1747,7 @@ descend: if (ntfs_index_rm_leaf(icx)) goto out2; } else - if (ntfs_ib_write(icx, vcn, ib)) + if (ntfs_ib_write(icx, ib)) goto out2; ret = STATUS_OK; @@ -1784,10 +1768,10 @@ out: * * Return 0 on success or -1 on error with errno set to the error code. */ -int ntfs_index_rm(ntfs_index_context *icx) +static int ntfs_index_rm(ntfs_index_context *icx) { INDEX_HEADER *ih; - int err; + int err, ret = STATUS_OK; ntfs_log_trace("Entering\n"); @@ -1803,8 +1787,7 @@ int ntfs_index_rm(ntfs_index_context *icx) if (icx->entry->ie_flags & INDEX_ENTRY_NODE) { - if (ntfs_index_rm_node(icx)) - goto err_out; + ret = ntfs_index_rm_node(icx); } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) { @@ -1821,16 +1804,51 @@ int ntfs_index_rm(ntfs_index_context *icx) if (ntfs_index_rm_leaf(icx)) goto err_out; } - - ntfs_index_ctx_reinit(icx); - ntfs_log_trace("Done.\n"); - return 0; +out: + return ret; err_out: - err = errno; - ntfs_index_ctx_reinit(icx); - errno = err; - ntfs_log_trace("Failed.\n"); - return -1; + ret = STATUS_ERROR; + goto out; +} + +int ntfs_index_remove(ntfs_inode *ni, const void *key, const int keylen) +{ + int ret = STATUS_ERROR; + ntfs_index_context *icx; + + icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4); + if (!icx) + return -1; + + while (1) { + + if (ntfs_index_lookup(key, keylen, icx)) + goto err_out; + + if (((FILE_NAME_ATTR *)icx->data)->file_attributes & + FILE_ATTR_REPARSE_POINT) { + errno = EOPNOTSUPP; + goto err_out; + } + + ret = ntfs_index_rm(icx); + if (ret == STATUS_ERROR) + goto err_out; + else if (ret == STATUS_OK) + break; + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); + ntfs_index_ctx_reinit(icx); + } + + ntfs_inode_mark_dirty(icx->actx->ntfs_ino); +out: + ntfs_index_ctx_put(icx); + return ret; +err_out: + ret = STATUS_ERROR; + ntfs_log_perror("Delete failed"); + goto out; } /** diff --git a/libntfs-3g/logging.c b/libntfs-3g/logging.c index c34bc2b0..beb96548 100644 --- a/libntfs-3g/logging.c +++ b/libntfs-3g/logging.c @@ -291,7 +291,7 @@ void ntfs_log_set_handler(ntfs_log_handler *handler) ntfs_log.handler = handler; #ifdef HAVE_SYSLOG_H if (handler == ntfs_log_handler_syslog) - openlog("libntfs", LOG_PID, LOG_USER); + openlog("ntfs-3g", LOG_PID, LOG_USER); #endif } else ntfs_log.handler = ntfs_log_handler_null; diff --git a/libntfs-3g/volume.c b/libntfs-3g/volume.c index e20f7630..c8192521 100644 --- a/libntfs-3g/volume.c +++ b/libntfs-3g/volume.c @@ -2,7 +2,7 @@ * volume.c - NTFS volume handling code. Originated from the Linux-NTFS project. * * Copyright (c) 2000-2006 Anton Altaparmakov - * Copyright (c) 2002-2006 Szabolcs Szakacsits + * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2005 Richard Russon * * This program/include file is free software; you can redistribute it and/or @@ -676,7 +676,7 @@ out: * Return: 0 if Windows isn't hibernated for sure * -1 otherwise and errno is set to the appropriate value */ -static int ntfs_volume_check_hiberfile(ntfs_volume *vol) +int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose) { ntfs_inode *ni; ntfs_attr *na = NULL; @@ -706,13 +706,15 @@ static int ntfs_volume_check_hiberfile(ntfs_volume *vol) goto out; } if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { - ntfs_log_error("Hibernated non-system partition, refused to " - "mount.\n"); + if (verbose) + ntfs_log_error("Hibernated non-system partition, " + "refused to mount.\n"); errno = EPERM; goto out; } if (memcmp(buf, "hibr", 4) == 0) { - ntfs_log_error("Windows is hibernated, refused to mount.\n"); + if (verbose) + ntfs_log_error("Windows is hibernated, refused to mount.\n"); errno = EPERM; goto out; } @@ -1099,7 +1101,8 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) * We care only about read-write mounts. */ if (!(flags & MS_RDONLY)) { - if (ntfs_volume_check_hiberfile(vol) < 0) + if (!(flags & MS_IGNORE_HIBERFILE) && + ntfs_volume_check_hiberfile(vol, 1) < 0) goto error_exit; if (ntfs_volume_check_logfile(vol) < 0) { if (!(flags & MS_FORCE)) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 0fa48d4f..86c7a8a7 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -70,7 +70,6 @@ #include #include #include -#include #ifdef HAVE_SETXATTR #include @@ -130,6 +129,7 @@ typedef struct { BOOL show_sys_files; BOOL silent; BOOL force; + BOOL hiberfile; BOOL debug; BOOL no_detach; BOOL blkdev; @@ -167,13 +167,39 @@ static const char *usage_msg = "\n" "Usage: %s [-o option[,...]]\n" "\n" -"Options: ro, force, locale=, uid=, gid=, umask=, fmask=, dmask=,\n" -" streams_interface=. Please see details in the manual.\n" +"Options: ro (read-only mount), force, remove_hiberfile, locale=,\n" +" uid=, gid=, umask=, fmask=, dmask=, streams_interface=.\n" +" Please see the details in the manual.\n" "\n" "Example: ntfs-3g /dev/sda1 /mnt/win -o force\n" "\n" "%s"; +#ifdef FUSE_INTERNAL +int drop_privs(void); +int restore_privs(void); +#else +/* + * setuid and setgid root ntfs-3g denies to start with external FUSE, + * therefore the below functions are no-op in such case. + */ +static int drop_privs(void) { return 0; } +static int restore_privs(void) { return 0; } + +static const char *setuid_msg = +"Mount is denied because setuid and setgid root ntfs-3g is insecure with the\n" +"external FUSE library. Either remove the setuid/setgid bit from the binary\n" +"or rebuild NTFS-3G with integrated FUSE support and make it setuid root.\n" +"Please see more information at http://ntfs-3g.org/support.html#unprivileged\n"; + +static const char *unpriv_fuseblk_msg = +"Unprivileged user can not mount NTFS block devices using the external FUSE\n" +"library. Either mount the volume as root, or rebuild NTFS-3G with integrated\n" +"FUSE support and make it setuid root. Please see more information at\n" +"http://ntfs-3g.org/support.html#unprivileged\n"; +#endif + + /** * ntfs_fuse_is_named_data_stream - check path to be to named data stream * @path: path to check @@ -1934,6 +1960,8 @@ static int ntfs_open(const char *device) flags |= MS_RDONLY; if (ctx->force) flags |= MS_FORCE; + if (ctx->hiberfile) + flags |= MS_IGNORE_HIBERFILE; ctx->vol = ntfs_mount(device, flags); if (!ctx->vol) { @@ -1953,81 +1981,111 @@ static int ntfs_open(const char *device) goto err_out; } + if (ctx->hiberfile && ntfs_volume_check_hiberfile(ctx->vol, 0)) { + if (errno != EPERM) + goto err_out; + if (ntfs_fuse_rm("/hiberfil.sys")) + goto err_out; + } + errno = 0; err_out: return ntfs_volume_error(errno); } +#define STRAPPEND_MAX_INSIZE 8192 +#define strappend_is_large(x) ((x) > STRAPPEND_MAX_INSIZE) + +static int strappend(char **dest, const char *append) +{ + char *p; + size_t size_append, size_dest = 0; + + if (!dest) + return -1; + if (!append) + return 0; + + size_append = strlen(append); + if (*dest) + size_dest = strlen(*dest); + + if (strappend_is_large(size_dest) || strappend_is_large(size_append)) { + errno = EOVERFLOW; + ntfs_log_perror("%s: Too large input buffer", EXEC_NAME); + return -1; + } + + p = realloc(*dest, size_dest + size_append + 1); + if (!p) { + ntfs_log_perror("%s: Memory realloction failed", EXEC_NAME); + return -1; + } + + *dest = p; + strcpy(*dest + size_dest, append); + + return 0; +} + +static int bogus_option_value(char *val, const char *s) +{ + if (val) { + ntfs_log_error("'%s' option shouldn't have value.\n", s); + return -1; + } + return 0; +} + +static int missing_option_value(char *val, const char *s) +{ + if (!val) { + ntfs_log_error("'%s' option should have a value.\n", s); + return -1; + } + return 0; +} + static char *parse_mount_options(const char *orig_opts) { - char *options, *s, *opt, *val, *ret; + char *options, *s, *opt, *val, *ret = NULL; BOOL no_def_opts = FALSE; int default_permissions = 0; - /* - * FIXME: This is not pretty ... - * +7 fsname= - * +1 comma - * +1 null-terminator - * +21 ,blkdev,blksize=65536 - * +20 ,default_permissions - * +70 ,user= - * +PATH_MAX resolved realpath() device name - */ - ret = ntfs_malloc(strlen(def_opts) + strlen(orig_opts) + 256 + PATH_MAX); - if (!ret) - return NULL; - - *ret = 0; - options = strdup(orig_opts); + options = strdup(orig_opts ? orig_opts : ""); if (!options) { - ntfs_log_perror("strdup failed"); + ntfs_log_perror("%s: strdup failed", EXEC_NAME); return NULL; } ctx->silent = TRUE; - - ctx->atime = ATIME_RELATIVE; - + ctx->atime = ATIME_RELATIVE; + s = options; while (s && *s && (val = strsep(&s, ","))) { opt = strsep(&val, "="); if (!strcmp(opt, "ro")) { /* Read-only mount. */ - if (val) { - ntfs_log_error("'ro' option should not have " - "value.\n"); + if (bogus_option_value(val, "ro")) goto err_exit; - } ctx->ro = TRUE; - strcat(ret, "ro,"); - } else if (!strcmp(opt, "noatime")) { - if (val) { - ntfs_log_error("'noatime' option should not " - "have value.\n"); + if (strappend(&ret, "ro,")) + goto err_exit; + } else if (!strcmp(opt, "noatime")) { + if (bogus_option_value(val, "noatime")) goto err_exit; - } ctx->atime = ATIME_DISABLED; } else if (!strcmp(opt, "atime")) { - if (val) { - ntfs_log_error("'atime' option should not " - "have value.\n"); + if (bogus_option_value(val, "atime")) goto err_exit; - } ctx->atime = ATIME_ENABLED; } else if (!strcmp(opt, "relatime")) { - if (val) { - ntfs_log_error("'relatime' option should not " - "have value.\n"); + if (bogus_option_value(val, "relatime")) goto err_exit; - } ctx->atime = ATIME_RELATIVE; } else if (!strcmp(opt, "fake_rw")) { - if (val) { - ntfs_log_error("'fake_rw' option should not " - "have value.\n"); + if (bogus_option_value(val, "fake_rw")) goto err_exit; - } ctx->ro = TRUE; } else if (!strcmp(opt, "fsname")) { /* Filesystem name. */ /* @@ -2037,93 +2095,64 @@ static char *parse_mount_options(const char *orig_opts) ntfs_log_error("'fsname' is unsupported option.\n"); goto err_exit; } else if (!strcmp(opt, "no_def_opts")) { - if (val) { - ntfs_log_error("'no_def_opts' option should " - "not have value.\n"); + if (bogus_option_value(val, "no_def_opts")) goto err_exit; - } no_def_opts = TRUE; /* Don't add default options. */ } else if (!strcmp(opt, "default_permissions")) { default_permissions = 1; } else if (!strcmp(opt, "umask")) { - if (!val) { - ntfs_log_error("'umask' option should have " - "value.\n"); + if (missing_option_value(val, "umask")) goto err_exit; - } sscanf(val, "%o", &ctx->fmask); ctx->dmask = ctx->fmask; if (ctx->fmask) default_permissions = 1; } else if (!strcmp(opt, "fmask")) { - if (!val) { - ntfs_log_error("'fmask' option should have " - "value.\n"); + if (missing_option_value(val, "fmask")) goto err_exit; - } sscanf(val, "%o", &ctx->fmask); if (ctx->fmask) default_permissions = 1; } else if (!strcmp(opt, "dmask")) { - if (!val) { - ntfs_log_error("'dmask' option should have " - "value.\n"); + if (missing_option_value(val, "dmask")) goto err_exit; - } sscanf(val, "%o", &ctx->dmask); if (ctx->dmask) default_permissions = 1; } else if (!strcmp(opt, "uid")) { - if (!val) { - ntfs_log_error("'uid' option should have " - "value.\n"); + if (missing_option_value(val, "uid")) goto err_exit; - } sscanf(val, "%i", &ctx->uid); default_permissions = 1; } else if (!strcmp(opt, "gid")) { - if (!val) { - ntfs_log_error("'gid' option should have " - "value.\n"); + if (missing_option_value(val, "gid")) goto err_exit; - } sscanf(val, "%i", &ctx->gid); default_permissions = 1; } else if (!strcmp(opt, "show_sys_files")) { - if (val) { - ntfs_log_error("'show_sys_files' option should " - "not have value.\n"); + if (bogus_option_value(val, "show_sys_files")) goto err_exit; - } ctx->show_sys_files = TRUE; } else if (!strcmp(opt, "silent")) { - if (val) { - ntfs_log_error("'silent' option should " - "not have value.\n"); + if (bogus_option_value(val, "silent")) goto err_exit; - } ctx->silent = TRUE; } else if (!strcmp(opt, "force")) { - if (val) { - ntfs_log_error("'force' option should not " - "have value.\n"); + if (bogus_option_value(val, "force")) goto err_exit; - } ctx->force = TRUE; - } else if (!strcmp(opt, "locale")) { - if (!val) { - ntfs_log_error("'locale' option should have " - "value.\n"); + } else if (!strcmp(opt, "remove_hiberfile")) { + if (bogus_option_value(val, "remove_hiberfile")) + goto err_exit; + ctx->hiberfile = TRUE; + } else if (!strcmp(opt, "locale")) { + if (missing_option_value(val, "locale")) goto err_exit; - } if (!setlocale(LC_ALL, val)) ntfs_log_error(locale_msg, val); } else if (!strcmp(opt, "streams_interface")) { - if (!val) { - ntfs_log_error("'streams_interface' option " - "should have value.\n"); + if (missing_option_value(val, "streams_interface")) goto err_exit; - } if (!strcmp(val, "none")) ctx->streams = NF_STREAMS_INTERFACE_NONE; else if (!strcmp(val, "xattr")) @@ -2138,20 +2167,14 @@ static char *parse_mount_options(const char *orig_opts) } else if (!strcmp(opt, "noauto")) { /* Don't pass noauto option to fuse. */ } else if (!strcmp(opt, "debug")) { - if (val) { - ntfs_log_error("'debug' option should not have " - "value.\n"); + if (bogus_option_value(val, "debug")) goto err_exit; - } ctx->debug = TRUE; ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); } else if (!strcmp(opt, "no_detach")) { - if (val) { - ntfs_log_error("'no_detach' option should not " - "have value.\n"); + if (bogus_option_value(val, "no_detach")) goto err_exit; - } ctx->no_detach = TRUE; } else if (!strcmp(opt, "remount")) { ntfs_log_error("Remounting is not supported at present." @@ -2186,28 +2209,34 @@ static char *parse_mount_options(const char *orig_opts) goto err_exit; } } else { /* Probably FUSE option. */ - strcat(ret, opt); + if (strappend(&ret, opt)) + goto err_exit; if (val) { - strcat(ret, "="); - strcat(ret, val); + if (strappend(&ret, "=")) + goto err_exit; + if (strappend(&ret, val)) + goto err_exit; } - strcat(ret, ","); + if (strappend(&ret, ",")) + goto err_exit; } } - if (!no_def_opts) - strcat(ret, def_opts); - if (default_permissions) - strcat(ret, "default_permissions,"); + if (!no_def_opts && strappend(&ret, def_opts)) + goto err_exit; + if (default_permissions && strappend(&ret, "default_permissions,")) + goto err_exit; - if (ctx->atime == ATIME_RELATIVE) - strcat(ret, "relatime,"); - else if (ctx->atime == ATIME_ENABLED) - strcat(ret, "atime,"); - else - strcat(ret, "noatime,"); + if (ctx->atime == ATIME_RELATIVE && strappend(&ret, "relatime,")) + goto err_exit; + else if (ctx->atime == ATIME_ENABLED && strappend(&ret, "atime,")) + goto err_exit; + else if (strappend(&ret, "noatime,")) + goto err_exit; - strcat(ret, "fsname="); - strcat(ret, opts.device); + if (strappend(&ret, "fsname=")) + goto err_exit; + if (strappend(&ret, opts.device)) + goto err_exit; exit: free(options); return ret; @@ -2233,35 +2262,6 @@ static char *realpath(const char *path, char *resolved_path) } #endif -static int strappend(char **dest, const char *append) -{ - char *p; - size_t size; - - if (!dest) - return -1; - if (!append) - return 0; - - size = strlen(append) + 1; - if (*dest) - size += strlen(*dest); - - p = realloc(*dest, size); - if (!p) { - ntfs_log_perror("Memory realloction failed"); - return -1; - } - - if (*dest) - strcat(p, append); - else - strcpy(p, append); - *dest = p; - - return 0; -} - /** * parse_options - Read and validate the programs command line * Read the command line, verify the syntax and parse the options. @@ -2290,18 +2290,14 @@ static int parse_options(int argc, char *argv[]) if (!opts.device) return -1; - /* We don't want relative path in /etc/mtab. */ - if (optarg[0] != '/') { - if (!realpath(optarg, opts.device)) { - ntfs_log_perror("%s: " - "Cannot mount '%s'", - EXEC_NAME, optarg); - free(opts.device); - opts.device = NULL; - return -1; - } - } else - strcpy(opts.device, optarg); + /* Canonicalize device name (mtab, etc) */ + if (!realpath(optarg, opts.device)) { + ntfs_log_perror("%s: Failed to access " + "volume '%s'", EXEC_NAME, optarg); + free(opts.device); + opts.device = NULL; + return -1; + } } else if (!opts.mnt_point) { opts.mnt_point = optarg; } else { @@ -2477,7 +2473,7 @@ free_args: } -static void set_fuseblk_options(char *parsed_options) +static int set_fuseblk_options(char **parsed_options) { char options[64]; long pagesize; @@ -2490,61 +2486,17 @@ static void set_fuseblk_options(char *parsed_options) if (blksize > (u32)pagesize) blksize = pagesize; - /* parsed_options already allocated enough space. */ snprintf(options, sizeof(options), ",blkdev,blksize=%u", blksize); - strcat(parsed_options, options); + if (strappend(parsed_options, options)) + return -1; + return 0; } -static void set_user_mount_option(char *parsed_options, uid_t uid) -{ - struct passwd *pw; - char option[64]; - - if (!uid) - return; - - errno = 0; - pw = getpwuid(uid); - if (!pw || !pw->pw_name) { - ntfs_log_perror("WARNING: could not get username for uid %lld, " - "unprivileged unmount may fail", (long long)uid); - return; - } - - /* parsed_options already allocated enough space. */ - snprintf(option, sizeof(option), ",user=%s", pw->pw_name); - strcat(parsed_options, option); -} - -#ifndef FUSE_INTERNAL -static int set_uid(uid_t uid) -{ - if (setuid(uid)) { - ntfs_log_perror("Failed to set uid to %d", uid); - return NTFS_VOLUME_NO_PRIVILEGE; - } - return NTFS_VOLUME_OK; -} -#endif - static struct fuse *mount_fuse(char *parsed_options) { struct fuse *fh = NULL; struct fuse_args args = FUSE_ARGS_INIT(0, NULL); -#ifndef FUSE_INTERNAL - uid_t uid, euid; - /* - * We must raise privilege if possible, otherwise the user[s] fstab - * option doesn't work because mount(8) always drops privilege what - * the blkdev option requires. - */ - uid = getuid(); - euid = geteuid(); - - if (set_uid(euid)) - return NULL; -#endif /* Libfuse can't always find fusermount, so let's help it. */ if (setenv("PATH", ":/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0)) ntfs_log_perror("WARNING: Failed to set $PATH\n"); @@ -2576,15 +2528,6 @@ static struct fuse *mount_fuse(char *parsed_options) if (fuse_set_signal_handlers(fuse_get_session(fh))) goto err_destory; - /* - * We can't drop privilege if internal FUSE is used because internal - * unmount needs it. Kernel 2.6.25 may include unprivileged full - * mount/unmount support. - */ -#ifndef FUSE_INTERNAL - if (set_uid(uid)) - goto err_destory; -#endif out: fuse_opt_free_args(&args); return fh; @@ -2629,18 +2572,30 @@ int main(int argc, char *argv[]) struct stat sbuf; int err; +#ifndef FUSE_INTERNAL + if ((getuid() != geteuid()) || (getgid() != getegid())) { + fprintf(stderr, "%s", setuid_msg); + return NTFS_VOLUME_INSECURE; + } +#endif + if (drop_privs()) + return NTFS_VOLUME_NO_PRIVILEGE; + utils_set_locale(); ntfs_log_set_handler(ntfs_log_handler_stderr); if (parse_options(argc, argv)) { - usage(); + ntfs_log_error("Please type '%s --help' for more " + "information.\n", argv[0]); return NTFS_VOLUME_SYNTAX_ERROR; } - if (ntfs_fuse_init()) - return NTFS_VOLUME_OUT_OF_MEMORY; + if (ntfs_fuse_init()) { + err = NTFS_VOLUME_OUT_OF_MEMORY; + goto err2; + } - parsed_options = parse_mount_options(opts.options ? opts.options : ""); + parsed_options = parse_mount_options(opts.options); if (!parsed_options) { err = NTFS_VOLUME_SYNTAX_ERROR; goto err_out; @@ -2648,10 +2603,18 @@ int main(int argc, char *argv[]) #if defined(linux) || defined(__uClinux__) fstype = get_fuse_fstype(); + + err = NTFS_VOLUME_NO_PRIVILEGE; + if (restore_privs()) + goto err_out; + if (fstype == FSTYPE_NONE || fstype == FSTYPE_UNKNOWN) fstype = load_fuse_module(); create_dev_fuse(); + + if (drop_privs()) + goto err_out; #endif if (stat(opts.device, &sbuf)) { @@ -2663,15 +2626,19 @@ int main(int argc, char *argv[]) if (S_ISBLK(sbuf.st_mode) && (fstype != FSTYPE_FUSE)) ctx->blkdev = TRUE; +#ifndef FUSE_INTERNAL + if (getuid() && ctx->blkdev) { + ntfs_log_error("%s", unpriv_fuseblk_msg); + goto err2; + } +#endif err = ntfs_open(opts.device); if (err) goto err_out; - if (ctx->blkdev) { - /* Must do after ntfs_open() to set the right blksize. */ - set_fuseblk_options(parsed_options); - set_user_mount_option(parsed_options, getuid()); - } + /* 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; fh = mount_fuse(parsed_options); if (!fh) { @@ -2711,6 +2678,7 @@ int main(int argc, char *argv[]) fuse_destroy(fh); err_out: utils_mount_error(opts.device, opts.mnt_point, err); +err2: ntfs_close(); free(ctx); free(parsed_options); diff --git a/src/utils.c b/src/utils.c index 2627d83f..b57b0714 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,7 +4,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2003 Lode Leroy - * Copyright (c) 2005-2007 Szabolcs Szakacsits + * Copyright (c) 2005-2008 Szabolcs Szakacsits * * A set of shared functions for ntfs utilities * @@ -62,7 +62,12 @@ static const char *corrupt_volume_msg = static const char *hibernated_volume_msg = "The NTFS partition is hibernated. Please resume and shutdown Windows\n" -"properly, so mounting could be done safely.\n"; +"properly, or mount the volume read-only with the 'ro' mount option, or\n" +"mount the volume read-write with the 'remove_hiberfile' mount option.\n" +"For example type on the command line:\n" +"\n" +" mount -t ntfs-3g %s %s -o remove_hiberfile\n" +"\n"; static const char *unclean_journal_msg = "Mount is denied because NTFS is marked to be in use. Choose one action:\n" @@ -85,9 +90,9 @@ static const char *fakeraid_msg = "to mount NTFS. Please see the 'dmraid' documentation for help.\n"; static const char *access_denied_msg = -"Please check the device and the ntfs-3g binary permissions, the mounting\n" -"user and group ID, and the mount options. You can find more explanation\n" -"at http://ntfs-3g.org/support.html#useroption\n"; +"Please check the volume and the ntfs-3g binary permissions,\n" +"and the mounting user ID. More explanation is provided at\n" +"http://ntfs-3g.org/support.html#unprivileged\n"; static const char *forced_mount_msg = "\n" @@ -95,7 +100,7 @@ static const char *forced_mount_msg = "\n" " Or add the option to the relevant row in the /etc/fstab file:\n" "\n" -" %s %s ntfs-3g defaults,force 0 0\n"; +" %s %s ntfs-3g force 0 0\n"; /** * utils_set_locale @@ -124,7 +129,7 @@ void utils_mount_error(const char *volume, const char *mntpoint, int err) ntfs_log_error("%s", corrupt_volume_msg); break; case NTFS_VOLUME_HIBERNATED: - ntfs_log_error("%s", hibernated_volume_msg); + ntfs_log_error(hibernated_volume_msg, volume, mntpoint); break; case NTFS_VOLUME_UNCLEAN_UNMOUNT: ntfs_log_error(unclean_journal_msg);