diff --git a/include/ntfs/dir.h b/include/ntfs/dir.h index 600bb3a3..5299861b 100644 --- a/include/ntfs/dir.h +++ b/include/ntfs/dir.h @@ -77,7 +77,7 @@ extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, ntfschar *target, u8 target_len); -extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, +extern int ntfs_delete(ntfs_inode **pni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len); extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, diff --git a/include/ntfs/inode.h b/include/ntfs/inode.h index b6a7d0c4..5ad9bc47 100644 --- a/include/ntfs/inode.h +++ b/include/ntfs/inode.h @@ -130,7 +130,7 @@ struct _ntfs_inode { }; /* Below fields are valid only for base inode. */ - s64 data_size; /* Data size stored in the filename index. */ + s64 data_size; /* Data size of unnamed DATA attribute. */ s64 allocated_size; /* Allocated size stored in the filename index. (NOTE: Equal to allocated size of the unnamed data attribute for normal or @@ -172,8 +172,14 @@ static __inline__ void ntfs_inode_mark_dirty(ntfs_inode *ni) NInoSetDirty(ni->base_ni); } -extern void ntfs_inode_update_atime(ntfs_inode *ni); -extern void ntfs_inode_update_time(ntfs_inode *ni); +typedef enum { + NTFS_UPDATE_ATIME = 1 << 0, + NTFS_UPDATE_MTIME = 1 << 1, + NTFS_UPDATE_CTIME = 1 << 2, +} ntfs_time_update_flags; + +extern void ntfs_inode_update_times(ntfs_inode *ni, + ntfs_time_update_flags mask); extern int ntfs_inode_sync(ntfs_inode *ni); diff --git a/include/ntfs/volume.h b/include/ntfs/volume.h index e5f97c93..c7f3e893 100644 --- a/include/ntfs/volume.h +++ b/include/ntfs/volume.h @@ -57,10 +57,9 @@ typedef struct _ntfs_volume ntfs_volume; */ typedef enum { NTFS_MNT_RDONLY = 1, - NTFS_MNT_NOATIME = 2, + NTFS_MNT_FORENSIC = 2, NTFS_MNT_CASE_SENSITIVE = 4, NTFS_MNT_NOT_EXCLUSIVE = 8, - NTFS_MNT_FORENSIC = 16, } ntfs_mount_flags; /** @@ -108,10 +107,6 @@ typedef enum { #define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) #define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) -#define NVolNoATime(nv) test_nvol_flag(nv, NoATime) -#define NVolSetNoATime(nv) set_nvol_flag(nv, NoATime) -#define NVolClearNoATime(nv) clear_nvol_flag(nv, NoATime) - #define NVolWasDirty(nv) test_nvol_flag(nv, WasDirty) #define NVolSetWasDirty(nv) set_nvol_flag(nv, WasDirty) #define NVolClearWasDirty(nv) clear_nvol_flag(nv, WasDirty) diff --git a/libntfs/attrib.c b/libntfs/attrib.c index 100678ad..cccc86b9 100644 --- a/libntfs/attrib.c +++ b/libntfs/attrib.c @@ -864,10 +864,6 @@ s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b) return ntfs_crypto_attr_pread(na, pos, count, b); vol = na->ni->vol; - /* Update access time if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_atime(na->ni); if (!count) return 0; /* Truncate reads beyond end of attribute. */ @@ -1050,10 +1046,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) errno = EOPNOTSUPP; return -1; } - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); if (!count) return 0; /* If the write reaches beyond the end, extend the attribute. */ @@ -5092,10 +5084,6 @@ int __ntfs_attr_truncate(ntfs_attr *na, const s64 newsize, BOOL sparse) ret = ntfs_non_resident_attr_shrink(na, newsize); } else ret = ntfs_resident_attr_resize(na, newsize); - /* Update access and change times if needed. */ - if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || - na->type == AT_INDEX_ALLOCATION) - ntfs_inode_update_time(na->ni); if (!ret) ntfs_log_trace("Done!\n"); else diff --git a/libntfs/dir.c b/libntfs/dir.c index 85c768d9..b04089de 100644 --- a/libntfs/dir.c +++ b/libntfs/dir.c @@ -487,7 +487,7 @@ close_err_out: u64 ntfs_pathname_to_inode_num(ntfs_volume *vol, ntfs_inode *parent, const char *pathname) { - u64 inum, result; + u64 inum, result; int len, err = 0; char *p, *q; ntfs_inode *ni = NULL; @@ -1430,20 +1430,24 @@ ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, /** * ntfs_delete - delete file or directory from ntfs volume - * @ni: ntfs inode for object to delete + * @pni: ntfs inode for object to delete * @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 the call to this function (even if it failed), - * user does not need to call ntfs_inode_close himself. + * @pni is pointer to pointer to ntfs_inode structure. Upon successful + * completion and if inode is really deleted (there are no more links left to + * it) this function will close @*pni and set it to NULL, in the other cases + * @*pni will stay opened. * * 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) +int ntfs_delete(ntfs_inode **pni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len) { ntfs_attr_search_ctx *actx = NULL; ntfs_index_context *ictx = NULL; + ntfs_inode *ni; FILE_NAME_ATTR *fn = NULL; BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; @@ -1451,15 +1455,12 @@ int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) ntfs_log_trace("Entering.\n"); - if (!ni || !dir_ni || !name || !name_len) { + if (!pni || !(ni = *pni) || !dir_ni || !name || !name_len || + ni->nr_extents == -1 || dir_ni->nr_extents == -1) { ntfs_log_error("Invalid arguments.\n"); 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. @@ -1642,14 +1643,12 @@ search: ntfs_log_error("Failed to free base MFT record. " "Leaving inconsistent metadata.\n"); } - ni = NULL; + *pni = 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_log_error("%s(): Failed.\n", __FUNCTION__); errno = err; diff --git a/libntfs/inode.c b/libntfs/inode.c index dcbe7045..d076a5b3 100644 --- a/libntfs/inode.c +++ b/libntfs/inode.c @@ -1069,43 +1069,34 @@ put_err_out: } /** - * ntfs_inode_update_atime - update access time for ntfs inode - * @ni: ntfs inode for which update access time + * ntfs_inode_update_times - update selected time fields for ntfs inode + * @ni: ntfs inode for which update time fields + * @mask: select which time fields should be updated * - * This function usually get called when user read not metadata from inode. - * Do not update time for system files. + * This function updates time fields to current time. Fields to update are + * selected using @mask (see enum @ntfs_time_update_flags for posssible values). */ -void ntfs_inode_update_atime(ntfs_inode *ni) +void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - if (!NVolReadOnly(ni->vol) && !NVolNoATime(ni->vol) && (ni->mft_no >= - FILE_first_user || ni->mft_no == FILE_root)) { - ni->last_access_time = time(NULL); - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); + time_t now; + + if (!ni) { + ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); + return; } -} + if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) || + NVolReadOnly(ni->vol) || !mask) + return; -/** - * ntfs_inode_update_time - update all times for ntfs inode - * @ni: ntfs inode for which update times - * - * This function updates last access, mft and data change times. Usually - * get called when user write not metadata to inode. Do not update time for - * system files. - */ -void ntfs_inode_update_time(ntfs_inode *ni) -{ - if (!NVolReadOnly(ni->vol) && !NVolNoATime(ni->vol) && (ni->mft_no >= - FILE_first_user || ni->mft_no == FILE_root)) { - time_t now; - - now = time(NULL); + now = time(NULL); + if (mask & NTFS_UPDATE_ATIME) ni->last_access_time = now; + if (mask & NTFS_UPDATE_MTIME) ni->last_data_change_time = now; + if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); - } + NInoFileNameSetDirty(ni); + NInoSetDirty(ni); } /** diff --git a/libntfs/volume.c b/libntfs/volume.c index 2ecb6881..0f7dd140 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -440,8 +440,6 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, vol->upcase_len * sizeof(ntfschar)); if (flags & NTFS_MNT_RDONLY) NVolSetReadOnly(vol); - if (flags & NTFS_MNT_NOATIME) - NVolSetNoATime(vol); if (flags & NTFS_MNT_CASE_SENSITIVE) NVolSetCaseSensitive(vol); ntfs_log_debug("Reading bootsector... "); @@ -758,7 +756,6 @@ out: * as for the mount system call (man 2 mount). Currently the following flags * are implemented: * NTFS_MNT_RDONLY - mount volume read-only - * NTFS_MNT_NOATIME - do not update access time * NTFS_MNT_CASE_SENSITIVE - treat filenames as case sensitive even if * they are not in POSIX namespace * NTFS_MNT_NOT_EXCLUSIVE - (unix only) do not open volume exclusively diff --git a/ntfsprogs/ntfsck.c b/ntfsprogs/ntfsck.c index 704ccd75..2171a620 100644 --- a/ntfsprogs/ntfsck.c +++ b/ntfsprogs/ntfsck.c @@ -833,7 +833,7 @@ int main(int argc, char **argv) // at this point we know that the volume is valid enough for mounting. /* Call ntfs_device_mount() to do the actual mount. */ - vol = ntfs_device_mount(dev, NTFS_MNT_NOATIME | NTFS_MNT_RDONLY); + vol = ntfs_device_mount(dev, NTFS_MNT_RDONLY); if (!vol) { ntfs_device_free(dev); return 2; diff --git a/ntfsprogs/ntfsclone.c b/ntfsprogs/ntfsclone.c index 9f78f900..dfcb58d9 100644 --- a/ntfsprogs/ntfsclone.c +++ b/ntfsprogs/ntfsclone.c @@ -1841,7 +1841,7 @@ int main(int argc, char **argv) /* 'force' again mount for dirty volumes (e.g. after resize). FIXME: use mount flags to avoid potential side-effects in future */ opt.force++; - mount_volume(NTFS_MNT_NOATIME); + mount_volume(0); free(lcn_bitmap.bm); setup_lcn_bitmap(); diff --git a/ntfsprogs/ntfsmount.c b/ntfsprogs/ntfsmount.c index 39a84bbc..0ac8e278 100644 --- a/ntfsprogs/ntfsmount.c +++ b/ntfsprogs/ntfsmount.c @@ -107,6 +107,7 @@ typedef struct { BOOL verbose; BOOL no_def_opts; BOOL case_insensitive; + BOOL noatime; } ntfs_fuse_context_t; typedef enum { @@ -138,6 +139,7 @@ static const struct fuse_opt ntfs_fuse_opts[] = { NTFS_FUSE_OPT("no_detach", no_detach), NTFS_FUSE_OPT("no_def_opts", no_def_opts), NTFS_FUSE_OPT("case_insensitive", case_insensitive), + NTFS_FUSE_OPT("noatime", noatime), NTFS_FUSE_OPT("fmask=%o", fmask), NTFS_FUSE_OPT("dmask=%o", dmask), NTFS_FUSE_OPT("umask=%o", fmask), @@ -160,6 +162,7 @@ static const struct fuse_opt ntfs_fuse_opts[] = { FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_DISCARD), FUSE_OPT_KEY("ro", FUSE_OPT_KEY_KEEP), FUSE_OPT_KEY("rw", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("noatime", FUSE_OPT_KEY_KEEP), FUSE_OPT_END }; @@ -190,6 +193,14 @@ static __inline__ int ntfs_fuse_is_named_data_stream(const char *path) return 0; } +static __inline__ void ntfs_fuse_update_times(ntfs_inode *ni, + ntfs_time_update_flags mask) +{ + if (ctx->noatime) + mask &= ~NTFS_UPDATE_ATIME; + ntfs_inode_update_times(ni, mask); +} + static long ntfs_fuse_get_nr_free_mft_records(ntfs_volume *vol, long nr_free) { u8 *buf; @@ -589,6 +600,7 @@ static int ntfs_fuse_readdir(const char *path, void *buf, if (ntfs_readdir(ni, &pos, &fill_ctx, (ntfs_filldir_t)ntfs_fuse_filler)) err = -errno; + ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); ntfs_inode_close(ni); return err; } @@ -633,6 +645,8 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, ntfschar *stream_name; int stream_name_len, res, total = 0; + if (!size) + return 0; stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name); if (stream_name_len < 0) return stream_name_len; @@ -662,6 +676,7 @@ static int ntfs_fuse_read(const char *org_path, char *buf, size_t size, total += res; } res = total; + ntfs_fuse_update_times(ni, NTFS_UPDATE_ATIME); exit: if (na) ntfs_attr_close(na); @@ -711,6 +726,9 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size, res = total; exit: ntfs_fuse_mark_free_space_outdated(); + if (res > 0) + ntfs_fuse_update_times(ni, NTFS_UPDATE_MTIME | + NTFS_UPDATE_CTIME); if (na) ntfs_attr_close(na); if (ni && ntfs_inode_close(ni)) @@ -738,6 +756,10 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) res = -errno; goto exit; } + if (ni->data_size == size) { + res = 0; + goto exit; + } na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len); if (!na) { res = -errno; @@ -745,8 +767,11 @@ static int ntfs_fuse_truncate(const char *org_path, off_t size) } if (ntfs_attr_truncate(na, size)) res = -errno; - else + else { + ntfs_fuse_update_times(ni, NTFS_UPDATE_MTIME | + NTFS_UPDATE_CTIME); res = 0; + } ntfs_fuse_mark_free_space_outdated(); ntfs_attr_close(na); exit: @@ -827,9 +852,11 @@ static int ntfs_fuse_create(const char *org_path, dev_t type, dev_t dev, ni = ntfs_create(dir_ni, uname, uname_len, type); break; } - if (ni) + if (ni) { ntfs_inode_close(ni); - else + ntfs_fuse_update_times(dir_ni, + NTFS_UPDATE_CTIME | NTFS_UPDATE_MTIME); + } else res = -errno; exit: free(uname); @@ -948,6 +975,11 @@ static int ntfs_fuse_link(const char *old_path, const char *new_path) /* Create hard link. */ if (ntfs_link(ni, dir_ni, uname, uname_len)) res = -errno; + else { + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_CTIME | + NTFS_UPDATE_MTIME); + } exit: if (ni) ntfs_inode_close(ni); @@ -993,9 +1025,14 @@ static int ntfs_fuse_rm(const char *org_path) goto exit; } /* Delete object. */ - if (ntfs_delete(ni, dir_ni, uname, uname_len)) + if (ntfs_delete(&ni, dir_ni, uname, uname_len)) res = -errno; - ni = NULL; + else { + if (ni) + ntfs_fuse_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_fuse_update_times(dir_ni, NTFS_UPDATE_CTIME | + NTFS_UPDATE_MTIME); + } exit: if (ni) ntfs_inode_close(ni); @@ -1110,23 +1147,21 @@ static int ntfs_fuse_rmdir(const char *path) static int ntfs_fuse_utimens(const char *path, const struct timespec ts[2]) { ntfs_inode *ni; + time_t now; if (ntfs_fuse_is_named_data_stream(path)) return -EINVAL; /* n/a for named data streams. */ ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); if (!ni) return -errno; + now = time(NULL); + ni->last_mft_change_time = now; if (ts) { ni->last_access_time = ts[0].tv_sec; ni->last_data_change_time = ts[1].tv_sec; - ni->last_mft_change_time = ts[1].tv_sec; } 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); @@ -1599,7 +1634,7 @@ static int ntfs_fuse_mount(void) { ntfs_volume *vol; - vol = utils_mount_volume(ctx->device, NTFS_MNT_NOATIME | + vol = utils_mount_volume(ctx->device, ((ctx->ro) ? NTFS_MNT_RDONLY : 0) | ((ctx->case_insensitive) ? 0 : NTFS_MNT_CASE_SENSITIVE) | diff --git a/ntfsprogs/ntfsresize.c b/ntfsprogs/ntfsresize.c index 6d215d8a..627647a9 100644 --- a/ntfsprogs/ntfsresize.c +++ b/ntfsprogs/ntfsresize.c @@ -2243,8 +2243,7 @@ static ntfs_volume *mount_volume(void) * volume at all. We will do the logfile emptying and dirty setting * later if needed. */ - if (!(vol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_NOATIME | - NTFS_MNT_FORENSIC))) { + if (!(vol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_FORENSIC))) { int err = errno; perr_printf("Opening '%s' as NTFS failed", opt.volume);