diff --git a/src/add-ons/kernel/file_systems/ext2/Inode.cpp b/src/add-ons/kernel/file_systems/ext2/Inode.cpp index e9e2c65c2b..7c7ed57a07 100644 --- a/src/add-ons/kernel/file_systems/ext2/Inode.cpp +++ b/src/add-ons/kernel/file_systems/ext2/Inode.cpp @@ -6,9 +6,9 @@ #include "Inode.h" -#include #include #include +#include #include "CachedBlock.h" #include "DataStream.h" @@ -51,7 +51,7 @@ Inode::Inode(Volume* volume, ino_t id) fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode) && fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE == sizeof(ext2_inode)); - + if (IsDirectory() || (IsSymLink() && Size() < 60)) { TRACE("Inode::Inode(): Not creating the file cache\n"); fCached = false; @@ -130,7 +130,7 @@ Inode::WriteBack(Transaction& transaction) if (status != B_OK) return status; } - + CachedBlock cached(fVolume); uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum); if (inodeBlockData == NULL) @@ -140,7 +140,7 @@ Inode::WriteBack(Transaction& transaction) "index: %lu, inode size: %lu, node size: %lu, this: %p, node: %p\n", fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID), fVolume->InodeSize(), fNodeSize, this, &fNode); - memcpy(inodeBlockData + + memcpy(inodeBlockData + fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(), (uint8*)&fNode, fNodeSize); @@ -187,32 +187,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 && fVolume->IsReadOnly()) + 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) - || S_ISDIR(Mode())) - return B_OK; + // get node permissions + mode_t mode = Mode(); + 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; } - // shift mode bits, to check directly against accessMode - 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; - - return B_OK; + return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED; } @@ -321,8 +328,8 @@ Inode::FindBlock(off_t offset, off_t& block, uint32 *_count) if (_count) { *_count = 1; uint32 nextBlock = block; - while (((++index & (perBlock - 1)) != 0) - && indirectBlocks[index & (perBlock - 1)] + while (((++index & (perBlock - 1)) != 0) + && indirectBlocks[index & (perBlock - 1)] == ++nextBlock) (*_count)++; } @@ -391,9 +398,11 @@ Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer, // NOTE: Debugging info to find why sometimes resize doesn't happen size_t length = *_length; +#ifdef TRACE_EXT2 off_t oldEnd = pos + length; TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n", (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF)); +#endif off_t end = pos + (off_t)length; off_t oldSize = Size(); @@ -564,7 +573,7 @@ Inode::Unlink(Transaction& transaction) if (firstOrphanID != 0) { Vnode firstOrphan(fVolume, firstOrphanID); Inode* nextOrphan; - + status = firstOrphan.Get(&nextOrphan); if (status != B_OK) return status; @@ -625,7 +634,7 @@ Inode::Create(Transaction& transaction, Inode* parent, const char* name, Vnode vnode(volume, entryID); Inode* inode; - + status = vnode.Get(&inode); if (status != B_OK) { TRACE("Inode::Create() Failed to get the inode from the " @@ -720,7 +729,7 @@ Inode::Create(Transaction& transaction, Inode* parent, const char* name, inode->SetAccessTime(×pec); inode->SetCreationTime(×pec); inode->SetModificationTime(×pec); - + if (sizeof(ext2_inode) < volume->InodeSize()) node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); @@ -979,9 +988,9 @@ Inode::_SetNumBlocks(uint64 numBlocks) if (numBlocks > 0xffffffffffffULL) { fNode.SetFlag(EXT2_INODE_HUGE_FILE); numBlocks /= (fVolume->BlockSize() / 512); - } else + } else fNode.ClearFlag(EXT2_INODE_HUGE_FILE); - + fNode.SetNumBlocks64(numBlocks); return B_OK; } diff --git a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp index b9089f0dbf..c37005375f 100644 --- a/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/ext2/kernel_interface.cpp @@ -257,7 +257,7 @@ ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter) status = inode->WriteBack(transaction); if (status != B_OK) return status; - + TRACE("ext2_remove_vnode(): Freeing inode\n"); status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory()); @@ -414,7 +414,7 @@ ext2_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset, off_t blockOffset = block << volume->BlockShift(); uint32 blockLength = volume->BlockSize() * count; - if (index > 0 && (vecs[index - 1].offset + if (index > 0 && (vecs[index - 1].offset == blockOffset - vecs[index - 1].length || (vecs[index - 1].offset == -1 && block == 0))) { vecs[index - 1].length += blockLength; @@ -470,17 +470,17 @@ ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, HTree htree(volume, directory); DirectoryIterator* iterator; - + status = htree.Lookup(name, &iterator); if (status != B_OK) return status; - + ObjectDeleter iteratorDeleter(iterator); status = iterator->FindEntry(name, _vnodeID); if (status != B_OK) return status; - + return get_vnode(volume->FSVolume(), *_vnodeID, NULL); } @@ -525,7 +525,7 @@ ext2_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd, } TRACE("ioctl: Blocks cleared\n"); - + transaction.Done(); transaction.Start(volume->GetJournal()); } @@ -568,7 +568,7 @@ ext2_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat) inode->GetModificationTime(&stat->st_mtim); inode->GetChangeTime(&stat->st_ctim); inode->GetCreationTime(&stat->st_crtim); - + stat->st_size = inode->Size(); stat->st_blocks = (inode->Size() + 511) / 512; @@ -588,59 +588,72 @@ ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, Inode* inode = (Inode*)_node->private_node; - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - return status; + ext2_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; TRACE("ext2_write_stat: Starting transaction\n"); Transaction transaction(volume->GetJournal()); inode->WriteLockInTransaction(transaction); - bool updateTime = false; - - if ((mask & B_STAT_SIZE) != 0) { + if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) { if (inode->IsDirectory()) return B_IS_A_DIRECTORY; if (!inode->IsFile()) return B_BAD_VALUE; + if (!hasWriteAccess) + return B_NOT_ALLOWED; TRACE("ext2_write_stat: Old size: %ld, new size: %ld\n", (long)inode->Size(), (long)stat->st_size); - if (inode->Size() != stat->st_size) { - off_t oldSize = inode->Size(); - status = inode->Resize(transaction, stat->st_size); - if(status != B_OK) - return status; + off_t oldSize = inode->Size(); - if ((mask & B_STAT_SIZE_INSECURE) == 0) { - rw_lock_write_unlock(inode->Lock()); - inode->FillGapWithZeros(oldSize, inode->Size()); - rw_lock_write_lock(inode->Lock()); - } + status_t status = inode->Resize(transaction, stat->st_size); + if(status != B_OK) + return status; - updateTime = true; + if ((mask & B_STAT_SIZE_INSECURE) == 0) { + rw_lock_write_unlock(inode->Lock()); + inode->FillGapWithZeros(oldSize, inode->Size()); + rw_lock_write_lock(inode->Lock()); } + + updateTime = true; } - ext2_inode& node = inode->Node(); - 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); updateTime = true; } if ((mask & B_STAT_UID) != 0) { + // only root should be allowed + if (uid != 0) + return B_NOT_ALLOWED; node.SetUserID(stat->st_uid); updateTime = true; } + 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); updateTime = true; } if ((mask & B_STAT_MODIFICATION_TIME) != 0 || updateTime || (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}; if ((mask & B_STAT_MODIFICATION_TIME) != 0) @@ -655,10 +668,14 @@ ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, 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); + } - status = inode->WriteBack(transaction); + status_t status = inode->WriteBack(transaction); if (status == B_OK) status = transaction.Done(); if (status == B_OK) @@ -817,7 +834,7 @@ static status_t ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node) { // TODO - + return B_NOT_SUPPORTED; } @@ -943,7 +960,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum); parentID = data->dotdot.InodeID(); - } while (parentID != oldID && parentID != oldDirID + } while (parentID != oldID && parentID != oldDirID && parentID != EXT2_ROOT_NODE); if (parentID == oldID) @@ -1013,7 +1030,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, return status; } else return status; - + // Remove entry from source folder status = oldIterator->RemoveEntry(transaction); if (status != B_OK) @@ -1047,7 +1064,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, if (status != B_OK) { entry_cache_remove(volume->ID(), oldDirectory->ID(), newName); entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID); - + return status; } @@ -1237,7 +1254,7 @@ ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer, static status_t ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, int mode) -{ +{ TRACE("ext2_create_dir()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; @@ -1416,7 +1433,7 @@ ext2_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie) } -static status_t +static status_t ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie) { Inode* inode = (Inode*)_node->private_node; @@ -1512,7 +1529,7 @@ ext2_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name, int openMode, void** _cookie) { TRACE("%s()\n", __FUNCTION__); - + Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; Attribute attribute(inode); @@ -1610,6 +1627,7 @@ fs_volume_ops gExt2VolumeOps = { &ext2_get_vnode, }; + fs_vnode_ops gExt2VnodeOps = { /* vnode operations */ &ext2_lookup, @@ -1680,9 +1698,9 @@ fs_vnode_ops gExt2VnodeOps = { NULL, //&ext2_write_attr_stat, NULL, //&ext2_rename_attr, NULL, //&ext2_remove_attr, - }; + static file_system_module_info sExt2FileSystem = { { "file_systems/ext2" B_CURRENT_FS_API_VERSION, @@ -1705,6 +1723,7 @@ static file_system_module_info sExt2FileSystem = { NULL, }; + module_info *modules[] = { (module_info *)&sExt2FileSystem, NULL,