diff --git a/include/ntfs-3g/inode.h b/include/ntfs-3g/inode.h index 26be3577..3abc878c 100644 --- a/include/ntfs-3g/inode.h +++ b/include/ntfs-3g/inode.h @@ -49,6 +49,7 @@ typedef enum { NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated in the index. */ NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ + NI_TimesDirty, /* 1: Times need to be updated */ } ntfs_inode_state_bits; #define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) @@ -194,4 +195,9 @@ extern int ntfs_inode_free_space(ntfs_inode *ni, int size); extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); +extern int ntfs_inode_get_times(const char *path, char *value, + size_t size, ntfs_inode *ni); +extern int ntfs_inode_set_times(const char *path, const char *value, + size_t size, int flags, ntfs_inode *ni); + #endif /* defined _NTFS_INODE_H */ diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c index 4f03121d..fc24bb05 100644 --- a/libntfs-3g/inode.c +++ b/libntfs-3g/inode.c @@ -35,6 +35,9 @@ #ifdef HAVE_ERRNO_H #include #endif +#ifdef HAVE_SETXATTR +#include +#endif #include "compat.h" #include "types.h" @@ -554,10 +557,12 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); std_info->file_attributes = ni->flags; - 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); + if (test_nino_flag(ni, TimesDirty)) { + 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); + } /* JPA update v3.x extensions, ensuring consistency */ @@ -655,10 +660,12 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) (ni->flags & FILE_ATTR_VALID_FLAGS); fn->allocated_size = cpu_to_sle64(ni->allocated_size); 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); + if (test_nino_flag(ni, TimesDirty)) { + 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); if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err) @@ -1137,6 +1144,7 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; + set_nino_flag(ni, TimesDirty); NInoFileNameSetDirty(ni); NInoSetDirty(ni); } @@ -1184,3 +1192,151 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) return ret; } + +/* + * Get high precision NTFS times + * + * They are returned in following order : create, update, access, change + * provided they fit in requested size. + * + * Returns the modified size if successfull (or 32 if buffer size is null) + * -errno if failed + */ + +int ntfs_inode_get_times(const char *path __attribute__((unused)), + char *value, size_t size, ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + u64 *times; + int ret; + + ret = 0; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + if (value && (size >= 8)) { + times = (u64*)value; + times[0] = le64_to_cpu(std_info->creation_time); + ret = 8; + if (size >= 16) { + times[1] = le64_to_cpu(std_info->last_data_change_time); + ret = 16; + } + if (size >= 24) { + times[2] = le64_to_cpu(std_info->last_access_time); + ret = 24; + } + if (size >= 32) { + times[3] = le64_to_cpu(std_info->last_mft_change_time); + ret = 32; + } + } else + if (!size) + ret = 32; + else + ret = -ERANGE; + } + ntfs_attr_put_search_ctx(ctx); + } + return (ret ? ret : -errno); +} + +/* + * Set high precision NTFS times + * + * They are expected in this order : create, update, access + * provided they are present in input. The change time is set to + * current time. + * + * The times are inserted directly in the standard_information and + * file names attributes to avoid manipulating low precision times + * + * Returns 0 if success + * -1 if there were an error (described by errno) + */ + +int ntfs_inode_set_times(const char *path __attribute__((unused)), + const char *value, size_t size, + int flags, ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + STANDARD_INFORMATION *std_info; + FILE_NAME_ATTR *fn; + const u64 *times; + le64 now; + int cnt; + int ret; + + ret = -1; + if ((size >= 8) && !(flags & XATTR_CREATE)) { + times = (const u64*)value; + now = utc2ntfs(time((time_t*)NULL)); + /* update the standard information attribute */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (ctx) { + if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + ntfs_log_perror("Failed to get standard info (inode %lld)", + (long long)ni->mft_no); + } else { + std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + /* + * Do not mark times dirty to avoid + * overwriting them when the inode is closed. + */ + std_info->creation_time = cpu_to_le64(times[0]); + if (size >= 16) + std_info->last_data_change_time = cpu_to_le64(times[1]); + if (size >= 24) + std_info->last_access_time = cpu_to_le64(times[2]); + std_info->last_mft_change_time = now; + ntfs_inode_mark_dirty(ctx->ntfs_ino); + + /* update the file names attributes */ + ntfs_attr_reinit_search_ctx(ctx); + cnt = 0; + while (!ntfs_attr_lookup(AT_FILE_NAME, + AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + /* + * Do not mark times dirty to avoid + * overwriting them when the inode is closed. + */ + fn->creation_time + = cpu_to_le64(times[0]); + if (size >= 16) + fn->last_data_change_time + = cpu_to_le64(times[1]); + if (size >= 24) + fn->last_access_time + = cpu_to_le64(times[2]); + fn->last_mft_change_time = now; + cnt++; + } + if (cnt) + ret = 0; + else { + ntfs_log_perror("Failed to get file names (inode %lld)", + (long long)ni->mft_no); + } + } + ntfs_attr_put_search_ctx(ctx); + } + } else + if (size < 8) + errno = ERANGE; + else + errno = EEXIST; + return (ret); +} diff --git a/libntfs-3g/mft.c b/libntfs-3g/mft.c index 54cffea7..09aaaee9 100644 --- a/libntfs-3g/mft.c +++ b/libntfs-3g/mft.c @@ -1476,6 +1476,7 @@ found_free_rec: ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = ni->last_access_time = time(NULL); + set_nino_flag(ni, TimesDirty); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; @@ -1778,6 +1779,7 @@ found_free_rec: ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = ni->last_access_time = time(NULL); + set_nino_flag(ni, TimesDirty); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 584be82a..d10c4e18 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -2014,6 +2014,7 @@ enum { XATTR_UNMAPPED, XATTR_NTFS_EFSINFO, XATTR_NTFS_REPARSE_DATA, XATTR_NTFS_DOS_NAME, + XATTR_NTFS_TIMES, XATTR_POSIX_ACC, XATTR_POSIX_DEF } ; @@ -2022,6 +2023,7 @@ static const char nf_ns_xattr_attrib[] = "system.ntfs_attrib"; static const char nf_ns_xattr_efsinfo[] = "user.ntfs.efsinfo"; static const char nf_ns_xattr_reparse[] = "system.ntfs_reparse_data"; static const char nf_ns_xattr_dos_name[] = "system.ntfs_dos_name"; +static const char nf_ns_xattr_times[] = "system.ntfs_times"; static const char nf_ns_xattr_posix_access[] = "system.posix_acl_access"; static const char nf_ns_xattr_posix_default[] = "system.posix_acl_default"; @@ -2036,6 +2038,7 @@ static struct XATTRNAME nf_ns_xattr_names[] = { { XATTR_NTFS_EFSINFO, nf_ns_xattr_efsinfo }, { XATTR_NTFS_REPARSE_DATA, nf_ns_xattr_reparse }, { XATTR_NTFS_DOS_NAME, nf_ns_xattr_dos_name }, + { XATTR_NTFS_TIMES, nf_ns_xattr_times }, { XATTR_POSIX_ACC, nf_ns_xattr_posix_access }, { XATTR_POSIX_DEF, nf_ns_xattr_posix_default }, { XATTR_UNMAPPED, (char*)NULL } /* terminator */ @@ -2422,6 +2425,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, res = ntfs_get_ntfs_dos_name(path, value,size,ni); break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_get_times(path, + value,size,ni); + break; default : /* not possible */ break; } @@ -2472,6 +2479,10 @@ static int ntfs_fuse_getxattr(const char *path, const char *name, res = ntfs_get_ntfs_dos_name(path, value,size,ni); break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_get_times(path, + value,size,ni); + break; default : /* * make sure applications do not see @@ -2601,6 +2612,10 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = ntfs_set_ntfs_dos_name(path, value,size,flags,ni); break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_set_times(path, + value,size,flags,ni); + break; default : /* not possible */ break; } @@ -2656,6 +2671,10 @@ static int ntfs_fuse_setxattr(const char *path, const char *name, res = ntfs_set_ntfs_dos_name(path, value,size,flags,ni); break; + case XATTR_NTFS_TIMES: + res = ntfs_inode_set_times(path, + value,size,flags,ni); + break; default : /* * make sure applications do not see @@ -2805,11 +2824,13 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) res = 0; switch (attr) { /* - * Removal of NTFS ACL or ATTRIB is never allowed + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed */ case XATTR_NTFS_ACL : case XATTR_NTFS_ATTRIB : case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : res = -EPERM; break; case XATTR_POSIX_ACC : @@ -2854,11 +2875,13 @@ static int ntfs_fuse_removexattr(const char *path, const char *name) else { switch (attr) { /* - * Removal of NTFS ACL or ATTRIB is never allowed + * Removal of NTFS ACL, ATTRIB, EFSINFO or TIMES + * is never allowed */ case XATTR_NTFS_ACL : case XATTR_NTFS_ATTRIB : case XATTR_NTFS_EFSINFO : + case XATTR_NTFS_TIMES : res = -EPERM; break; case XATTR_NTFS_REPARSE_DATA :