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; }
// _HandleRequest status_t KernelRequestHandler::_HandleRequest(NotifyListenerRequest* request) { // check and execute the request status_t result = B_OK; if (fVolume && request->device != fVolume->GetID()) result = B_BAD_VALUE; // get the names // name char* name = (char*)request->name.GetData(); int32 nameLen = request->name.GetSize(); if (name && (nameLen <= 0)) name = NULL; else if (name) name[nameLen - 1] = '\0'; // NULL-terminate to be safe // old name char* oldName = (char*)request->oldName.GetData(); int32 oldNameLen = request->oldName.GetSize(); if (oldName && (oldNameLen <= 0)) oldName = NULL; else if (oldName) oldName[oldNameLen - 1] = '\0'; // NULL-terminate to be safe // check the names if (result == B_OK) { switch (request->operation) { case B_ENTRY_MOVED: if (!oldName) { ERROR(("NotifyListenerRequest: NULL oldName for " "B_ENTRY_MOVED\n")); result = B_BAD_VALUE; break; } // fall through... case B_ENTRY_CREATED: case B_ENTRY_REMOVED: case B_ATTR_CHANGED: if (!name) { ERROR(("NotifyListenerRequest: NULL name for opcode: %" B_PRId32 "\n", request->operation)); result = B_BAD_VALUE; } break; case B_STAT_CHANGED: break; } } // execute the request if (result == B_OK) { switch (request->operation) { case B_ENTRY_CREATED: PRINT(("notify_entry_created(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", %" B_PRId64 ")\n", request->device, request->directory, name, request->node)); result = notify_entry_created(request->device, request->directory, name, request->node); break; case B_ENTRY_REMOVED: PRINT(("notify_entry_removed(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", %" B_PRId64 ")\n", request->device, request->directory, name, request->node)); result = notify_entry_removed(request->device, request->directory, name, request->node); break; case B_ENTRY_MOVED: PRINT(("notify_entry_moved(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", %" B_PRId64 ", \"%s\", %" B_PRId64 ")\n", request->device, request->oldDirectory, oldName, request->directory, name, request->node)); result = notify_entry_moved(request->device, request->oldDirectory, oldName, request->directory, name, request->node); break; case B_STAT_CHANGED: PRINT(("notify_stat_changed(%" B_PRId32 ", %" B_PRId64 ", " "0x%" B_PRIx32 ")\n", request->device, request->node, request->details)); result = notify_stat_changed(request->device, request->node, request->details); break; case B_ATTR_CHANGED: PRINT(("notify_attribute_changed(%" B_PRId32 ", %" B_PRId64 ", " "\"%s\", 0x%" B_PRIx32 ")\n", request->device, request->node, name, (int32)request->details)); result = notify_attribute_changed(request->device, request->node, name, (int32)request->details); break; default: ERROR(("NotifyQueryRequest: unsupported operation: %" B_PRId32 "\n", request->operation)); result = B_BAD_VALUE; break; } } // prepare the reply RequestAllocator allocator(fPort->GetPort()); NotifyListenerReply* reply; status_t error = AllocateRequest(allocator, &reply); if (error != B_OK) return error; reply->error = result; // send the reply return fPort->SendRequest(&allocator); }
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; }