* Applied patch by Rohit Yadav that fixes #6750 for ext2. Thanks a lot!

* Fixed warning in Inode::WriteAt(), but left warnings in kernel_interface.cpp
  in there; I guess that's work in progress (although having the io hook would
  be nice).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39379 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2010-11-09 20:34:59 +00:00
parent 48ec4fbb40
commit ea75a051ad
2 changed files with 96 additions and 68 deletions

View File

@ -6,9 +6,9 @@
#include "Inode.h" #include "Inode.h"
#include <fs_cache.h>
#include <string.h> #include <string.h>
#include <util/AutoLock.h> #include <util/AutoLock.h>
#include <NodeMonitor.h>
#include "CachedBlock.h" #include "CachedBlock.h"
#include "DataStream.h" #include "DataStream.h"
@ -51,7 +51,7 @@ Inode::Inode(Volume* volume, ino_t id)
fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode) fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode)
&& fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE && fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE
== sizeof(ext2_inode)); == sizeof(ext2_inode));
if (IsDirectory() || (IsSymLink() && Size() < 60)) { if (IsDirectory() || (IsSymLink() && Size() < 60)) {
TRACE("Inode::Inode(): Not creating the file cache\n"); TRACE("Inode::Inode(): Not creating the file cache\n");
fCached = false; fCached = false;
@ -130,7 +130,7 @@ Inode::WriteBack(Transaction& transaction)
if (status != B_OK) if (status != B_OK)
return status; return status;
} }
CachedBlock cached(fVolume); CachedBlock cached(fVolume);
uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum); uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum);
if (inodeBlockData == NULL) if (inodeBlockData == NULL)
@ -140,7 +140,7 @@ Inode::WriteBack(Transaction& transaction)
"index: %lu, inode size: %lu, node size: %lu, this: %p, node: %p\n", "index: %lu, inode size: %lu, node size: %lu, this: %p, node: %p\n",
fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID), fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID),
fVolume->InodeSize(), fNodeSize, this, &fNode); fVolume->InodeSize(), fNodeSize, this, &fNode);
memcpy(inodeBlockData + memcpy(inodeBlockData +
fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(), fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(),
(uint8*)&fNode, fNodeSize); (uint8*)&fNode, fNodeSize);
@ -187,32 +187,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 && 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) mode_t mode = Mode();
if (user == 0) { int userPermissions = (mode & S_IRWXU) >> 6;
if (!((accessMode & X_OK) != 0 && (Mode() & S_IXUSR) == 0) int groupPermissions = (mode & S_IRWXG) >> 3;
|| S_ISDIR(Mode())) int otherPermissions = mode & S_IRWXO;
return B_OK;
// 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 return (accessMode & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED;
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;
} }
@ -321,8 +328,8 @@ Inode::FindBlock(off_t offset, off_t& block, uint32 *_count)
if (_count) { if (_count) {
*_count = 1; *_count = 1;
uint32 nextBlock = block; uint32 nextBlock = block;
while (((++index & (perBlock - 1)) != 0) while (((++index & (perBlock - 1)) != 0)
&& indirectBlocks[index & (perBlock - 1)] && indirectBlocks[index & (perBlock - 1)]
== ++nextBlock) == ++nextBlock)
(*_count)++; (*_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 // NOTE: Debugging info to find why sometimes resize doesn't happen
size_t length = *_length; size_t length = *_length;
#ifdef TRACE_EXT2
off_t oldEnd = pos + length; off_t oldEnd = pos + length;
TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n", TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n",
(int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF)); (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF));
#endif
off_t end = pos + (off_t)length; off_t end = pos + (off_t)length;
off_t oldSize = Size(); off_t oldSize = Size();
@ -564,7 +573,7 @@ Inode::Unlink(Transaction& transaction)
if (firstOrphanID != 0) { if (firstOrphanID != 0) {
Vnode firstOrphan(fVolume, firstOrphanID); Vnode firstOrphan(fVolume, firstOrphanID);
Inode* nextOrphan; Inode* nextOrphan;
status = firstOrphan.Get(&nextOrphan); status = firstOrphan.Get(&nextOrphan);
if (status != B_OK) if (status != B_OK)
return status; return status;
@ -625,7 +634,7 @@ Inode::Create(Transaction& transaction, Inode* parent, const char* name,
Vnode vnode(volume, entryID); Vnode vnode(volume, entryID);
Inode* inode; Inode* inode;
status = vnode.Get(&inode); status = vnode.Get(&inode);
if (status != B_OK) { if (status != B_OK) {
TRACE("Inode::Create() Failed to get the inode from the " 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(&timespec); inode->SetAccessTime(&timespec);
inode->SetCreationTime(&timespec); inode->SetCreationTime(&timespec);
inode->SetModificationTime(&timespec); inode->SetModificationTime(&timespec);
if (sizeof(ext2_inode) < volume->InodeSize()) if (sizeof(ext2_inode) < volume->InodeSize())
node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE);
@ -979,9 +988,9 @@ Inode::_SetNumBlocks(uint64 numBlocks)
if (numBlocks > 0xffffffffffffULL) { if (numBlocks > 0xffffffffffffULL) {
fNode.SetFlag(EXT2_INODE_HUGE_FILE); fNode.SetFlag(EXT2_INODE_HUGE_FILE);
numBlocks /= (fVolume->BlockSize() / 512); numBlocks /= (fVolume->BlockSize() / 512);
} else } else
fNode.ClearFlag(EXT2_INODE_HUGE_FILE); fNode.ClearFlag(EXT2_INODE_HUGE_FILE);
fNode.SetNumBlocks64(numBlocks); fNode.SetNumBlocks64(numBlocks);
return B_OK; return B_OK;
} }

View File

@ -257,7 +257,7 @@ ext2_remove_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
status = inode->WriteBack(transaction); status = inode->WriteBack(transaction);
if (status != B_OK) if (status != B_OK)
return status; return status;
TRACE("ext2_remove_vnode(): Freeing inode\n"); TRACE("ext2_remove_vnode(): Freeing inode\n");
status = volume->FreeInode(transaction, inode->ID(), inode->IsDirectory()); 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(); off_t blockOffset = block << volume->BlockShift();
uint32 blockLength = volume->BlockSize() * count; uint32 blockLength = volume->BlockSize() * count;
if (index > 0 && (vecs[index - 1].offset if (index > 0 && (vecs[index - 1].offset
== blockOffset - vecs[index - 1].length == blockOffset - vecs[index - 1].length
|| (vecs[index - 1].offset == -1 && block == 0))) { || (vecs[index - 1].offset == -1 && block == 0))) {
vecs[index - 1].length += blockLength; vecs[index - 1].length += blockLength;
@ -470,17 +470,17 @@ ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
HTree htree(volume, directory); HTree htree(volume, directory);
DirectoryIterator* iterator; DirectoryIterator* iterator;
status = htree.Lookup(name, &iterator); status = htree.Lookup(name, &iterator);
if (status != B_OK) if (status != B_OK)
return status; return status;
ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator); ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator);
status = iterator->FindEntry(name, _vnodeID); status = iterator->FindEntry(name, _vnodeID);
if (status != B_OK) if (status != B_OK)
return status; return status;
return get_vnode(volume->FSVolume(), *_vnodeID, NULL); 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"); TRACE("ioctl: Blocks cleared\n");
transaction.Done(); transaction.Done();
transaction.Start(volume->GetJournal()); 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->GetModificationTime(&stat->st_mtim);
inode->GetChangeTime(&stat->st_ctim); inode->GetChangeTime(&stat->st_ctim);
inode->GetCreationTime(&stat->st_crtim); inode->GetCreationTime(&stat->st_crtim);
stat->st_size = inode->Size(); stat->st_size = inode->Size();
stat->st_blocks = (inode->Size() + 511) / 512; 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; Inode* inode = (Inode*)_node->private_node;
status_t status = inode->CheckPermissions(W_OK); ext2_inode& node = inode->Node();
if (status < B_OK) bool updateTime = false;
return status; 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);
bool updateTime = false; if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
if ((mask & B_STAT_SIZE) != 0) {
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);
if (inode->Size() != stat->st_size) {
off_t oldSize = inode->Size();
status = inode->Resize(transaction, stat->st_size); off_t oldSize = inode->Size();
if(status != B_OK)
return status;
if ((mask & B_STAT_SIZE_INSECURE) == 0) { status_t status = inode->Resize(transaction, stat->st_size);
rw_lock_write_unlock(inode->Lock()); if(status != B_OK)
inode->FillGapWithZeros(oldSize, inode->Size()); return status;
rw_lock_write_lock(inode->Lock());
}
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) { 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)
@ -655,10 +668,14 @@ 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);
}
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)
@ -817,7 +834,7 @@ static status_t
ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node) ext2_link(fs_volume* volume, fs_vnode* dir, const char* name, fs_vnode* node)
{ {
// TODO // TODO
return B_NOT_SUPPORTED; 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); const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum);
parentID = data->dotdot.InodeID(); parentID = data->dotdot.InodeID();
} while (parentID != oldID && parentID != oldDirID } while (parentID != oldID && parentID != oldDirID
&& parentID != EXT2_ROOT_NODE); && parentID != EXT2_ROOT_NODE);
if (parentID == oldID) if (parentID == oldID)
@ -1013,7 +1030,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
return status; return status;
} else } else
return status; return status;
// Remove entry from source folder // Remove entry from source folder
status = oldIterator->RemoveEntry(transaction); status = oldIterator->RemoveEntry(transaction);
if (status != B_OK) if (status != B_OK)
@ -1047,7 +1064,7 @@ ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName,
if (status != B_OK) { if (status != B_OK) {
entry_cache_remove(volume->ID(), oldDirectory->ID(), newName); entry_cache_remove(volume->ID(), oldDirectory->ID(), newName);
entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID); entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID);
return status; return status;
} }
@ -1237,7 +1254,7 @@ ext2_read_link(fs_volume *_volume, fs_vnode *_node, char *buffer,
static status_t static status_t
ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name, ext2_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
int mode) int mode)
{ {
TRACE("ext2_create_dir()\n"); TRACE("ext2_create_dir()\n");
Volume* volume = (Volume*)_volume->private_volume; Volume* volume = (Volume*)_volume->private_volume;
Inode* directory = (Inode*)_directory->private_node; 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) ext2_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
{ {
Inode* inode = (Inode*)_node->private_node; 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) int openMode, void** _cookie)
{ {
TRACE("%s()\n", __FUNCTION__); TRACE("%s()\n", __FUNCTION__);
Volume* volume = (Volume*)_volume->private_volume; Volume* volume = (Volume*)_volume->private_volume;
Inode* inode = (Inode*)_node->private_node; Inode* inode = (Inode*)_node->private_node;
Attribute attribute(inode); Attribute attribute(inode);
@ -1610,6 +1627,7 @@ fs_volume_ops gExt2VolumeOps = {
&ext2_get_vnode, &ext2_get_vnode,
}; };
fs_vnode_ops gExt2VnodeOps = { fs_vnode_ops gExt2VnodeOps = {
/* vnode operations */ /* vnode operations */
&ext2_lookup, &ext2_lookup,
@ -1680,9 +1698,9 @@ fs_vnode_ops gExt2VnodeOps = {
NULL, //&ext2_write_attr_stat, NULL, //&ext2_write_attr_stat,
NULL, //&ext2_rename_attr, NULL, //&ext2_rename_attr,
NULL, //&ext2_remove_attr, NULL, //&ext2_remove_attr,
}; };
static file_system_module_info sExt2FileSystem = { static file_system_module_info sExt2FileSystem = {
{ {
"file_systems/ext2" B_CURRENT_FS_API_VERSION, "file_systems/ext2" B_CURRENT_FS_API_VERSION,
@ -1705,6 +1723,7 @@ static file_system_module_info sExt2FileSystem = {
NULL, NULL,
}; };
module_info *modules[] = { module_info *modules[] = {
(module_info *)&sExt2FileSystem, (module_info *)&sExt2FileSystem,
NULL, NULL,