diff --git a/ChangeLog b/ChangeLog index 12103a5f..b23c5506 100644 --- a/ChangeLog +++ b/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. diff --git a/include/ntfs/dir.h b/include/ntfs/dir.h index 59835b5a..ddf624b3 100644 --- a/include/ntfs/dir.h +++ b/include/ntfs/dir.h @@ -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 ) */ diff --git a/include/ntfs/index.h b/include/ntfs/index.h index 5330a61e..c4a78375 100644 --- a/include/ntfs/index.h +++ b/include/ntfs/index.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 diff --git a/include/ntfs/inode.h b/include/ntfs/inode.h index 4718a909..05c4f07c 100644 --- a/include/ntfs/inode.h +++ b/include/ntfs/inode.h @@ -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); diff --git a/include/ntfs/layout.h b/include/ntfs/layout.h index 5d639d9b..2d78b19b 100644 --- a/include/ntfs/layout.h +++ b/include/ntfs/layout.h @@ -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 diff --git a/include/ntfs/ntfstime.h b/include/ntfs/ntfstime.h index d6c9677f..85d8a53c 100644 --- a/include/ntfs/ntfstime.h +++ b/include/ntfs/ntfstime.h @@ -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 */ diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h index a25ef156..95593a6c 100644 --- a/include/ntfs/volume.h +++ b/include/ntfs/volume.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. */ diff --git a/libntfs/attrib.c b/libntfs/attrib.c index e594cfd2..e366c6fb 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -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); diff --git a/libntfs/bootsect.c b/libntfs/bootsect.c index c6a1a36f..4230bcad 100644 --- a/libntfs/bootsect.c +++ b/libntfs/bootsect.c @@ -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 diff --git a/libntfs/dir.c b/libntfs/dir.c index 8dbc7723..fdca85e4 100644 --- a/libntfs/dir.c +++ b/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; +} diff --git a/libntfs/index.c b/libntfs/index.c index 444b4eac..7f85cc3c 100644 --- a/libntfs/index.c +++ b/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; +} diff --git a/libntfs/inode.c b/libntfs/inode.c index 68a8650f..4ceffbe4 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -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); diff --git a/libntfs/mft.c b/libntfs/mft.c index 055c581e..770868a6 100644 --- a/libntfs/mft.c +++ b/libntfs/mft.c @@ -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 #include #include +#include #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; diff --git a/ntfsprogs/ntfsinfo.c b/ntfsprogs/ntfsinfo.c index af0a3fbe..c2744c68 100644 --- a/ntfsprogs/ntfsinfo.c +++ b/ntfsprogs/ntfsinfo.c @@ -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; } } diff --git a/ntfsprogs/ntfsls.c b/ntfsprogs/ntfsls.c index 49dc3ef0..e51fa8f9 100644 --- a/ntfsprogs/ntfsls.c +++ b/ntfsprogs/ntfsls.c @@ -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'; diff --git a/ntfsprogs/ntfsmount.c b/ntfsprogs/ntfsmount.c index 18d15d5e..94a25909 100644 --- a/ntfsprogs/ntfsmount.c +++ b/ntfsprogs/ntfsmount.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef HAVE_SETXATTR #include @@ -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 diff --git a/ntfsprogs/ntfsrm.c b/ntfsprogs/ntfsrm.c index 0c355522..9771f33a 100644 --- a/ntfsprogs/ntfsrm.c +++ b/ntfsprogs/ntfsrm.c @@ -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 diff --git a/ntfsprogs/ntfsundelete.c b/ntfsprogs/ntfsundelete.c index 110e17a6..a0d0a72b 100644 --- a/ntfsprogs/ntfsundelete.c +++ b/ntfsprogs/ntfsundelete.c @@ -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)