* Applied patch by Rohit Yadav that fixes #6750, thanks a lot!

* 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
This commit is contained in:
Axel Dörfler
2010-11-09 20:25:43 +00:00
parent a569816bb0
commit 48ec4fbb40
2 changed files with 87 additions and 60 deletions
+26 -20
View File
@@ -489,33 +489,39 @@ Inode::UpdateNodeFromDisk()
status_t status_t
Inode::CheckPermissions(int accessMode) const Inode::CheckPermissions(int accessMode) const
{ {
uid_t user = geteuid();
gid_t group = getegid();
// you never have write access to a read-only volume // you never have write access to a read-only volume
if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly())
return B_READ_ONLY_DEVICE; return B_READ_ONLY_DEVICE;
// root users always have full access (but they can't execute files without // get node permissions
// 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
mode_t mode = Mode(); mode_t mode = Mode();
if (user == (uid_t)fNode.UserID()) int userPermissions = (mode & S_IRWXU) >> 6;
mode >>= 6; int groupPermissions = (mode & S_IRWXG) >> 3;
else if (group == (gid_t)fNode.GroupID()) int otherPermissions = mode & S_IRWXO;
mode >>= 3;
if (accessMode & ~(mode & S_IRWXO)) // get the node permissions for this uid/gid
return B_NOT_ALLOWED; int permissions = 0;
uid_t uid = geteuid();
gid_t gid = getegid();
return B_OK; 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 (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
} }
@@ -778,29 +778,30 @@ 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 // 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 // valid - or even better, the VFS should check this before calling us
status_t status = inode->CheckPermissions(W_OK); bfs_inode& node = inode->Node();
if (status < B_OK) bool updateTime = false;
RETURN_ERROR(status); 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);
bfs_inode& node = inode->Node(); if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
bool updateTime = false; // Since B_STAT_SIZE is the only thing that can fail directly, we
if ((mask & B_STAT_SIZE) != 0) {
// Since WSTAT_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
// with the on-disk version // with the on-disk version
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_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); status_t status = inode->SetFileSize(transaction, stat->st_size);
if (status != B_OK) if (status != B_OK)
return status; return status;
@@ -820,9 +821,27 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
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) { 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(), PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(),
stat->st_mode)); 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)
@@ -830,16 +849,18 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
updateTime = true; updateTime = true;
} }
if ((mask & B_STAT_UID) != 0) { if ((mask & B_STAT_CREATION_TIME) != 0) {
node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid); // the user or root can do that or any user with write access
updateTime = true; if (!isOwnerOrRoot && !hasWriteAccess)
} RETURN_ERROR(B_NOT_ALLOWED);
if ((mask & B_STAT_GID) != 0) { node.create_time
node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid); = HOST_ENDIAN_TO_BFS_INT64(bfs_inode::ToInode(stat->st_crtim));
updateTime = true;
} }
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
@@ -851,11 +872,11 @@ bfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
bfs_inode::ToInode(stat->st_mtim)); 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) { 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());
@@ -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); node.status_change_time = HOST_ENDIAN_TO_BFS_INT64(newTime);
} }
status = inode->WriteBack(transaction); status_t status = inode->WriteBack(transaction);
if (status == B_OK) if (status == B_OK)
status = transaction.Done(); status = transaction.Done();
if (status == B_OK) if (status == B_OK)