summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAxel Dörfler <axeld@pinc-software.de>2010-11-09 20:25:43 (GMT)
committerAxel Dörfler <axeld@pinc-software.de>2010-11-09 20:25:43 (GMT)
commit48ec4fbb40a9ac5327e0351486946403d0b533b8 (patch)
tree19738d9f8150586ce3b436b037802e453acac564
parenta569816bb0b823227e8ce2e24c84a9566ef22c63 (diff)
* Applied patch by Rohit Yadav that fixes #6750, thanks a lot!hrev39378
* This changes Inode::CheckPermissions(), and bfs_write_stat() based on Ingo's solution in his file corruption test file system. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39378 a95241bf-73f2-0310-859d-f6bbb57e9c96
-rw-r--r--src/add-ons/kernel/file_systems/bfs/Inode.cpp48
-rw-r--r--src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp99
2 files changed, 87 insertions, 60 deletions
diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp
index 9f4b555..670c955 100644
--- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp
+++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp
@@ -489,33 +489,39 @@ Inode::UpdateNodeFromDisk()
status_t
Inode::CheckPermissions(int accessMode) const
{
- uid_t user = geteuid();
- gid_t group = getegid();
-
// you never have write access to a read-only volume
if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
return B_READ_ONLY_DEVICE;
- // root users always have full access (but they can't execute files without
- // any execute permissions set)
- if (user == 0) {
- if (!((accessMode & X_OK) != 0 && (Mode() & S_IXUSR) == 0)
- || (Mode() & S_DIRECTORY) != 0) {
- return B_OK;
- }
- }
-
- // shift mode bits, to check directly against accessMode
+ // get node permissions
mode_t mode = Mode();
- if (user == (uid_t)fNode.UserID())
- mode >>= 6;
- else if (group == (gid_t)fNode.GroupID())
- mode >>= 3;
-
- if (accessMode & ~(mode & S_IRWXO))
- return B_NOT_ALLOWED;
+ int userPermissions = (mode & S_IRWXU) >> 6;
+ int groupPermissions = (mode & S_IRWXG) >> 3;
+ int otherPermissions = mode & S_IRWXO;
+
+ // get the node permissions for this uid/gid
+ int permissions = 0;
+ uid_t uid = geteuid();
+ gid_t gid = getegid();
+
+ if (uid == 0) {
+ // user is root
+ // root has always read/write permission, but at least one of the
+ // X bits must be set for execute permission
+ permissions = userPermissions | groupPermissions | otherPermissions
+ | R_OK | W_OK;
+ } else if (uid == (uid_t)fNode.UserID()) {
+ // user is node owner
+ permissions = userPermissions;
+ } else if (gid == (gid_t)fNode.GroupID()) {
+ // user is in owning group
+ permissions = groupPermissions;
+ } else {
+ // user is one of the others
+ permissions = otherPermissions;
+ }
- return B_OK;
+ return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
}
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 20de45e..9219c81 100644
--- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
+++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
@@ -36,7 +36,7 @@ static void
fill_stat_time(const bfs_inode& node, struct stat& stat)
{
bigtime_t now = real_time_clock_usecs();
- stat.st_atim.tv_sec = now / 1000000LL;
+ stat.st_atim.tv_sec = now / 1000000LL;
stat.st_atim.tv_nsec = (now % 1000000LL) * 1000;
stat.st_mtim.tv_sec = bfs_inode::ToSecs(node.LastModifiedTime());
@@ -778,51 +778,70 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
// TODO: we should definitely check a bit more if the new stats are
// valid - or even better, the VFS should check this before calling us
- status_t status = inode->CheckPermissions(W_OK);
- if (status < B_OK)
- RETURN_ERROR(status);
+ bfs_inode& node = inode->Node();
+ 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());
inode->WriteLockInTransaction(transaction);
- bfs_inode& node = inode->Node();
- bool updateTime = false;
-
- if ((mask & B_STAT_SIZE) != 0) {
- // Since WSTAT_SIZE is the only thing that can fail directly, we
+ if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
+ // 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
// with the on-disk version
if (inode->IsDirectory())
return B_IS_A_DIRECTORY;
if (!inode->IsFile())
return B_BAD_VALUE;
+ if (!hasWriteAccess)
+ RETURN_ERROR(B_NOT_ALLOWED);
- if (inode->Size() != stat->st_size) {
- off_t oldSize = inode->Size();
+ off_t oldSize = inode->Size();
- status = inode->SetFileSize(transaction, stat->st_size);
- if (status != B_OK)
- return status;
+ status_t status = inode->SetFileSize(transaction, stat->st_size);
+ if (status != B_OK)
+ return status;
- // fill the new blocks (if any) with zeros
- if ((mask & B_STAT_SIZE_INSECURE) == 0) {
- // We must not keep the inode locked during a write operation,
- // or else we might deadlock.
- rw_lock_write_unlock(&inode->Lock());
- inode->FillGapWithZeros(oldSize, inode->Size());
- rw_lock_write_lock(&inode->Lock());
- }
+ // fill the new blocks (if any) with zeros
+ if ((mask & B_STAT_SIZE_INSECURE) == 0) {
+ // We must not keep the inode locked during a write operation,
+ // or else we might deadlock.
+ rw_lock_write_unlock(&inode->Lock());
+ inode->FillGapWithZeros(oldSize, inode->Size());
+ rw_lock_write_lock(&inode->Lock());
+ }
- if (!inode->IsDeleted()) {
- Index index(volume);
- index.UpdateSize(transaction, inode);
+ if (!inode->IsDeleted()) {
+ Index index(volume);
+ index.UpdateSize(transaction, inode);
- updateTime = true;
- }
+ updateTime = true;
}
}
+ 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);
+ updateTime = true;
+ }
+
+ 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);
+ updateTime = true;
+ }
+
if ((mask & B_STAT_MODE) != 0) {
+ // only the user or root can do that
+ if (!isOwnerOrRoot)
+ RETURN_ERROR(B_NOT_ALLOWED);
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)
@@ -830,16 +849,18 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
updateTime = true;
}
- if ((mask & B_STAT_UID) != 0) {
- node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
- updateTime = true;
- }
- if ((mask & B_STAT_GID) != 0) {
- node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
- updateTime = true;
+ 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
+ = HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
}
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()) {
// directory modification times are not part of the index
node.last_modified_time
@@ -851,11 +872,11 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
bfs_inode::ToInode(stat->st_mtim));
}
}
- if ((mask & B_STAT_CREATION_TIME) != 0) {
- node.create_time
- = HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
- }
+
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;
if ((mask & B_STAT_CHANGE_TIME) == 0)
newTime = bfs_inode::ToInode(real_time_clock_usecs());
@@ -865,7 +886,7 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
node.status_change_time = HOST_ENDIAN_TO_BFS_INT64(newTime);
}
- status = inode->WriteBack(transaction);
+ status_t status = inode->WriteBack(transaction);
if (status == B_OK)
status = transaction.Done();
if (status == B_OK)