BFS & EXTFS: Use the new check_write_stat_permissions in write_stat hooks.

This commit is contained in:
Augustin Cavalier
2025-07-07 15:11:21 -04:00
parent 9ea1065b16
commit 15b255904d
2 changed files with 8 additions and 45 deletions
@@ -886,14 +886,14 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
bfs_inode& node = inode->Node(); bfs_inode& node = inode->Node();
bool updateTime = false; bool updateTime = false;
uid_t uid = geteuid();
bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
Transaction transaction(volume, inode->BlockNumber()); Transaction transaction(volume, inode->BlockNumber());
inode->WriteLockInTransaction(transaction); inode->WriteLockInTransaction(transaction);
if (check_write_stat_permissions(node.GroupID(), node.UserID(), node.Mode(),
mask, stat) != B_OK)
RETURN_ERROR(B_NOT_ALLOWED);
if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
// Since B_STAT_SIZE is the only thing that can fail directly, we // Since B_STAT_SIZE is the only thing that can fail directly, we
// do it first, so that the inode state will still be consistent // do it first, so that the inode state will still be consistent
@@ -902,8 +902,6 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
return B_IS_A_DIRECTORY; return B_IS_A_DIRECTORY;
if (!inode->IsFile()) if (!inode->IsFile())
return B_BAD_VALUE; return B_BAD_VALUE;
if (!hasWriteAccess)
RETURN_ERROR(B_NOT_ALLOWED);
off_t oldSize = inode->Size(); off_t oldSize = inode->Size();
@@ -929,25 +927,16 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
} }
if ((mask & B_STAT_UID) != 0) { if ((mask & B_STAT_UID) != 0) {
// only root should be allowed
if (uid != 0)
RETURN_ERROR(B_NOT_ALLOWED);
node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid); node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
updateTime = true; updateTime = true;
} }
if ((mask & B_STAT_GID) != 0) { if ((mask & B_STAT_GID) != 0) {
// only the user or root can do that
if (!isOwnerOrRoot)
RETURN_ERROR(B_NOT_ALLOWED);
node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid); node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
updateTime = true; updateTime = true;
} }
if ((mask & B_STAT_MODE) != 0) { if ((mask & B_STAT_MODE) != 0) {
// only the user or root can do that
if (!isOwnerOrRoot)
RETURN_ERROR(B_NOT_ALLOWED);
PRINT(("original mode = %u, stat->st_mode = %u\n", PRINT(("original mode = %u, stat->st_mode = %u\n",
(unsigned int)node.Mode(), (unsigned int)stat->st_mode)); (unsigned int)node.Mode(), (unsigned int)stat->st_mode));
node.mode = HOST_ENDIAN_TO_BFS_INT32((node.Mode() & ~S_IUMSK) node.mode = HOST_ENDIAN_TO_BFS_INT32((node.Mode() & ~S_IUMSK)
@@ -956,17 +945,11 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
} }
if ((mask & B_STAT_CREATION_TIME) != 0) { if ((mask & B_STAT_CREATION_TIME) != 0) {
// the user or root can do that or any user with write access
if (!isOwnerOrRoot && !hasWriteAccess)
RETURN_ERROR(B_NOT_ALLOWED);
node.create_time node.create_time
= HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim)); = HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
} }
if ((mask & B_STAT_MODIFICATION_TIME) != 0) { if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
// the user or root can do that or any user with write access
if (!isOwnerOrRoot && !hasWriteAccess)
RETURN_ERROR(B_NOT_ALLOWED);
if (!inode->InLastModifiedIndex()) { if (!inode->InLastModifiedIndex()) {
// directory modification times are not part of the index // directory modification times are not part of the index
node.last_modified_time node.last_modified_time
@@ -980,9 +963,6 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
} }
if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) { if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
// the user or root can do that or any user with write access
if (!isOwnerOrRoot && !hasWriteAccess)
RETURN_ERROR(B_NOT_ALLOWED);
bigtime_t newTime; bigtime_t newTime;
if ((mask & B_STAT_CHANGE_TIME) == 0) if ((mask & B_STAT_CHANGE_TIME) == 0)
newTime = bfs_inode::ToInode(real_time_clock_usecs()); newTime = bfs_inode::ToInode(real_time_clock_usecs());
@@ -625,22 +625,20 @@ ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
ext2_inode& node = inode->Node(); ext2_inode& node = inode->Node();
bool updateTime = false; bool updateTime = false;
uid_t uid = geteuid();
bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
TRACE("ext2_write_stat: Starting transaction\n"); TRACE("ext2_write_stat: Starting transaction\n");
Transaction transaction(volume->GetJournal()); Transaction transaction(volume->GetJournal());
inode->WriteLockInTransaction(transaction); inode->WriteLockInTransaction(transaction);
if (check_write_stat_permissions(node.GroupID(), node.UserID(), node.Mode(),
mask, stat) != B_OK)
return B_NOT_ALLOWED;
if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
if (inode->IsDirectory()) if (inode->IsDirectory())
return B_IS_A_DIRECTORY; return B_IS_A_DIRECTORY;
if (!inode->IsFile()) if (!inode->IsFile())
return B_BAD_VALUE; return B_BAD_VALUE;
if (!hasWriteAccess)
return B_NOT_ALLOWED;
TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n", TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n",
(long)inode->Size(), (long)stat->st_size); (long)inode->Size(), (long)stat->st_size);
@@ -661,34 +659,22 @@ ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
} }
if ((mask & B_STAT_MODE) != 0) { if ((mask & B_STAT_MODE) != 0) {
// only the user or root can do that
if (!isOwnerOrRoot)
return B_NOT_ALLOWED;
node.UpdateMode(stat->st_mode, S_IUMSK); node.UpdateMode(stat->st_mode, S_IUMSK);
updateTime = true; updateTime = true;
} }
if ((mask & B_STAT_UID) != 0) { if ((mask & B_STAT_UID) != 0) {
// only root should be allowed
if (uid != 0)
return B_NOT_ALLOWED;
node.SetUserID(stat->st_uid); node.SetUserID(stat->st_uid);
updateTime = true; updateTime = true;
} }
if ((mask & B_STAT_GID) != 0) { if ((mask & B_STAT_GID) != 0) {
// only the user or root can do that
if (!isOwnerOrRoot)
return B_NOT_ALLOWED;
node.SetGroupID(stat->st_gid); node.SetGroupID(stat->st_gid);
updateTime = true; updateTime = true;
} }
if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime
|| (mask & B_STAT_CHANGE_TIME) != 0) { || (mask & B_STAT_CHANGE_TIME) != 0) {
// the user or root can do that or any user with write access
if (!isOwnerOrRoot && !hasWriteAccess)
return B_NOT_ALLOWED;
struct timespec newTimespec = { 0, 0}; struct timespec newTimespec = { 0, 0};
if ((mask & B_STAT_MODIFICATION_TIME) != 0) if ((mask & B_STAT_MODIFICATION_TIME) != 0)
@@ -704,9 +690,6 @@ ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
inode->SetModificationTime(&newTimespec); inode->SetModificationTime(&newTimespec);
} }
if ((mask & B_STAT_CREATION_TIME) != 0) { if ((mask & B_STAT_CREATION_TIME) != 0) {
// the user or root can do that or any user with write access
if (!isOwnerOrRoot && !hasWriteAccess)
return B_NOT_ALLOWED;
inode->SetCreationTime(&stat->st_crtim); inode->SetCreationTime(&stat->st_crtim);
} }