diff --git a/src/add-ons/kernel/file_systems/bfs/Debug.cpp b/src/add-ons/kernel/file_systems/bfs/Debug.cpp index 37cdc3abf1..56d646511b 100644 --- a/src/add-ons/kernel/file_systems/bfs/Debug.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Debug.cpp @@ -121,6 +121,8 @@ dump_inode(const bfs_inode* inode) inode->CreateTime() >> INODE_TIME_SHIFT); kprintf(" last_modified_time = %Ld (%Ld)\n", inode->LastModifiedTime(), inode->LastModifiedTime() >> INODE_TIME_SHIFT); + kprintf(" status_change_time = %Ld (%Ld)\n", inode->StatusChangeTime(), + inode->StatusChangeTime() >> INODE_TIME_SHIFT); dump_block_run( " parent = ", inode->parent); dump_block_run( " attributes = ", inode->attributes); kprintf(" type = %u\n", (unsigned)inode->Type()); diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 13aa38af7d..e53d65580b 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -395,6 +395,7 @@ Inode::Inode(Volume* volume, Transaction& transaction, ino_t id, mode_t mode, | (volume->GetUniqueID() & INODE_TIME_MASK)); // we use Volume::GetUniqueID() to avoid having too many duplicates // in the last_modified index + Node().status_change_time = HOST_ENDIAN_TO_BFS_INT64(Node().create_time); Node().inode_size = HOST_ENDIAN_TO_BFS_INT32(volume->InodeSize()); diff --git a/src/add-ons/kernel/file_systems/bfs/bfs.h b/src/add-ons/kernel/file_systems/bfs/bfs.h index 5d56e632be..f46496f07d 100644 --- a/src/add-ons/kernel/file_systems/bfs/bfs.h +++ b/src/add-ons/kernel/file_systems/bfs/bfs.h @@ -188,7 +188,8 @@ struct bfs_inode { data_stream data; char short_symlink[SHORT_SYMLINK_NAME_LENGTH]; }; - int32 pad[4]; + bigtime_t status_change_time; + int32 pad[2]; // we use this member as a doubly linked list link small_data small_data_start[0]; @@ -200,9 +201,13 @@ struct bfs_inode { int32 Flags() const { return BFS_ENDIAN_TO_HOST_INT32(flags); } int32 Type() const { return BFS_ENDIAN_TO_HOST_INT32(type); } int32 InodeSize() const { return BFS_ENDIAN_TO_HOST_INT32(inode_size); } - bigtime_t LastModifiedTime() const { return BFS_ENDIAN_TO_HOST_INT64(last_modified_time); } - bigtime_t CreateTime() const { return BFS_ENDIAN_TO_HOST_INT64(create_time); } + bigtime_t LastModifiedTime() const { + return BFS_ENDIAN_TO_HOST_INT64(last_modified_time); } + bigtime_t CreateTime() const { + return BFS_ENDIAN_TO_HOST_INT64(create_time); } small_data *SmallDataStart() { return small_data_start; } + bigtime_t StatusChangeTime() const { + return BFS_ENDIAN_TO_HOST_INT64(status_change_time); } status_t InitCheck(Volume *volume); // defined in Inode.cpp diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp index 7c185b6a69..d3bd652882 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -47,9 +47,22 @@ fill_stat_buffer(Inode* inode, struct stat& stat) stat.st_type = node.Type(); stat.st_atime = time(NULL); - stat.st_mtime = stat.st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); + stat.st_mtime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); stat.st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT); + // if on-disk ctime is invalid (pointer value from previous [ab]use of + // the first 4 bytes) or 0, fall back to mtime: + // N.B.: This has the drawback that explicitly setting a ctime of 0 + // will not work, but I suppose no one will do that, since ctime + // is usually just set to the current time whenever something happens + // to the inode. + // TODO: find out if this sanity check should be dropped! + bigtime_t ctime = node.StatusChangeTime(); + if (((uint64)ctime & 0xFFFF00000000FFFFULL) != 0 || ctime == 0) + stat.st_ctime = stat.st_mtime; + else + stat.st_ctime = (time_t)(ctime >> INODE_TIME_SHIFT); + if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) { // symlinks report the size of the link here stat.st_size = strlen(node.short_symlink); @@ -772,9 +785,6 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, } } - // Note, the following changes (mode/uid/gid) would update st_ctime; - // since we don't have that, we'll use st_mtime instead. - if ((mask & B_STAT_MODE) != 0) { PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(), stat->st_mode)); node.mode = HOST_ENDIAN_TO_BFS_INT32((node.Mode() & ~S_IUMSK) @@ -791,26 +801,31 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, updateTime = true; } - if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime) { - bigtime_t newTime; - if ((mask & B_STAT_MODIFICATION_TIME) == 0) - newTime = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; - else - newTime = (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT; - + if ((mask & B_STAT_MODIFICATION_TIME) != 0) { if (!inode->InLastModifiedIndex()) { // directory modification times are not part of the index - node.last_modified_time = HOST_ENDIAN_TO_BFS_INT64(newTime); + node.last_modified_time = HOST_ENDIAN_TO_BFS_INT64( + (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); } else if (!inode->IsDeleted()) { // Index::UpdateLastModified() will set the new time in the inode Index index(volume); - index.UpdateLastModified(transaction, inode, newTime); + index.UpdateLastModified(transaction, inode, + (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); } } if ((mask & B_STAT_CREATION_TIME) != 0) { node.create_time = HOST_ENDIAN_TO_BFS_INT64( (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT); } + if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) { + bigtime_t newTime; + if ((mask & B_STAT_CHANGE_TIME) == 0) + newTime = (bigtime_t)time(NULL); + else + newTime = (bigtime_t)stat->st_ctime; + node.status_change_time + = HOST_ENDIAN_TO_BFS_INT64(newTime << INODE_TIME_SHIFT); + } status = inode->WriteBack(transaction); if (status == B_OK) @@ -2032,8 +2047,8 @@ bfs_stat_index(fs_volume* _volume, const char* name, struct stat* stat) stat->st_gid = node.GroupID(); stat->st_atime = time(NULL); - stat->st_mtime = stat->st_ctime - = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); + stat->st_mtime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); + stat->st_ctime = (time_t)(node.StatusChangeTime() >> INODE_TIME_SHIFT); stat->st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT); return B_OK;