Sorry it's very large commit (46K of diffs between my local tree and CVS HEAD
before commit), file and directory creation/deletion recursively requested many changes. See ChangeLog for description of all changes.edge.strict_endians
parent
4e292d96f1
commit
9028a53dfc
29
ChangeLog
29
ChangeLog
|
@ -1,6 +1,5 @@
|
|||
xx/xx/2005 - 1.11.3-WIP
|
||||
xx/xx/2005 - 1.12.0-WIP
|
||||
|
||||
- ntfsmount: Fix small memleak. (Yura)
|
||||
- Add ./configure detection for gnutls library and make minimum version
|
||||
1.2.3 which is the one that has the rsa key export fixed. (Anton)
|
||||
- Put in a minimum version for libgcrypt of 1.2.0 as I do not know if
|
||||
|
@ -8,6 +7,32 @@ xx/xx/2005 - 1.11.3-WIP
|
|||
- Fix some memory leaks in ntfsdecrypt and do some cleanups. (Anton)
|
||||
- Fix ntfsdecrypt to also work with passwordless files. Note this
|
||||
requires a patched gnutls library or it still does not work. (Anton)
|
||||
- Add new APIs for index adding/remove:
|
||||
index.[ch]::ntfs_index_add_filename and index.[ch]::ntfs_index_rm.
|
||||
They support only basic cases, so can fail with EOPNOTSUPP. (Yura)
|
||||
- Add new API for index context reinitialization:
|
||||
ntfs_index_ctx_reinit. (Yura)
|
||||
- Add new high-level APIs for file and directory creation/deletion that
|
||||
rely on ntfs_index_{add_filename,rm}:
|
||||
dir.[ch]::ntfs_create and dir.[ch]::ntfs_delete. (Yura)
|
||||
- Add @creation_time field to struct ntfs_inode and rename [acm]time
|
||||
fields to @last_{data_change,mft_change,access}_time. Update them in
|
||||
STANDARD_INFORMATION and FILE_NAMEs during inode sync. (Yura)
|
||||
- layout.h: Add @v1_end and @v3_end markers for offsetof to struct
|
||||
FILE_NAME_ATTR. Change type of @clusters_per_index_block from u8 to
|
||||
s8, fix error in comment. Simplify a bit INDEX_ENTRY struct. (Yura)
|
||||
- ntfstime.h: Use cpu_to_sle64 and sle64_to_cpu in utc2ntfs and
|
||||
ntfs2utc respectively. Update all users. (Yura)
|
||||
- Add @indx_record_size and @inx_record_size_bits to struct ntfs_volume.
|
||||
Set them during mount. (Yura)
|
||||
- attrib.c: Set RESIDENT_ATTR_IS_INDEXED flag for FILE_NAME attribute in
|
||||
ntfs_attr_add. (Yura)
|
||||
- inode.c: Do not sync STANDARD_INFORMATION and FILE_NAMEs for
|
||||
freed inodes. (Yura)
|
||||
- mft.c: Set *time and *size fields of struct ntfs_inode in
|
||||
ntfs_mft_record_alloc. (Yura)
|
||||
- Make ntfsmount use new APIs for file and directory creation/deletion.
|
||||
Implement utime operation. (Yura)
|
||||
|
||||
08/08/2005 - 1.11.2 - ntfsdecrypt now works and lots of fixes and improvements.
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* dir.h - Exports for directory handling. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* 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
|
||||
|
@ -39,6 +40,11 @@ extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
|||
extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
|
||||
const char *pathname);
|
||||
|
||||
extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
|
||||
const unsigned type);
|
||||
extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
|
||||
u8 name_len);
|
||||
|
||||
/*
|
||||
* File types (adapted from include <linux/fs.h>)
|
||||
*/
|
||||
|
|
|
@ -90,10 +90,15 @@ typedef struct {
|
|||
extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
|
||||
ntfschar *name, u32 name_len);
|
||||
extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
|
||||
extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
|
||||
|
||||
extern int ntfs_index_lookup(const void *key, const int key_len,
|
||||
ntfs_index_context *ictx);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* ntfs_index_entry_mark_dirty - mark an index entry dirty
|
||||
* @ictx: ntfs index context describing the index entry
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* inode.h - Defines for NTFS inode handling. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2001,2002 Anton Altaparmakov
|
||||
* Copyright (c) 2004 Yura Pakhuchiy
|
||||
* Copyright (c) 2004-2005 Yura Pakhuchiy
|
||||
*
|
||||
* 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
|
||||
|
@ -144,9 +144,10 @@ struct _ntfs_inode {
|
|||
s64 data_size;
|
||||
s64 allocated_size;
|
||||
|
||||
time_t atime; /* Last access to the data within the file. */
|
||||
time_t mtime; /* Last change of the data within the file. */
|
||||
time_t ctime; /* Last change of the metadata of the file. */
|
||||
time_t creation_time;
|
||||
time_t last_data_change_time;
|
||||
time_t last_mft_change_time;
|
||||
time_t last_access_time;
|
||||
};
|
||||
|
||||
extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol);
|
||||
|
|
|
@ -883,8 +883,11 @@ typedef struct {
|
|||
/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */
|
||||
/* 36*/ union {
|
||||
/* NTFS 1.2 (and previous, presumably) */
|
||||
/* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte
|
||||
boundary. */
|
||||
struct {
|
||||
/* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte
|
||||
boundary. */
|
||||
/* 48 */ void *v1_end[0]; /* Marker for offsetof(). */
|
||||
} __attribute__ ((__packed__));
|
||||
/* sizeof() = 48 bytes */
|
||||
/* NTFS 3.0 */
|
||||
struct {
|
||||
|
@ -937,6 +940,7 @@ typedef struct {
|
|||
partition. This, in contrast to disabling the
|
||||
journal is a very fast process, so the user
|
||||
won't even notice it. */
|
||||
/* 72*/ void *v3_end[0]; /* Marker for offsetof(). */
|
||||
} __attribute__ ((__packed__));
|
||||
} __attribute__ ((__packed__));
|
||||
/* sizeof() = 72 bytes (NTFS 3.0) */
|
||||
|
@ -1989,10 +1993,10 @@ typedef struct {
|
|||
this must be COLLATION_FILE_NAME. */
|
||||
u32 index_block_size; /* Size of each index block in bytes (in
|
||||
the index allocation attribute). */
|
||||
u8 clusters_per_index_block; /* Cluster size of each index block (in
|
||||
s8 clusters_per_index_block; /* Cluster size of each index block (in
|
||||
the index allocation attribute), when
|
||||
an index block is >= than a cluster,
|
||||
otherwise this will be the log of
|
||||
otherwise this will be the -log of
|
||||
the size (like how the encoding of
|
||||
the mft record size and the index
|
||||
record size found in the boot sector
|
||||
|
@ -2168,13 +2172,11 @@ typedef struct {
|
|||
*/
|
||||
typedef struct {
|
||||
/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */
|
||||
union {
|
||||
struct { /* Only valid when INDEX_ENTRY_END is not set. */
|
||||
MFT_REF indexed_file; /* The mft reference of the file
|
||||
union { /* Only valid when INDEX_ENTRY_END is not set. */
|
||||
MFT_REF indexed_file; /* The mft reference of the file
|
||||
described by this index
|
||||
entry. Used for directory
|
||||
indexes. */
|
||||
} __attribute__ ((__packed__));
|
||||
struct { /* Used for views/indexes to find the entry's data. */
|
||||
u16 data_offset; /* Data byte offset from this
|
||||
INDEX_ENTRY. Follows the
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
*/
|
||||
static __inline__ time_t ntfs2utc(s64 ntfs_time)
|
||||
{
|
||||
return (ntfs_time - (NTFS_TIME_OFFSET)) / 10000000;
|
||||
return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +61,7 @@ static __inline__ time_t ntfs2utc(s64 ntfs_time)
|
|||
static __inline__ s64 utc2ntfs(time_t utc_time)
|
||||
{
|
||||
/* Convert to 100ns intervals and then add the NTFS time offset. */
|
||||
return (s64)utc_time * 10000000 + NTFS_TIME_OFFSET;
|
||||
return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET);
|
||||
}
|
||||
|
||||
#endif /* _NTFS_NTFSTIME_H */
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* volume.h - Exports for NTFS volume handling. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* 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
|
||||
|
@ -124,8 +125,10 @@ struct _ntfs_volume {
|
|||
u8 sector_size_bits; /* Log(2) of the byte size of a sector. */
|
||||
u32 cluster_size; /* Byte size of a cluster. */
|
||||
u32 mft_record_size; /* Byte size of a mft record. */
|
||||
u32 indx_record_size; /* Byte size of a INDX record. */
|
||||
u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */
|
||||
u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */
|
||||
u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */
|
||||
|
||||
/* Variables used by the cluster and mft allocators. */
|
||||
u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
|
||||
|
|
|
@ -2573,7 +2573,10 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
|||
a->instance = m->next_attr_instance;
|
||||
a->value_length = 0;
|
||||
a->value_offset = cpu_to_le16(length);
|
||||
a->resident_flags = 0;
|
||||
if (type == AT_FILE_NAME)
|
||||
a->resident_flags = RESIDENT_ATTR_IS_INDEXED;
|
||||
else
|
||||
a->resident_flags = 0;
|
||||
if (name_len)
|
||||
memcpy((u8*)a + le16_to_cpu(a->name_offset),
|
||||
name, sizeof(ntfschar) * name_len);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* bootsect.c - Boot sector handling code. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* 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
|
||||
|
@ -225,7 +226,7 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
|||
if (c < 0)
|
||||
vol->mft_record_size = 1 << -c;
|
||||
else
|
||||
vol->mft_record_size = vol->cluster_size * c;
|
||||
vol->mft_record_size = c << vol->cluster_size_bits;
|
||||
if (vol->mft_record_size & (vol->mft_record_size - 1)) {
|
||||
Dprintf("Error: %s is not a valid NTFS partition! "
|
||||
"mft_record_size is not a power of 2.\n",
|
||||
|
@ -235,6 +236,16 @@ int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
|
|||
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
|
||||
Dprintf("MftRecordSize = 0x%x\n", (unsigned)vol->mft_record_size);
|
||||
Dprintf("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
|
||||
/* Same as above for INDX record. */
|
||||
c = bs->clusters_per_index_record;
|
||||
Dprintf("ClustersPerINDXRecord = 0x%x\n", c);
|
||||
if (c < 0)
|
||||
vol->indx_record_size = 1 << -c;
|
||||
else
|
||||
vol->indx_record_size = c << vol->cluster_size_bits;
|
||||
vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
|
||||
Dprintf("INDXRecordSize = 0x%x\n", (unsigned)vol->indx_record_size);
|
||||
Dprintf("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
|
||||
/*
|
||||
* Work out the size of the MFT mirror in number of mft records. If the
|
||||
* cluster size is less than or equal to the size taken by four mft
|
||||
|
|
358
libntfs/dir.c
358
libntfs/dir.c
|
@ -2,6 +2,7 @@
|
|||
* dir.c - Directory handling code. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2002-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* 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,6 +38,10 @@
|
|||
#include "inode.h"
|
||||
#include "dir.h"
|
||||
#include "volume.h"
|
||||
#include "mft.h"
|
||||
#include "index.h"
|
||||
#include "ntfstime.h"
|
||||
#include "lcnalloc.h"
|
||||
|
||||
/*
|
||||
* The little endian Unicode string "$I30" as a global constant.
|
||||
|
@ -1054,3 +1059,356 @@ err_out:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_create - create file or directory on ntfs volume
|
||||
* @dir_ni: ntfs inode for directory in which create new object
|
||||
* @name: unicode name of new object
|
||||
* @name_len: length of the name in unicode characters
|
||||
* @type: type of the object to create
|
||||
*
|
||||
* @type can be either NTFS_DT_REG to create regular file or NTFS_DT_DIR to
|
||||
* create directory, other valuer are invalid.
|
||||
*
|
||||
* Return opened ntfs inode that describes created file on success or NULL
|
||||
* on error with errno set to the error code.
|
||||
*/
|
||||
ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
|
||||
const unsigned type)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
ntfs_attr *na;
|
||||
FILE_NAME_ATTR *fn = NULL;
|
||||
STANDARD_INFORMATION *si = NULL;
|
||||
int err, fn_len, si_len;
|
||||
|
||||
ntfs_debug("Entering.");
|
||||
/* Sanity checks. */
|
||||
if (!dir_ni || !name || !name_len ||
|
||||
(type != NTFS_DT_REG && type != NTFS_DT_DIR)) {
|
||||
ntfs_error(, "Invalid arguments.");
|
||||
return NULL;
|
||||
}
|
||||
/* Allocate MFT record for new file. */
|
||||
ni = ntfs_mft_record_alloc(dir_ni->vol, NULL);
|
||||
if (!ni) {
|
||||
ntfs_error(, "Failed to allocate new MFT record.");
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION
|
||||
* version 1.2, windows will upgrade it to version 3 if needed.
|
||||
*/
|
||||
si_len = offsetof(STANDARD_INFORMATION, v1_end);
|
||||
si = calloc(1, si_len);
|
||||
if (!si) {
|
||||
err = errno;
|
||||
ntfs_error(, "Not enough memory.");
|
||||
goto err_out;
|
||||
}
|
||||
si->creation_time = utc2ntfs(ni->creation_time);
|
||||
si->last_data_change_time = utc2ntfs(ni->last_data_change_time);
|
||||
si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
|
||||
si->last_access_time = utc2ntfs(ni->last_access_time);
|
||||
/* Add STANDARD_INFORMATION to inode. */
|
||||
na = ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, si_len);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to add STANDARD_INFORMATION attribute.");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_pwrite(na, 0, si_len, si) != si_len) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to initialize STANDARD_INFORMATION "
|
||||
"attribute.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
if (type == NTFS_DT_DIR) {
|
||||
INDEX_ROOT *ir = NULL;
|
||||
INDEX_ENTRY *ie;
|
||||
int ir_len, index_len;
|
||||
|
||||
/* Create INDEX_ROOT attribute. */
|
||||
index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER);
|
||||
ir_len = offsetof(INDEX_ROOT, index) + index_len;
|
||||
ir = calloc(1, ir_len);
|
||||
if (!ir) {
|
||||
err = errno;
|
||||
ntfs_error(, "Not enough memory.");
|
||||
goto err_out;
|
||||
}
|
||||
ir->type = AT_FILE_NAME;
|
||||
ir->collation_rule = COLLATION_FILE_NAME;
|
||||
ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size);
|
||||
if (ni->vol->cluster_size <= ni->vol->indx_record_size)
|
||||
ir->clusters_per_index_block =
|
||||
ni->vol->indx_record_size >>
|
||||
ni->vol->cluster_size_bits;
|
||||
else
|
||||
ir->clusters_per_index_block =
|
||||
-ni->vol->indx_record_size_bits;
|
||||
ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER));
|
||||
ir->index.index_length = cpu_to_le32(index_len);
|
||||
ir->index.allocated_size = cpu_to_le32(index_len);
|
||||
ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT));
|
||||
ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER));
|
||||
ie->key_length = 0;
|
||||
ie->flags = INDEX_ENTRY_END;
|
||||
/* Add INDEX_ROOT attribute to inode. */
|
||||
na = ntfs_attr_add(ni, AT_INDEX_ROOT, I30, 4, ir_len);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
free(ir);
|
||||
ntfs_error(, "Failed to add INDEX_ROOT attribute.");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_pwrite(na, 0, ir_len, ir) != ir_len) {
|
||||
err = errno;
|
||||
free(ir);
|
||||
ntfs_error(, "Failed to initialize INDEX_ROOT.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
} else {
|
||||
/* Add DATA attribute to inode. */
|
||||
na = ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, 0);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to add DATA attribute.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
/* Create FILE_NAME attribute. */
|
||||
fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
|
||||
fn = calloc(1, fn_len);
|
||||
if (!fn) {
|
||||
err = errno;
|
||||
ntfs_error(, "Not enough memory.");
|
||||
goto err_out;
|
||||
}
|
||||
fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
|
||||
le16_to_cpu(dir_ni->mrec->sequence_number));
|
||||
fn->file_name_length = name_len;
|
||||
fn->file_name_type = FILE_NAME_POSIX;
|
||||
if (type == NTFS_DT_DIR)
|
||||
fn->file_attributes = FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT;
|
||||
fn->creation_time = utc2ntfs(ni->creation_time);
|
||||
fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
|
||||
fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
|
||||
fn->last_access_time = utc2ntfs(ni->last_access_time);
|
||||
memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
|
||||
/* Add FILE_NAME attribute to inode. */
|
||||
na = ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, fn_len);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to add FILE_NAME attribute.");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_pwrite(na, 0, fn_len, fn) != fn_len) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to initialize FILE_NAME attribute.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
/* Add FILE_NAME attribute to index. */
|
||||
if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
|
||||
le16_to_cpu(ni->mrec->sequence_number)))) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to add entry to the index.");
|
||||
goto err_out;
|
||||
}
|
||||
/* Set hard links count and directory flag. */
|
||||
ni->mrec->link_count = cpu_to_le16(1);
|
||||
if (type == NTFS_DT_DIR)
|
||||
ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY;
|
||||
ntfs_inode_mark_dirty(ni);
|
||||
/* Done! */
|
||||
free(fn);
|
||||
free(si);
|
||||
ntfs_debug("Done.");
|
||||
return ni;
|
||||
err_out:
|
||||
ntfs_debug("Failed.");
|
||||
if (ntfs_mft_record_free(ni->vol, ni))
|
||||
ntfs_error(, "Failed to free MFT record. "
|
||||
"Leaving inconsist metadata. Run chkdsk.");
|
||||
if (fn)
|
||||
free(fn);
|
||||
if (si)
|
||||
free(si);
|
||||
errno = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_delete - delete file or directory from ntfs volume
|
||||
* @ni: ntfs inode for object to delte
|
||||
* @dir_ni: ntfs inode for directory in which delete object
|
||||
* @name: unicode name of the object to delete
|
||||
* @name_len: length of the name in unicode characters
|
||||
*
|
||||
* @ni is always closed after using of this function (even if it failed),
|
||||
* user do not need to call ntfs_inode_close himself.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code.
|
||||
*/
|
||||
int ntfs_delete(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;
|
||||
int err = 0;
|
||||
|
||||
ntfs_debug("Entering.");
|
||||
if (!ni || !dir_ni || !name || !name_len) {
|
||||
ntfs_error(, "Invalid arguments.");
|
||||
errno = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
if (ni->nr_extents == -1)
|
||||
ni = ni->base_ni;
|
||||
if (dir_ni->nr_extents == -1)
|
||||
dir_ni = dir_ni->base_ni;
|
||||
/*
|
||||
* Search for FILE_NAME attribute with such name. If it's in POSIX or
|
||||
* WIN32_AND_DOS namespace, then simply remove it from index and inode.
|
||||
* If filename in DOS or in WIN32 namespace, then remove DOS name first,
|
||||
* only then remove WIN32 name. Mark WIN32 name as POSIX name to prevent
|
||||
* chkdsk to complain about DOS name absentation, in case if DOS name
|
||||
* had been successfully deleted, but WIN32 name removing failed.
|
||||
*/
|
||||
actx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (!actx)
|
||||
goto err_out;
|
||||
search:
|
||||
while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
|
||||
0, NULL, 0, actx)) {
|
||||
errno = 0;
|
||||
fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
|
||||
le16_to_cpu(actx->attr->value_offset));
|
||||
if (looking_for_dos_name && fn->file_name_type == FILE_NAME_DOS)
|
||||
break;
|
||||
if (looking_for_win32_name &&
|
||||
fn->file_name_type == FILE_NAME_WIN32)
|
||||
break;
|
||||
if (dir_ni->mft_no == MREF_LE(fn->parent_directory) &&
|
||||
ntfs_names_are_equal(fn->file_name,
|
||||
fn->file_name_length, name,
|
||||
name_len, (fn->file_name_type ==
|
||||
FILE_NAME_POSIX) ? CASE_SENSITIVE : IGNORE_CASE,
|
||||
ni->vol->upcase, ni->vol->upcase_len)) {
|
||||
if (fn->file_name_type == FILE_NAME_WIN32) {
|
||||
looking_for_dos_name = TRUE;
|
||||
ntfs_attr_reinit_search_ctx(actx);
|
||||
continue;
|
||||
}
|
||||
if (fn->file_name_type == FILE_NAME_DOS)
|
||||
looking_for_dos_name = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (errno)
|
||||
goto err_out;
|
||||
/* Search for such FILE_NAME in index. */
|
||||
ictx = ntfs_index_ctx_get(dir_ni, I30, 4);
|
||||
if (!ictx)
|
||||
goto err_out;
|
||||
if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->value_length), ictx))
|
||||
goto err_out;
|
||||
/* Set namespace to POSIX for WIN32 name. */
|
||||
if (fn->file_name_type == FILE_NAME_WIN32) {
|
||||
fn->file_name_type = FILE_NAME_POSIX;
|
||||
ntfs_inode_mark_dirty(actx->ntfs_ino);
|
||||
((FILE_NAME_ATTR*)ictx->data)->file_name_type = FILE_NAME_POSIX;
|
||||
ntfs_index_entry_mark_dirty(ictx);
|
||||
}
|
||||
/* Do not support reparse oint deletion yet. */
|
||||
if (((FILE_NAME_ATTR*)ictx->data)->file_attributes &
|
||||
FILE_ATTR_REPARSE_POINT) {
|
||||
errno = EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
/* Remove FILE_NAME from index. */
|
||||
if (ntfs_index_rm(ictx))
|
||||
goto err_out;
|
||||
ictx = NULL;
|
||||
/* Remove FILE_NAME from inode. */
|
||||
if (ntfs_attr_record_rm(actx))
|
||||
goto err_out;
|
||||
/* Decerement hard link count. */
|
||||
ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
|
||||
ni->mrec->link_count) - 1);
|
||||
ntfs_inode_mark_dirty(ni);
|
||||
if (looking_for_dos_name) {
|
||||
looking_for_dos_name = FALSE;
|
||||
looking_for_win32_name = TRUE;
|
||||
ntfs_attr_reinit_search_ctx(actx);
|
||||
goto search;
|
||||
}
|
||||
/*
|
||||
* If hard link count is not equal to zero then we are done. In other
|
||||
* case there are no reference to this inode left, so we should free all
|
||||
* non-resident atributes and mark inode as not in use.
|
||||
*/
|
||||
if (ni->mrec->link_count)
|
||||
goto out;
|
||||
ntfs_attr_reinit_search_ctx(actx);
|
||||
while (!ntfs_attrs_walk(actx)) {
|
||||
if (actx->attr->non_resident) {
|
||||
runlist *rl;
|
||||
|
||||
rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr,
|
||||
NULL);
|
||||
if (!rl) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to decompress runlist. "
|
||||
"Leaving inconsist metadata.");
|
||||
continue;
|
||||
}
|
||||
if (ntfs_cluster_free_from_rl(ni->vol, rl)) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to free clusters. "
|
||||
"Leaving inconsist metadata.");
|
||||
continue;
|
||||
}
|
||||
free(rl);
|
||||
}
|
||||
}
|
||||
if (errno != ENOENT) {
|
||||
err = errno;
|
||||
ntfs_error(, "Atribute enumeration failed. "
|
||||
"Probably leaving inconsist metadata.");
|
||||
}
|
||||
/* All extents should be attached after attribute walk. */
|
||||
while (ni->nr_extents)
|
||||
if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to free extent MFT record. "
|
||||
"Leaving inconsist metadata.");
|
||||
}
|
||||
if (ntfs_mft_record_free(ni->vol, ni)) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to free base MFT record. "
|
||||
"Leaving inconsist metadata.");
|
||||
}
|
||||
ni = NULL;
|
||||
out:
|
||||
if (actx)
|
||||
ntfs_attr_put_search_ctx(actx);
|
||||
if (ictx)
|
||||
ntfs_index_ctx_put(ictx);
|
||||
if (ni)
|
||||
ntfs_inode_close(ni);
|
||||
if (err) {
|
||||
ntfs_error(, "Failed.");
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
ntfs_debug("Done.");
|
||||
return 0;
|
||||
err_out:
|
||||
err = errno;
|
||||
goto out;
|
||||
}
|
||||
|
|
229
libntfs/index.c
229
libntfs/index.c
|
@ -27,6 +27,7 @@
|
|||
#include "debug.h"
|
||||
#include "index.h"
|
||||
#include "mst.h"
|
||||
#include "dir.h"
|
||||
|
||||
/**
|
||||
* ntfs_index_ctx_get - allocate and initialize a new index context
|
||||
|
@ -90,6 +91,42 @@ void ntfs_index_ctx_put(ntfs_index_context *ictx)
|
|||
free(ictx);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_index_ctx_reinit - reinitialize an index context
|
||||
* @ictx: index context to reinitialize
|
||||
*
|
||||
* Reintialize the index context @ictx so it can be used for ntfs_index_lookup.
|
||||
*/
|
||||
void ntfs_index_ctx_reinit(ntfs_index_context *ictx)
|
||||
{
|
||||
if (ictx->entry) {
|
||||
if (ictx->is_in_root) {
|
||||
if (ictx->actx)
|
||||
ntfs_attr_put_search_ctx(ictx->actx);
|
||||
} else {
|
||||
/* Write out index block it it's dirty. */
|
||||
if (ictx->ia_dirty) {
|
||||
if (ntfs_attr_mst_pwrite(ictx->ia_na,
|
||||
ictx->ia_vcn <<
|
||||
ictx->ni->vol->
|
||||
cluster_size_bits,
|
||||
1, ictx->block_size,
|
||||
ictx->ia) != 1)
|
||||
ntfs_error(, "Failed to write out "
|
||||
"index block.");
|
||||
}
|
||||
/* Free resources. */
|
||||
free(ictx->ia);
|
||||
ntfs_attr_close(ictx->ia_na);
|
||||
}
|
||||
}
|
||||
*ictx = (ntfs_index_context) {
|
||||
.ni = ictx->ni,
|
||||
.name = ictx->name,
|
||||
.name_len = ictx->name_len,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_index_lookup - find a key in an index and return its index entry
|
||||
* @key: [IN] key for which to search in the index
|
||||
|
@ -397,3 +434,195 @@ idx_err_out:
|
|||
goto err_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_index_add_filename - add filename to directory index
|
||||
* @ni: ntfs inode describing directory to which index add filename
|
||||
* @fn: FILE_NAME attribute to add
|
||||
* @mref: reference of the inode which @fn describes
|
||||
*
|
||||
* NOTE: This function does not support all cases, so it can fail with
|
||||
* EOPNOTSUPP error code.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code.
|
||||
*/
|
||||
int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref)
|
||||
{
|
||||
ntfs_index_context *ictx;
|
||||
INDEX_ENTRY *ie;
|
||||
INDEX_HEADER *ih;
|
||||
int err, fn_size, ie_size, allocated_size = 0;
|
||||
|
||||
ntfs_debug("Entering.");
|
||||
if (!ni || !fn) {
|
||||
ntfs_error(, "Invalid arguments.");
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
ictx = ntfs_index_ctx_get(ni, I30, 4);
|
||||
if (!ictx)
|
||||
return -1;
|
||||
fn_size = (fn->file_name_length * sizeof(ntfschar)) +
|
||||
sizeof(FILE_NAME_ATTR);
|
||||
ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7;
|
||||
retry:
|
||||
/* Find place where insert new entry. */
|
||||
if (!ntfs_index_lookup(fn, fn_size, ictx)) {
|
||||
err = EEXIST;
|
||||
ntfs_error(, "Index already have such entry.");
|
||||
goto err_out;
|
||||
}
|
||||
if (errno != ENOENT) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to find place where to insert new entry.");
|
||||
goto err_out;
|
||||
}
|
||||
/* Some setup. */
|
||||
if (ictx->is_in_root)
|
||||
ih = &ictx->ir->index;
|
||||
else
|
||||
ih = &ictx->ia->index;
|
||||
if (!allocated_size)
|
||||
allocated_size = le16_to_cpu(ih->allocated_size);
|
||||
/* Check whether we have enough space in the index. */
|
||||
if (le16_to_cpu(ih->index_length) + ie_size > allocated_size) {
|
||||
/* If we in the index root try to resize it. */
|
||||
if (ictx->is_in_root) {
|
||||
ntfs_attr *na;
|
||||
|
||||
allocated_size = le16_to_cpu(ih->index_length) +
|
||||
ie_size;
|
||||
na = ntfs_attr_open(ictx->ni, AT_INDEX_ROOT, ictx->name,
|
||||
ictx->name_len);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to open INDEX_ROOT.");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, allocated_size + offsetof(
|
||||
INDEX_ROOT, index))) {
|
||||
err = EOPNOTSUPP;
|
||||
ntfs_attr_close(na);
|
||||
ntfs_error(, "Failed to truncate INDEX_ROOT.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
ntfs_index_ctx_reinit(ictx);
|
||||
goto retry;
|
||||
}
|
||||
ntfs_debug("Not implemented case.");
|
||||
err = EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
/* Update allocated size if we in INDEX_ROOT. */
|
||||
if (ictx->is_in_root)
|
||||
ih->allocated_size = cpu_to_le16(allocated_size);
|
||||
/* Create entry. */
|
||||
ie = calloc(1, ie_size);
|
||||
if (!ie) {
|
||||
err = errno;
|
||||
goto err_out;
|
||||
}
|
||||
ie->indexed_file = cpu_to_le64(mref);
|
||||
ie->length = cpu_to_le16(ie_size);
|
||||
ie->key_length = cpu_to_le16(fn_size);
|
||||
memcpy(&ie->key, fn, fn_size);
|
||||
/* Update index length, move following entries forard and copy entry. */
|
||||
ih->index_length = cpu_to_le16(le16_to_cpu(ih->index_length) + ie_size);
|
||||
memmove((u8*)ictx->entry + ie_size, ictx->entry,
|
||||
le16_to_cpu(ih->index_length) -
|
||||
((u8*)ictx->entry - (u8*)ih) - ie_size);
|
||||
memcpy(ictx->entry, ie, ie_size);
|
||||
/* Done! */
|
||||
ntfs_index_entry_mark_dirty(ictx);
|
||||
ntfs_index_ctx_put(ictx);
|
||||
free(ie);
|
||||
ntfs_debug("Done.");
|
||||
return 0;
|
||||
err_out:
|
||||
ntfs_debug("Failed.");
|
||||
ntfs_index_ctx_put(ictx);
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_index_rm - remove entry from the index
|
||||
* @ictx: index context describing entry to delete
|
||||
*
|
||||
* Delete entry described by @ictx from the index. NOTE: This function does not
|
||||
* support all cases, so it can fail with EOPNOTSUPP error code. In any case
|
||||
* index context is always reinitialized after use of this function, so it can
|
||||
* be used for index lookup once again.
|
||||
*
|
||||
* Return 0 on success or -1 on error with errno set to the error code.
|
||||
*/
|
||||
int ntfs_index_rm(ntfs_index_context *ictx)
|
||||
{
|
||||
INDEX_HEADER *ih;
|
||||
u32 new_index_length;
|
||||
int err;
|
||||
|
||||
ntfs_debug("Entering.");
|
||||
if (!ictx || (!ictx->ia && !ictx->ir) ||
|
||||
ictx->entry->flags & INDEX_ENTRY_END) {
|
||||
ntfs_error(, "Invalid arguments.");
|
||||
err = EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
if (ictx->is_in_root)
|
||||
ih = &ictx->ir->index;
|
||||
else
|
||||
ih = &ictx->ia->index;
|
||||
/* Don't support deletion of entries with subnodes yet. */
|
||||
if (ictx->entry->flags & INDEX_ENTRY_NODE) {
|
||||
err = EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
/* Calculate new length of the index. */
|
||||
new_index_length = le32_to_cpu(ih->index_length) -
|
||||
le16_to_cpu(ictx->entry->length);
|
||||
/* Don't support deletion of the last entry in the allocation block. */
|
||||
if (!ictx->is_in_root && (new_index_length <=
|
||||
le32_to_cpu(ih->entries_offset) +
|
||||
sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN))) {
|
||||
err = EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
/* Update index length and remove index entry. */
|
||||
ih->index_length = cpu_to_le32(new_index_length);
|
||||
if (ictx->is_in_root)
|
||||
ih->allocated_size = ih->index_length;
|
||||
memmove(ictx->entry, (u8*)ictx->entry + le16_to_cpu(
|
||||
ictx->entry->length), new_index_length -
|
||||
((u8*)ictx->entry - (u8*)ih));
|
||||
ntfs_index_entry_mark_dirty(ictx);
|
||||
/* Resize INDEX_ROOT attribute. */
|
||||
if (ictx->is_in_root) {
|
||||
ntfs_attr *na;
|
||||
|
||||
na = ntfs_attr_open(ictx->ni, AT_INDEX_ROOT, ictx->name,
|
||||
ictx->name_len);
|
||||
if (!na) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to open INDEX_ROOT attribute. "
|
||||
"Leaving inconsist metadata.");
|
||||
goto err_out;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, new_index_length + offsetof(
|
||||
INDEX_ROOT, index))) {
|
||||
err = errno;
|
||||
ntfs_error(, "Failed to truncate INDEX_ROOT attribute. "
|
||||
" Leaving inconsist metadata.");
|
||||
goto err_out;
|
||||
}
|
||||
ntfs_attr_close(na);
|
||||
}
|
||||
ntfs_index_ctx_reinit(ictx);
|
||||
ntfs_debug("Done.");
|
||||
return 0;
|
||||
err_out:
|
||||
ntfs_index_ctx_reinit(ictx);
|
||||
ntfs_debug("Failed.");
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -149,9 +149,10 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
|
|||
NInoSetEncrypted(ni);
|
||||
if (std_info->file_attributes & FILE_ATTR_SPARSE_FILE)
|
||||
NInoSetSparse(ni);
|
||||
ni->mtime = ntfs2utc(sle64_to_cpu(std_info->last_data_change_time));
|
||||
ni->ctime = ntfs2utc(sle64_to_cpu(std_info->last_mft_change_time));
|
||||
ni->atime = ntfs2utc(sle64_to_cpu(std_info->last_access_time));
|
||||
ni->creation_time = ntfs2utc(std_info->creation_time);
|
||||
ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time);
|
||||
ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time);
|
||||
ni->last_access_time = ntfs2utc(std_info->last_access_time);
|
||||
/* Set attribute list information. */
|
||||
if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,
|
||||
ctx)) {
|
||||
|
@ -471,6 +472,10 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
|
|||
std_info->file_attributes |= FILE_ATTR_SPARSE_FILE;
|
||||
else
|
||||
std_info->file_attributes &= ~FILE_ATTR_SPARSE_FILE;
|
||||
std_info->creation_time = utc2ntfs(ni->creation_time);
|
||||
std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time);
|
||||
std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
|
||||
std_info->last_access_time = utc2ntfs(ni->last_access_time);
|
||||
ntfs_inode_mark_dirty(ctx->ntfs_ino);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return 0;
|
||||
|
@ -561,6 +566,10 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni)
|
|||
fn->allocated_size = cpu_to_sle64(ni->allocated_size);
|
||||
if (ni->data_size != -1)
|
||||
fn->data_size = cpu_to_sle64(ni->data_size);
|
||||
fn->creation_time = utc2ntfs(ni->creation_time);
|
||||
fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
|
||||
fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
|
||||
fn->last_access_time = utc2ntfs(ni->last_access_time);
|
||||
ntfs_index_entry_mark_dirty(ictx);
|
||||
ntfs_index_ctx_put(ictx);
|
||||
ntfs_inode_close(index_ni);
|
||||
|
@ -616,7 +625,8 @@ int ntfs_inode_sync(ntfs_inode *ni)
|
|||
__FUNCTION__, (long long) ni->mft_no);
|
||||
|
||||
/* Update STANDARD_INFORMATION. */
|
||||
if (ni->nr_extents != -1 && ntfs_inode_sync_standard_information(ni)) {
|
||||
if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
|
||||
ntfs_inode_sync_standard_information(ni)) {
|
||||
if (!err || errno == EIO) {
|
||||
err = errno;
|
||||
if (err != EIO)
|
||||
|
@ -627,7 +637,8 @@ int ntfs_inode_sync(ntfs_inode *ni)
|
|||
}
|
||||
|
||||
/* Update FILE_NAME's in the index. */
|
||||
if (ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) &&
|
||||
if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
|
||||
NInoFileNameTestAndClearDirty(ni) &&
|
||||
ntfs_inode_sync_file_name(ni)) {
|
||||
if (!err || errno == EIO) {
|
||||
err = errno;
|
||||
|
@ -640,8 +651,8 @@ int ntfs_inode_sync(ntfs_inode *ni)
|
|||
}
|
||||
|
||||
/* Write out attribute list from cache to disk. */
|
||||
if (ni->nr_extents != -1 && NInoAttrList(ni) &&
|
||||
NInoAttrListTestAndClearDirty(ni)) {
|
||||
if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
|
||||
NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) {
|
||||
ntfs_attr *na;
|
||||
|
||||
na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* mft.c - Mft record handling code. Part of the Linux-NTFS project.
|
||||
*
|
||||
* Copyright (c) 2000-2004 Anton Altaparmakov
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
*
|
||||
* 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
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
|
@ -1361,7 +1363,7 @@ mft_rec_already_initialized:
|
|||
if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) {
|
||||
ntfs_error(vol->sb, "Mft record 0x%llx was marked unused in "
|
||||
"mft bitmap but is marked used itself. "
|
||||
"Corrupt filesystem or driver bug! "
|
||||
"Corrupt filesystem or library bug! "
|
||||
"Run chkdsk immediately!", (long long)bit);
|
||||
free(m);
|
||||
errno = EIO;
|
||||
|
@ -1435,6 +1437,11 @@ mft_rec_already_initialized:
|
|||
}
|
||||
/* Make sure the allocated inode is written out to disk later. */
|
||||
ntfs_inode_mark_dirty(ni);
|
||||
/* Initialize time, allocated and data size in ntfs_inode struct. */
|
||||
ni->data_size = ni->allocated_size = -1;
|
||||
ni->creation_time = ni->last_data_change_time =
|
||||
ni->last_mft_change_time =
|
||||
ni->last_access_time = time(NULL);
|
||||
/* Update the default mft allocation position if it was used. */
|
||||
if (!base_ni)
|
||||
vol->mft_data_pos = bit + 1;
|
||||
|
|
|
@ -280,7 +280,7 @@ static int parse_options (int argc, char *argv[])
|
|||
*/
|
||||
static char *ntfsinfo_time_to_str(const s64 sle_ntfs_clock)
|
||||
{
|
||||
time_t unix_clock = ntfs2utc(sle64_to_cpu(sle_ntfs_clock));
|
||||
time_t unix_clock = ntfs2utc(sle_ntfs_clock);
|
||||
return ctime(&unix_clock);
|
||||
}
|
||||
|
||||
|
@ -450,11 +450,11 @@ static void ntfs_dump_flags(ATTR_TYPES type, u32 flags)
|
|||
}
|
||||
if (type == AT_FILE_NAME) {
|
||||
if (flags & FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT) {
|
||||
printf(" DIRECTORY");
|
||||
printf(" FILE_NAME_INDEX");
|
||||
flags &= ~FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT;
|
||||
}
|
||||
if (flags & FILE_ATTR_DUP_VIEW_INDEX_PRESENT) {
|
||||
printf(" INDEX_VIEW");
|
||||
printf(" VIEW_INDEX");
|
||||
flags &= ~FILE_ATTR_DUP_VIEW_INDEX_PRESENT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -548,8 +548,7 @@ static int list_dir_entry(ntfsls_dirent * dirent, const ntfschar * name,
|
|||
if (!file_name_attr)
|
||||
goto release;
|
||||
|
||||
ntfs_time = ntfs2utc(sle64_to_cpu(
|
||||
file_name_attr->last_data_change_time));
|
||||
ntfs_time = ntfs2utc(file_name_attr->last_data_change_time);
|
||||
strcpy(t_buf, ctime(&ntfs_time));
|
||||
memmove(t_buf+16, t_buf+19, 5);
|
||||
t_buf[21] = '\0';
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_SETXATTR
|
||||
#include <sys/xattr.h>
|
||||
|
@ -285,9 +286,9 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
|
|||
stbuf->st_uid = ctx->uid;
|
||||
stbuf->st_gid = ctx->gid;
|
||||
stbuf->st_ino = ni->mft_no;
|
||||
stbuf->st_atime = ni->atime;
|
||||
stbuf->st_ctime = ni->ctime;
|
||||
stbuf->st_mtime = ni->mtime;
|
||||
stbuf->st_atime = ni->last_access_time;
|
||||
stbuf->st_ctime = ni->last_mft_change_time;
|
||||
stbuf->st_mtime = ni->last_data_change_time;
|
||||
ntfs_inode_close(ni);
|
||||
} else
|
||||
res = -ENOENT;
|
||||
|
@ -518,11 +519,86 @@ static int ntfs_fuse_chmod(const char *path __attribute__((unused)),
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_create(const char *org_path, const unsigned type)
|
||||
{
|
||||
char *name;
|
||||
ntfschar *uname = NULL;
|
||||
ntfs_inode *dir_ni = NULL, *ni;
|
||||
char *path;
|
||||
int res = 0, uname_len;
|
||||
|
||||
path = strdup(org_path);
|
||||
if (!path)
|
||||
return -errno;
|
||||
/* Generate unicode filename. */
|
||||
name = strrchr(path, '/');
|
||||
name++;
|
||||
uname_len = ntfs_mbstoucs(name, &uname, 0);
|
||||
if (uname_len < 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
/* Open parent directory. */
|
||||
*name = 0;
|
||||
dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!dir_ni) {
|
||||
res = -errno;
|
||||
if (res == -ENOENT)
|
||||
res = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
/* Create object specified in @type. */
|
||||
ni = ntfs_create(dir_ni, uname, uname_len, type);
|
||||
if (ni)
|
||||
ntfs_inode_close(ni);
|
||||
else
|
||||
res = -errno;
|
||||
exit:
|
||||
if (uname)
|
||||
free(uname);
|
||||
if (dir_ni)
|
||||
ntfs_inode_close(dir_ni);
|
||||
free(path);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_create_stream(const char *path,
|
||||
ntfschar *stream_name, const int stream_name_len)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
ntfs_attr *na;
|
||||
int res = 0;
|
||||
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!ni) {
|
||||
res = -errno;
|
||||
if (res == -ENOENT) {
|
||||
/*
|
||||
* If such file does not exist, create it and try once
|
||||
* again to add stream to it.
|
||||
*/
|
||||
res = ntfs_fuse_create(path, NTFS_DT_REG);
|
||||
if (!res)
|
||||
return ntfs_fuse_create_stream(path,
|
||||
stream_name, stream_name_len);
|
||||
else
|
||||
res = -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
na = ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, 0);
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
else
|
||||
res = -errno;
|
||||
if (ntfs_inode_close(ni))
|
||||
perror("Failed to close inode");
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_mknod(const char *org_path, mode_t mode,
|
||||
dev_t dev __attribute__((unused)))
|
||||
{
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_attr *na;
|
||||
char *path = NULL;
|
||||
ntfschar *stream_name;
|
||||
int stream_name_len;
|
||||
|
@ -533,56 +609,76 @@ static int ntfs_fuse_mknod(const char *org_path, mode_t mode,
|
|||
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
|
||||
if (stream_name_len < 0)
|
||||
return stream_name_len;
|
||||
if (!stream_name_len) {
|
||||
res = -EOPNOTSUPP;
|
||||
goto exit;
|
||||
}
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!ni) {
|
||||
res = -errno;
|
||||
if (res == -ENOENT)
|
||||
res = -EOPNOTSUPP;
|
||||
goto exit;
|
||||
}
|
||||
na = ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, 0);
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
if (!stream_name_len)
|
||||
res = ntfs_fuse_create(path, NTFS_DT_REG);
|
||||
else
|
||||
res = -errno;
|
||||
exit:
|
||||
if (ni && ntfs_inode_close(ni))
|
||||
perror("Failed to close inode");
|
||||
res = ntfs_fuse_create_stream(path, stream_name,
|
||||
stream_name_len);
|
||||
free(path);
|
||||
if (stream_name_len)
|
||||
free(stream_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_rm_file(char *file)
|
||||
static int ntfs_fuse_rm(const char *org_path)
|
||||
{
|
||||
free(file);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
char *name;
|
||||
ntfschar *uname = NULL;
|
||||
ntfs_inode *dir_ni = NULL, *ni;
|
||||
char *path;
|
||||
int res = 0, uname_len;
|
||||
|
||||
static int ntfs_fuse_unlink(const char *org_path)
|
||||
{
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_attr *na;
|
||||
char *path = NULL;
|
||||
ntfschar *stream_name;
|
||||
int stream_name_len;
|
||||
int res = 0;
|
||||
|
||||
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
|
||||
if (stream_name_len < 0)
|
||||
return stream_name_len;
|
||||
if (!stream_name_len)
|
||||
return ntfs_fuse_rm_file(path);
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
path = strdup(org_path);
|
||||
if (!path)
|
||||
return -errno;
|
||||
/* Open object for delete. */
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!ni) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
/* Generate unicode filename. */
|
||||
name = strrchr(path, '/');
|
||||
name++;
|
||||
uname_len = ntfs_mbstoucs(name, &uname, 0);
|
||||
if (uname_len < 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
/* Open parent directory. */
|
||||
*name = 0;
|
||||
dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!dir_ni) {
|
||||
res = -errno;
|
||||
if (res == -ENOENT)
|
||||
res = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
/* Delete object. */
|
||||
if (ntfs_delete(ni, dir_ni, uname, uname_len))
|
||||
res = -errno;
|
||||
ni = NULL;
|
||||
exit:
|
||||
if (ni)
|
||||
ntfs_inode_close(ni);
|
||||
if (uname)
|
||||
free(uname);
|
||||
if (dir_ni)
|
||||
ntfs_inode_close(dir_ni);
|
||||
free(path);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_rm_stream(const char *path, ntfschar *stream_name,
|
||||
const int stream_name_len)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
ntfs_attr *na;
|
||||
int res = 0;
|
||||
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!ni)
|
||||
return -errno;
|
||||
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
|
||||
if (!na) {
|
||||
res = -errno;
|
||||
|
@ -593,14 +689,70 @@ static int ntfs_fuse_unlink(const char *org_path)
|
|||
ntfs_attr_close(na);
|
||||
}
|
||||
exit:
|
||||
if (ni && ntfs_inode_close(ni))
|
||||
if (ntfs_inode_close(ni))
|
||||
perror("Failed to close inode");
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_unlink(const char *org_path)
|
||||
{
|
||||
char *path = NULL;
|
||||
ntfschar *stream_name;
|
||||
int stream_name_len;
|
||||
int res = 0;
|
||||
|
||||
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
|
||||
if (stream_name_len < 0)
|
||||
return stream_name_len;
|
||||
if (!stream_name_len)
|
||||
res = ntfs_fuse_rm(path);
|
||||
else
|
||||
res = ntfs_fuse_rm_stream(path, stream_name, stream_name_len);
|
||||
free(path);
|
||||
if (stream_name_len)
|
||||
free(stream_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_mkdir(const char *path,
|
||||
mode_t mode __attribute__((unused)))
|
||||
{
|
||||
return ntfs_fuse_create(path, NTFS_DT_DIR);
|
||||
}
|
||||
|
||||
static int ntfs_fuse_rmdir(const char *path)
|
||||
{
|
||||
return ntfs_fuse_rm(path);
|
||||
}
|
||||
|
||||
static int ntfs_fuse_utime(const char *path, struct utimbuf *buf)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
|
||||
if (strchr(path, ':'))
|
||||
return 0; /* Unable to change time for named data streams. */
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!ni)
|
||||
return -errno;
|
||||
if (buf) {
|
||||
ni->last_access_time = buf->actime;
|
||||
ni->last_data_change_time = buf->modtime;
|
||||
ni->last_mft_change_time = buf->modtime;
|
||||
} else {
|
||||
time_t now;
|
||||
|
||||
now = time(NULL);
|
||||
ni->last_access_time = now;
|
||||
ni->last_data_change_time = now;
|
||||
ni->last_mft_change_time = now;
|
||||
}
|
||||
NInoFileNameSetDirty(ni);
|
||||
NInoSetDirty(ni);
|
||||
if (ntfs_inode_close(ni))
|
||||
perror("Failed to close inode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETXATTR
|
||||
|
||||
static int ntfs_fuse_getxattr(const char *path, const char *name,
|
||||
|
@ -883,6 +1035,9 @@ static struct fuse_operations ntfs_fuse_oper = {
|
|||
.chmod = ntfs_fuse_chmod,
|
||||
.mknod = ntfs_fuse_mknod,
|
||||
.unlink = ntfs_fuse_unlink,
|
||||
.mkdir = ntfs_fuse_mkdir,
|
||||
.rmdir = ntfs_fuse_rmdir,
|
||||
.utime = ntfs_fuse_utime,
|
||||
#ifdef HAVE_SETXATTR
|
||||
.getxattr = ntfs_fuse_getxattr,
|
||||
#if 0
|
||||
|
|
|
@ -276,7 +276,7 @@ static void ntfs_binary_print (u8 num, BOOL backwards, BOOL colour)
|
|||
*/
|
||||
static const char * ntfsinfo_time_to_str(const s64 sle_ntfs_clock)
|
||||
{
|
||||
time_t unix_clock = ntfs2utc(sle64_to_cpu(sle_ntfs_clock));
|
||||
time_t unix_clock = ntfs2utc(sle_ntfs_clock);
|
||||
if (sle_ntfs_clock == 0)
|
||||
return "none\n";
|
||||
else
|
||||
|
|
|
@ -876,10 +876,10 @@ static int get_filenames (struct ufile *file, ntfs_volume* vol)
|
|||
name->size_data = sle64_to_cpu (attr->data_size);
|
||||
name->flags = attr->file_attributes;
|
||||
|
||||
name->date_c = ntfs2utc (sle64_to_cpu (attr->creation_time));
|
||||
name->date_a = ntfs2utc (sle64_to_cpu (attr->last_data_change_time));
|
||||
name->date_m = ntfs2utc (sle64_to_cpu (attr->last_mft_change_time));
|
||||
name->date_r = ntfs2utc (sle64_to_cpu (attr->last_access_time));
|
||||
name->date_c = ntfs2utc (attr->creation_time);
|
||||
name->date_a = ntfs2utc (attr->last_data_change_time);
|
||||
name->date_m = ntfs2utc (attr->last_mft_change_time);
|
||||
name->date_r = ntfs2utc (attr->last_access_time);
|
||||
|
||||
if (ntfs_ucstombs (name->uname, name->uname_len, &name->name,
|
||||
0) < 0) {
|
||||
|
@ -1052,7 +1052,7 @@ static struct ufile * read_record (ntfs_volume *vol, long long record)
|
|||
if (attr10) {
|
||||
STANDARD_INFORMATION *si;
|
||||
si = (STANDARD_INFORMATION *) ((char *) attr10 + le16_to_cpu (attr10->value_offset));
|
||||
file->date = ntfs2utc (sle64_to_cpu (si->last_data_change_time));
|
||||
file->date = ntfs2utc (si->last_data_change_time);
|
||||
}
|
||||
|
||||
if (attr20 || !attr10)
|
||||
|
|
Loading…
Reference in New Issue