static status_t ext2_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, ino_t* _vnodeID) { TRACE("ext2_lookup: name address: %p\n", name); TRACE("ext2_lookup: name: %s\n", name); Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; // check access permissions status_t status = directory->CheckPermissions(X_OK); if (status < B_OK) return status; HTree htree(volume, directory); DirectoryIterator* iterator; status = htree.Lookup(name, &iterator); if (status != B_OK) return status; ObjectDeleter<DirectoryIterator> iteratorDeleter(iterator); status = iterator->FindEntry(name, _vnodeID); if (status != B_OK) return status; return get_vnode(volume->FSVolume(), *_vnodeID, NULL); }
static status_t ext2_open(fs_volume* _volume, fs_vnode* _node, int openMode, void** _cookie) { Volume* volume = (Volume*)_volume->private_volume; Inode* inode = (Inode*)_node->private_node; // opening a directory read-only is allowed, although you can't read // any data from it. if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) return B_IS_A_DIRECTORY; status_t status = inode->CheckPermissions(open_mode_to_access(openMode) | (openMode & O_TRUNC ? W_OK : 0)); if (status != B_OK) return status; // Prepare the cookie file_cookie* cookie = new(std::nothrow) file_cookie; if (cookie == NULL) return B_NO_MEMORY; ObjectDeleter<file_cookie> cookieDeleter(cookie); cookie->open_mode = openMode & EXT2_OPEN_MODE_USER_MASK; cookie->last_size = inode->Size(); cookie->last_notification = system_time(); MethodDeleter<Inode, status_t> fileCacheEnabler(&Inode::EnableFileCache); if ((openMode & O_NOCACHE) != 0) { status = inode->DisableFileCache(); if (status != B_OK) return status; fileCacheEnabler.SetTo(inode); } // Should we truncate the file? if ((openMode & O_TRUNC) != 0) { if ((openMode & O_RWMASK) == O_RDONLY) return B_NOT_ALLOWED; Transaction transaction(volume->GetJournal()); inode->WriteLockInTransaction(transaction); status_t status = inode->Resize(transaction, 0); if (status == B_OK) status = inode->WriteBack(transaction); if (status == B_OK) status = transaction.Done(); if (status != B_OK) return status; // TODO: No need to notify file size changed? } fileCacheEnabler.Detach(); cookieDeleter.Detach(); *_cookie = cookie; return B_OK; }
static status_t ext2_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name) { TRACE("ext2_unlink()\n"); if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) return B_NOT_ALLOWED; Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; status_t status = directory->CheckPermissions(W_OK); if (status != B_OK) return status; TRACE("ext2_unlink(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); directory->WriteLockInTransaction(transaction); TRACE("ext2_unlink(): Looking up for directory entry\n"); HTree htree(volume, directory); DirectoryIterator* directoryIterator; status = htree.Lookup(name, &directoryIterator); if (status != B_OK) return status; ino_t id; status = directoryIterator->FindEntry(name, &id); if (status != B_OK) return status; Vnode vnode(volume, id); Inode* inode; status = vnode.Get(&inode); if (status != B_OK) return status; inode->WriteLockInTransaction(transaction); status = inode->Unlink(transaction); if (status != B_OK) return status; status = directoryIterator->RemoveEntry(transaction); if (status != B_OK) return status; entry_cache_remove(volume->ID(), directory->ID(), name); status = transaction.Done(); if (status != B_OK) entry_cache_add(volume->ID(), directory->ID(), name, id); else notify_entry_removed(volume->ID(), directory->ID(), name, id); return status; }
static status_t ext2_open_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie) { Inode* inode = (Inode*)_node->private_node; status_t status = inode->CheckPermissions(R_OK); if (status < B_OK) return status; if (!inode->IsDirectory()) return B_NOT_A_DIRECTORY; DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode); if (iterator == NULL) return B_NO_MEMORY; *_cookie = iterator; return B_OK; }
static status_t btrfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name, ino_t* _vnodeID) { TRACE("btrfs_lookup: name address: %p (%s)\n", name, name); Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; // check access permissions status_t status = directory->CheckPermissions(X_OK); if (status < B_OK) return status; status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID); if (status != B_OK) return status; return get_vnode(volume->FSVolume(), *_vnodeID, NULL); }
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; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (!directory->IsDirectory()) return B_BAD_TYPE; status_t status = directory->CheckPermissions(W_OK); if (status != B_OK) return status; TRACE("ext2_create_dir(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); ino_t id; status = Inode::Create(transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, EXT2_TYPE_DIRECTORY, NULL, &id); if (status != B_OK) return status; put_vnode(volume->FSVolume(), id); entry_cache_add(volume->ID(), directory->ID(), name, id); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), directory->ID(), name); return status; } notify_entry_created(volume->ID(), directory->ID(), name, id); TRACE("ext2_create_dir(): Done\n"); return B_OK; }
static status_t btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode, void** _cookie) { Inode* inode = (Inode*)_node->private_node; // opening a directory read-only is allowed, although you can't read // any data from it. if (inode->IsDirectory() && (openMode & O_RWMASK) != 0) return B_IS_A_DIRECTORY; status_t status = inode->CheckPermissions(open_mode_to_access(openMode) | (openMode & O_TRUNC ? W_OK : 0)); if (status != B_OK) return status; // Prepare the cookie file_cookie* cookie = new(std::nothrow) file_cookie; if (cookie == NULL) return B_NO_MEMORY; ObjectDeleter<file_cookie> cookieDeleter(cookie); cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK; cookie->last_size = inode->Size(); cookie->last_notification = system_time(); if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) { // Disable the file cache, if requested? status = file_cache_disable(inode->FileCache()); if (status != B_OK) return status; } cookieDeleter.Detach(); *_cookie = cookie; return B_OK; }
static status_t btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode) { Inode* inode = (Inode*)_node->private_node; return inode->CheckPermissions(accessMode); }
/*static*/ status_t Inode::Create(Transaction& transaction, Inode* parent, const char* name, int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id, Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags) { TRACE("Inode::Create()\n"); Volume* volume = transaction.GetVolume(); DirectoryIterator* entries = NULL; ObjectDeleter<DirectoryIterator> entriesDeleter; if (parent != NULL) { parent->WriteLockInTransaction(transaction); TRACE("Inode::Create(): Looking up entry destination\n"); HTree htree(volume, parent); status_t status = htree.Lookup(name, &entries); if (status == B_ENTRY_NOT_FOUND) { panic("We need to add the first node.\n"); return B_ERROR; } if (status != B_OK) return status; entriesDeleter.SetTo(entries); TRACE("Inode::Create(): Looking up to see if file already exists\n"); ino_t entryID; status = entries->FindEntry(name, &entryID); if (status == B_OK) { // File already exists TRACE("Inode::Create(): File already exists\n"); if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0) return B_FILE_EXISTS; Vnode vnode(volume, entryID); Inode* inode; status = vnode.Get(&inode); if (status != B_OK) { TRACE("Inode::Create() Failed to get the inode from the " "vnode\n"); return B_ENTRY_NOT_FOUND; } if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY) return B_IS_A_DIRECTORY; if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory()) return B_NOT_A_DIRECTORY; if (inode->CheckPermissions(open_mode_to_access(openMode) | ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK) return B_NOT_ALLOWED; if ((openMode & O_TRUNC) != 0) { // Truncate requested TRACE("Inode::Create(): Truncating file\n"); inode->WriteLockInTransaction(transaction); status = inode->Resize(transaction, 0); if (status != B_OK) return status; } if (_created != NULL) *_created = false; if (_id != NULL) *_id = inode->ID(); if (_inode != NULL) *_inode = inode; if (_id != NULL || _inode != NULL) vnode.Keep(); TRACE("Inode::Create(): Done opening file\n"); return B_OK; /*} else if ((mode & S_ATTR_DIR) == 0) { TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n"); return B_BAD_VALUE;*/ } else if ((openMode & O_DIRECTORY) != 0) { TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n"); return B_ENTRY_NOT_FOUND; } // Return to initial position TRACE("Inode::Create(): Restarting iterator\n"); entries->Restart(); } status_t status; if (parent != NULL) { status = parent->CheckPermissions(W_OK); if (status != B_OK) return status; } TRACE("Inode::Create(): Allocating inode\n"); ino_t id; status = volume->AllocateInode(transaction, parent, mode, id); if (status != B_OK) { ERROR("Inode::Create(): AllocateInode() failed\n"); return status; } if (entries != NULL) { size_t nameLength = strlen(name); status = entries->AddEntry(transaction, name, nameLength, id, type); if (status != B_OK) { ERROR("Inode::Create(): AddEntry() failed\n"); return status; } } TRACE("Inode::Create(): Creating inode\n"); Inode* inode = new(std::nothrow) Inode(volume); if (inode == NULL) return B_NO_MEMORY; TRACE("Inode::Create(): Getting node structure\n"); ext2_inode& node = inode->Node(); TRACE("Inode::Create(): Initializing inode data\n"); memset(&node, 0, sizeof(ext2_inode)); node.SetMode(mode); node.SetUserID(geteuid()); node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid()); node.SetNumLinks(inode->IsDirectory() ? 2 : 1); TRACE("Inode::Create(): Updating time\n"); struct timespec timespec; _BigtimeToTimespec(real_time_clock_usecs(), ×pec); inode->SetAccessTime(×pec); inode->SetCreationTime(×pec); inode->SetModificationTime(×pec); node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED); if (volume->HasExtentsFeature() && (inode->IsDirectory() || inode->IsFile())) { node.SetFlag(EXT2_INODE_EXTENTS); ExtentStream stream(volume, &node.extent_stream, 0); stream.Init(); ASSERT(stream.Check()); } if (sizeof(ext2_inode) < volume->InodeSize()) node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); TRACE("Inode::Create(): Updating ID\n"); inode->fID = id; if (inode->IsDirectory()) { TRACE("Inode::Create(): Initializing directory\n"); status = inode->InitDirectory(transaction, parent); if (status != B_OK) { ERROR("Inode::Create(): InitDirectory() failed\n"); delete inode; return status; } } // TODO: Maybe it can be better /*if (volume->HasExtendedAttributes()) { TRACE("Inode::Create(): Initializing extended attributes\n"); uint32 blockGroup = 0; uint32 pos = 0; uint32 allocated; status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos, allocated); if (status != B_OK) return status; // Clear the new block uint32 blockNum = volume->FirstDataBlock() + pos + volume->BlocksPerGroup() * blockGroup; CachedBlock cached(volume); cached.SetToWritable(transaction, blockNum, true); node.SetExtendedAttributesBlock(blockNum); }*/ TRACE("Inode::Create(): Saving inode\n"); status = inode->WriteBack(transaction); if (status != B_OK) { delete inode; return status; } TRACE("Inode::Create(): Creating vnode\n"); Vnode vnode; status = vnode.Publish(transaction, inode, vnodeOps, publishFlags); if (status != B_OK) return status; if (!inode->IsSymLink()) { // Vnode::Publish doesn't publish symlinks if (!inode->IsDirectory()) { status = inode->EnableFileCache(); if (status != B_OK) return status; } inode->WriteLockInTransaction(transaction); } if (_created) *_created = true; if (_id != NULL) *_id = id; if (_inode != NULL) *_inode = inode; if (_id != NULL || _inode != NULL) vnode.Keep(); TRACE("Inode::Create(): Deleting entries iterator\n"); DirectoryIterator* iterator = entriesDeleter.Detach(); TRACE("Inode::Create(): Entries iterator: %p\n", entries); delete iterator; TRACE("Inode::Create(): Done\n"); return B_OK; }
static status_t ext2_rename(fs_volume* _volume, fs_vnode* _oldDir, const char* oldName, fs_vnode* _newDir, const char* newName) { TRACE("ext2_rename()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* oldDirectory = (Inode*)_oldDir->private_node; Inode* newDirectory = (Inode*)_newDir->private_node; if (oldDirectory == newDirectory && strcmp(oldName, newName) == 0) return B_OK; Transaction transaction(volume->GetJournal()); oldDirectory->WriteLockInTransaction(transaction); if (oldDirectory != newDirectory) newDirectory->WriteLockInTransaction(transaction); status_t status = oldDirectory->CheckPermissions(W_OK); if (status == B_OK) status = newDirectory->CheckPermissions(W_OK); if (status != B_OK) return status; HTree oldHTree(volume, oldDirectory); DirectoryIterator* oldIterator; status = oldHTree.Lookup(oldName, &oldIterator); if (status != B_OK) return status; ObjectDeleter<DirectoryIterator> oldIteratorDeleter(oldIterator); ino_t oldID; status = oldIterator->FindEntry(oldName, &oldID); if (status != B_OK) return status; if (oldDirectory != newDirectory) { TRACE("ext2_rename(): Different parent directories\n"); CachedBlock cached(volume); ino_t parentID = newDirectory->ID(); ino_t oldDirID = oldDirectory->ID(); do { Vnode vnode(volume, parentID); Inode* parent; status = vnode.Get(&parent); if (status != B_OK) return B_IO_ERROR; fsblock_t blockNum; status = parent->FindBlock(0, blockNum); if (status != B_OK) return status; const HTreeRoot* data = (const HTreeRoot*)cached.SetTo(blockNum); parentID = data->dotdot.InodeID(); } while (parentID != oldID && parentID != oldDirID && parentID != EXT2_ROOT_NODE); if (parentID == oldID) return B_BAD_VALUE; } HTree newHTree(volume, newDirectory); DirectoryIterator* newIterator; status = newHTree.Lookup(newName, &newIterator); if (status != B_OK) return status; ObjectDeleter<DirectoryIterator> newIteratorDeleter(newIterator); Vnode vnode(volume, oldID); Inode* inode; status = vnode.Get(&inode); if (status != B_OK) return status; uint8 fileType; // TODO: Support all file types? if (inode->IsDirectory()) fileType = EXT2_TYPE_DIRECTORY; else if (inode->IsSymLink()) fileType = EXT2_TYPE_SYMLINK; else fileType = EXT2_TYPE_FILE; // Add entry in destination directory ino_t existentID; status = newIterator->FindEntry(newName, &existentID); if (status == B_OK) { if (existentID == oldID) { // Remove entry in oldID // return inode->Unlink(); return B_BAD_VALUE; } Vnode vnodeExistent(volume, existentID); Inode* existent; if (vnodeExistent.Get(&existent) != B_OK) return B_NAME_IN_USE; if (existent->IsDirectory() != inode->IsDirectory()) { return existent->IsDirectory() ? B_IS_A_DIRECTORY : B_NOT_A_DIRECTORY; } // TODO: Perhaps we have to revert this in case of error? status = newIterator->ChangeEntry(transaction, oldID, fileType); if (status != B_OK) return status; notify_entry_removed(volume->ID(), newDirectory->ID(), newName, existentID); } else if (status == B_ENTRY_NOT_FOUND) { newIterator->Restart(); status = newIterator->AddEntry(transaction, newName, strlen(newName), oldID, fileType); if (status != B_OK) return status; } else return status; // Remove entry from source folder status = oldIterator->RemoveEntry(transaction); if (status != B_OK) return status; inode->WriteLockInTransaction(transaction); if (oldDirectory != newDirectory && inode->IsDirectory()) { DirectoryIterator inodeIterator(inode); status = inodeIterator.FindEntry(".."); if (status == B_ENTRY_NOT_FOUND) { TRACE("Corrupt file system. Missing \"..\" in directory %" B_PRIdINO "\n", inode->ID()); return B_BAD_DATA; } else if (status != B_OK) return status; inodeIterator.ChangeEntry(transaction, newDirectory->ID(), (uint8)EXT2_TYPE_DIRECTORY); } status = inode->WriteBack(transaction); if (status != B_OK) return status; entry_cache_remove(volume->ID(), oldDirectory->ID(), oldName); entry_cache_add(volume->ID(), newDirectory->ID(), newName, oldID); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), oldDirectory->ID(), newName); entry_cache_add(volume->ID(), newDirectory->ID(), oldName, oldID); return status; } notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName, newDirectory->ID(), newName, oldID); return B_OK; }
static status_t ext2_create_symlink(fs_volume* _volume, fs_vnode* _directory, const char* name, const char* path, int mode) { TRACE("ext2_create_symlink()\n"); Volume* volume = (Volume*)_volume->private_volume; Inode* directory = (Inode*)_directory->private_node; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; if (!directory->IsDirectory()) return B_BAD_TYPE; status_t status = directory->CheckPermissions(W_OK); if (status != B_OK) return status; TRACE("ext2_create_symlink(): Starting transaction\n"); Transaction transaction(volume->GetJournal()); Inode* link; ino_t id; status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 0, (uint8)EXT2_TYPE_SYMLINK, NULL, &id, &link); if (status != B_OK) return status; // TODO: We have to prepare the link before publishing? size_t length = strlen(path); TRACE("ext2_create_symlink(): Path (%s) length: %d\n", path, (int)length); if (length < EXT2_SHORT_SYMLINK_LENGTH) { strcpy(link->Node().symlink, path); link->Node().SetSize((uint32)length); TRACE("ext2_create_symlink(): Publishing vnode\n"); publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps, link->Mode(), 0); put_vnode(volume->FSVolume(), id); } else { TRACE("ext2_create_symlink(): Publishing vnode\n"); publish_vnode(volume->FSVolume(), id, link, &gExt2VnodeOps, link->Mode(), 0); put_vnode(volume->FSVolume(), id); if (!link->HasFileCache()) { status = link->CreateFileCache(); if (status != B_OK) return status; } size_t written = length; status = link->WriteAt(transaction, 0, (const uint8*)path, &written); if (status == B_OK && written != length) status = B_IO_ERROR; } if (status == B_OK) status = link->WriteBack(transaction); entry_cache_add(volume->ID(), directory->ID(), name, id); status = transaction.Done(); if (status != B_OK) { entry_cache_remove(volume->ID(), directory->ID(), name); return status; } notify_entry_created(volume->ID(), directory->ID(), name, id); TRACE("ext2_create_symlink(): Done\n"); return status; }
status_t ext2_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat, uint32 mask) { TRACE("ext2_write_stat\n"); Volume* volume = (Volume*)_volume->private_volume; if (volume->IsReadOnly()) return B_READ_ONLY_DEVICE; Inode* inode = (Inode*)_node->private_node; 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); 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); off_t oldSize = inode->Size(); status_t status = inode->Resize(transaction, stat->st_size); if(status != B_OK) return status; 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; } 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) newTimespec = stat->st_mtim; if ((mask & B_STAT_CHANGE_TIME) != 0 && stat->st_ctim.tv_sec > newTimespec.tv_sec) newTimespec = stat->st_ctim; if (newTimespec.tv_sec == 0) Inode::_BigtimeToTimespec(real_time_clock_usecs(), &newTimespec); inode->SetModificationTime(&newTimespec); } 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_t status = inode->WriteBack(transaction); if (status == B_OK) status = transaction.Done(); if (status == B_OK) notify_stat_changed(volume->ID(), -1, inode->ID(), mask); return status; }