Adapted to ntfs-3g.1.2216

N2009_11_14_FIXES
jpandre 2008-02-18 15:15:13 +00:00
parent 427c0dc48f
commit a38b79897d
9 changed files with 335 additions and 343 deletions

View File

@ -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);

View File

@ -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_

View File

@ -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);

View File

@ -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. */

View File

@ -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;
}
/**

View File

@ -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;

View File

@ -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))

View File

@ -70,7 +70,6 @@
#include <getopt.h>
#include <syslog.h>
#include <sys/wait.h>
#include <pwd.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
@ -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 <device|image_file> <mount_point> [-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=<max_64_chars>
* +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);

View File

@ -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);