[haiku-commits] r39378 - haiku/trunk/src/add-ons/kernel/file_systems/bfs

  • From: axeld@xxxxxxxxxxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Tue, 9 Nov 2010 21:25:43 +0100 (CET)

Author: axeld
Date: 2010-11-09 21:25:43 +0100 (Tue, 09 Nov 2010)
New Revision: 39378
Changeset: http://dev.haiku-os.org/changeset/39378
Ticket: http://dev.haiku-os.org/ticket/6750

Modified:
   haiku/trunk/src/add-ons/kernel/file_systems/bfs/Inode.cpp
   haiku/trunk/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
Log:
* 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.


Modified: haiku/trunk/src/add-ons/kernel/file_systems/bfs/Inode.cpp
===================================================================
--- haiku/trunk/src/add-ons/kernel/file_systems/bfs/Inode.cpp   2010-11-09 
19:09:18 UTC (rev 39377)
+++ haiku/trunk/src/add-ons/kernel/file_systems/bfs/Inode.cpp   2010-11-09 
20:25:43 UTC (rev 39378)
@@ -489,33 +489,39 @@
 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;
+       int userPermissions = (mode & S_IRWXU) >> 6;
+       int groupPermissions = (mode & S_IRWXG) >> 3;
+       int otherPermissions = mode & S_IRWXO;
 
-       if (accessMode & ~(mode & S_IRWXO))
-               return B_NOT_ALLOWED;
+       // get the node permissions for this uid/gid
+       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;
 }
 
 

Modified: haiku/trunk/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp
===================================================================
--- haiku/trunk/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp        
2010-11-09 19:09:18 UTC (rev 39377)
+++ haiku/trunk/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp        
2010-11-09 20:25:43 UTC (rev 39378)
@@ -36,7 +36,7 @@
 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 @@
        // 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 @@
                updateTime = true;
        }
 
-       if ((mask & B_STAT_UID) != 0) {
-               node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
-               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_GID) != 0) {
-               node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
-               updateTime = true;
-       }
 
        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_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 @@
                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)


Other related posts:

  • » [haiku-commits] r39378 - haiku/trunk/src/add-ons/kernel/file_systems/bfs - axeld