| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SMBPROVIDER_FAKE_SAMBA_INTERFACE_H_ |
| #define SMBPROVIDER_FAKE_SAMBA_INTERFACE_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "smbprovider/samba_interface.h" |
| |
| namespace smbprovider { |
| |
| constexpr size_t kDirEntBufSize = 1024; |
| |
| // Fake implementation of SambaInterface. Uses a map to simulate a fake file |
| // system that can open and close directories. It can also store entries through |
| // |FakeEntry| which hold entry metadata. |
| class FakeSambaInterface : public SambaInterface { |
| public: |
| FakeSambaInterface(); |
| ~FakeSambaInterface() override; |
| |
| // SambaInterface overrides. |
| int32_t OpenDirectory(const std::string& directory_path, |
| int32_t* dir_id) override; |
| |
| int32_t CloseDirectory(int32_t dir_id) override; |
| |
| int32_t GetDirectoryEntry(int32_t dir_id, |
| const struct smbc_dirent** dirent) override; |
| |
| int32_t GetDirectoryEntryWithMetadata( |
| int32_t dir_id, const struct libsmb_file_info** file_info) override; |
| |
| int32_t GetEntryStatus(const std::string& entry_path, |
| struct stat* stat) override; |
| |
| int32_t OpenFile(const std::string& file_path, |
| int32_t flags, |
| int32_t* file_id) override; |
| |
| int32_t CloseFile(int32_t file_id) override; |
| |
| int32_t ReadFile(int32_t file_id, |
| uint8_t* buffer, |
| size_t buffer_size, |
| size_t* bytes_read) override; |
| |
| int32_t Seek(int32_t file_id, int64_t offset) override; |
| |
| int32_t Unlink(const std::string& file_path) override; |
| |
| int32_t RemoveDirectory(const std::string& dir_path) override; |
| |
| int32_t CreateFile(const std::string& file_path, int32_t* file_id) override; |
| |
| int32_t Truncate(int32_t file_id, size_t size) override; |
| |
| int32_t WriteFile(int32_t file_id, |
| const uint8_t* buffer, |
| size_t buffer_size) override; |
| |
| int32_t CreateDirectory(const std::string& directory_path) override; |
| |
| int32_t MoveEntry(const std::string& source_path, |
| const std::string& target_path) override; |
| |
| int32_t CopyFile(const std::string& source_path, |
| const std::string& target_path) override; |
| |
| int32_t SpliceFile(int32_t source_fd, |
| int32_t target_fd, |
| off_t length, |
| off_t* bytes_written) override; |
| |
| SambaInterface::SambaInterfaceId GetSambaInterfaceId() override; |
| |
| // Adds a directory that is able to be opened through OpenDirectory(). |
| // Does not support recursive creation. All parents must exist. |
| void AddDirectory(const std::string& path); |
| void AddDirectory(const std::string& path, bool locked, uint32_t smbc_type); |
| void AddDirectory(const std::string& path, |
| bool locked, |
| uint32_t smbc_type, |
| time_t date); |
| |
| // Adds a directory that has the type SMBC_FILE_SHARE. |
| void AddShare(const std::string& path); |
| |
| // Adds a directory that has the type SMBC_SERVER. |
| void AddServer(const std::string& server_url); |
| |
| // Adds a directory with |locked| set to true. All parents must exist. |
| // Operations on a locked directory will fail. |
| void AddLockedDirectory(const std::string& path); |
| |
| // Adds a file at the specified path. All parents must exist. |
| void AddFile(const std::string& path); |
| void AddFile(const std::string& path, size_t size); |
| void AddFile(const std::string& path, size_t size, time_t date); |
| void AddFile(const std::string& path, size_t size, time_t date, bool locked); |
| void AddFile(const std::string& path, |
| time_t date, |
| std::vector<uint8_t> file_data); |
| |
| // Adds a file at the specified path with |locked| set to true. All parents |
| // must exist. Operations on a locked file will fail. |
| void AddLockedFile(const std::string& path); |
| |
| void AddEntry(const std::string& path, uint32_t smbc_type); |
| |
| // Helper method to check if there are any leftover open directories or files |
| // in |open_fds|. |
| bool HasOpenEntries() const; |
| |
| // Helpers to check the flags set on a FakeFile entry. |
| bool HasReadSet(int32_t fd) const; |
| bool HasWriteSet(int32_t fd) const; |
| |
| // Helpers to check whether a given file descriptor |fd| is open. |
| bool IsFileFDOpen(uint32_t fd) const; |
| bool IsDirectoryFDOpen(uint32_t fd) const; |
| |
| // Checks whether an entry exists in a given |path|. |
| bool EntryExists(const std::string& path) const; |
| |
| // Gets current offset of file. |
| size_t GetFileOffset(int32_t fd) const; |
| |
| // Gets the current file size of a file in |path|. |
| size_t GetFileSize(const std::string& path) const; |
| |
| // Checks if a files data is equal to the expected value. Returns true if |
| // equal. |
| bool IsFileDataEqual(const std::string& path, |
| const std::vector<uint8_t>& expected) const; |
| |
| // Helper method to set the errno CloseFile() should return. |
| void SetCloseFileError(int32_t error); |
| |
| // Helper method to set the errno Truncate() should return. |
| void SetTruncateError(int32_t error); |
| |
| // Helper method to manually set the current_entry for the OpenInfo |
| // corresponding to |dir_id|. |
| void SetCurrentEntry(int32_t dir_id, size_t index); |
| |
| // Helper method to set the errno GetDirectory() should return. |
| void SetGetDirectoryError(int32_t error); |
| |
| // Helper method to get the path for the OpenInfo corresponding to |dir_id|. |
| std::string GetCurrentEntry(int32_t dir_id); |
| |
| private: |
| // Replacement struct for smbc_dirent within FakeSambaInterface. |
| struct FakeEntry { |
| std::string name; |
| uint32_t smbc_type; |
| size_t size; |
| time_t date; |
| // Indicates whether the entry should be inacessable by the user. |
| bool locked = false; |
| |
| FakeEntry(const std::string& full_path, |
| uint32_t smbc_type, |
| size_t size, |
| time_t date, |
| bool locked); |
| |
| FakeEntry(const std::string& full_path, |
| uint32_t smbc_type, |
| size_t size, |
| time_t date); |
| |
| virtual ~FakeEntry() = default; |
| |
| // Returns true for SMBC_FILE and SMBC_DIR. False for all others. |
| bool IsValidEntryType() const; |
| |
| // Returns true for SMBC_FILE. |
| bool IsFile() const; |
| |
| // Returns true for SMBC_DIR. |
| bool IsDir() const; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeEntry); |
| }; |
| |
| struct FakeDirectory : FakeEntry { |
| using Entries = std::vector<std::unique_ptr<FakeEntry>>; |
| using EntriesIterator = Entries::iterator; |
| |
| FakeDirectory(const std::string& full_path, |
| bool locked, |
| uint32_t smbc_type, |
| time_t date) |
| : FakeEntry(full_path, smbc_type, 0 /* size */, date, locked) {} |
| |
| FakeDirectory(const std::string& full_path, bool locked) |
| : FakeDirectory(full_path, locked, SMBC_DIR, 0) {} |
| |
| explicit FakeDirectory(const std::string& full_path) |
| : FakeDirectory(full_path, false /* locked */) {} |
| |
| // Returns entries.empty(). |
| bool IsEmpty() const; |
| |
| // Returns a pointer to the entry in the directory with |name|. |
| FakeEntry* FindEntry(const std::string& name); |
| |
| // Removes the entry in entries with |name| from the directory. |
| // This function must only be called on files and empty directories. |
| // Returns index of the entry if it was found and deleted. Otherwise returns |
| // -1. |
| int32_t RemoveEntry(const std::string& name); |
| |
| // Checks whether the provided FakeEntry is a file or an empty directory. |
| bool IsFileOrEmptyDirectory(FakeEntry* entry) const; |
| |
| EntriesIterator GetEntryIt(const std::string& name); |
| |
| // Contains pointers to entries that can be found in this directory. |
| Entries entries; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeDirectory); |
| }; |
| |
| struct FakeFile : FakeEntry { |
| FakeFile(const std::string& full_path, |
| size_t size, |
| time_t date, |
| bool locked) |
| : FakeEntry(full_path, SMBC_FILE, size, date, locked), |
| has_data(false) {} |
| |
| FakeFile(const std::string& full_path, |
| time_t date, |
| std::vector<uint8_t> file_data) |
| : FakeEntry( |
| full_path, SMBC_FILE, file_data.size(), date, false /* locked */), |
| has_data(true), |
| data(std::move(file_data)) {} |
| |
| // Writes |buffer_size| bytes from |buffer| into the file starting from |
| // |offset|. |
| void WriteData(size_t offset, const uint8_t* buffer, size_t buffer_size); |
| |
| // This is used to track if the file currently has data. This may be false |
| // during initialization, but can be switched to true if data is added |
| // later. |
| bool has_data; |
| |
| // Only populated for SMBC_FILE and is optionally provided. |
| // This only contains data if has_data is true. |
| // Contains the data for the file. |
| std::vector<uint8_t> data; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeFile); |
| }; |
| |
| struct OpenInfo { |
| std::string full_path; |
| |
| // When type is FakeDirectory, this keeps track of the index of the next |
| // file to be read from the directory. This is set to 0 when opening. |
| // When type is FakeFile, this functions as the current offset of the file. |
| size_t current_index = 0; |
| |
| // Type of FakeEntry that this OpenInfo is referring to. |
| uint32_t smbc_type; |
| |
| // For testing that read/write are set correctly by Open(). |
| bool readable = false; |
| bool writeable = false; |
| |
| OpenInfo(const std::string& full_path, uint32_t smbc_type) |
| : full_path(full_path), smbc_type(smbc_type) {} |
| OpenInfo(const std::string& full_path, |
| uint32_t smbc_type, |
| bool readable, |
| bool writeable) |
| : full_path(full_path), |
| smbc_type(smbc_type), |
| readable(readable), |
| writeable(writeable) {} |
| |
| OpenInfo(OpenInfo&& other) |
| : full_path(std::move(other.full_path)), |
| current_index(other.current_index), |
| smbc_type(other.smbc_type), |
| readable(other.readable), |
| writeable(other.writeable) {} |
| |
| // Returns true if |dir_path| is the same as full_path. |
| bool IsForDir(const std::string& dir_path); |
| |
| DISALLOW_COPY_AND_ASSIGN(OpenInfo); |
| }; |
| |
| using OpenEntries = std::map<uint32_t, OpenInfo>; |
| using OpenEntriesIterator = OpenEntries::iterator; |
| using OpenEntriesConstIterator = OpenEntries::const_iterator; |
| |
| // Adds an open directory to open_fds. |
| int32_t AddOpenDirectory(const std::string& path); |
| |
| // Adds an open file to open_fds. |
| int32_t AddOpenFile(const std::string& path, bool readable, bool writeable); |
| |
| // Checks whether the file/directory at the specified path is open. |
| bool IsOpen(const std::string& full_path) const; |
| |
| // Checks whether a file descriptor is open. |
| bool IsFDOpen(uint32_t fd) const; |
| |
| // Returns an iterator to an OpenInfo in open_fds. |
| OpenEntriesIterator FindOpenFD(uint32_t fd); |
| |
| // Returns a const_iterator to an OpenInfo in open_fds. |
| OpenEntriesConstIterator FindOpenFD(uint32_t fd) const; |
| |
| // Recurses through the file system, returning a pointer to a directory. |
| // Pointer is owned by the class and should not be retained passed the |
| // lifetime of a single public method call as it could be invalidated. |
| // |full_path| is expected to be in /foo/bar format. |
| FakeDirectory* GetDirectory(const std::string& full_path, |
| int32_t* error) const; |
| FakeDirectory* GetDirectory(const std::string& full_path) const; |
| |
| // Recurses through the file system, returning a pointer to the entry. |
| // Pointer is owned by the class and should not be retained passed the |
| // lifetime of a single public method call as it could be invalidated. |
| FakeEntry* GetEntry(const std::string& entry_path) const; |
| |
| // Recurses through the file system, returning a pointer to the file. |
| // Pointer is owned by the class and should not be retained passed the |
| // lifetime of a single public method call as it could be invalidated. |
| FakeFile* GetFile(const std::string& file_path) const; |
| |
| // Checks whether the directory has more entries. |
| bool HasMoreEntries(uint32_t dir_fd) const; |
| |
| // Goes through |open_fds| and rewinds |current_index| if |deleted_index| is |
| // already equal to entries.size() for the directory. |
| void RewindOpenInfoIndicesIfNeccessary(const std::string& dir_path, |
| size_t deleted_index); |
| |
| // Removes |full_path| from the file system and calls |
| // RewindOpenInfoIndicesIfNeccessary. |
| void RemoveEntryAndResetIndicies(const std::string& full_path); |
| |
| // Checks whether a MoveEntry operation should be performed from a |src_entry| |
| // to an already existing |target_entry|. Returns 0 if both |src_entry| and |
| // |target_entry| are empty directories, corresponding errno otherwise. |
| int32_t CheckEntriesValidForMove(FakeEntry* src_entry, |
| FakeEntry* target_entry) const; |
| |
| // Moves the directory entry at |source_path| to |target_path|. |
| // Returns an error if either of the parent directories is locked or if |
| // |source_path| is a directory and is locked. |
| int32_t MoveEntryFromSourceToTarget(const std::string& source_path, |
| const std::string& target_path); |
| |
| // Helper method that gets the parent directories for |source_path| and |
| // |target_path|. Returns 0 on success and error on failure. |
| int32_t GetSourceAndTargetParentDirectories( |
| const std::string& source_path, |
| const std::string& target_path, |
| FakeDirectory** source_parent, |
| FakeDirectory** target_parent) const; |
| |
| // Populates the |file_info_| member struct and returns a pointer to it. |
| const struct libsmb_file_info* PopulateFileInfo(const FakeEntry& entry); |
| |
| // Populates |dirent_buf_| member and returns a pointer to it. |
| const struct smbc_dirent* PopulateDirEnt(const FakeEntry& entry); |
| |
| // Counter for assigning file descriptor when opening. |
| uint32_t next_fd = 1; |
| |
| // Root directory of the file system. |
| std::unique_ptr<FakeDirectory> root; |
| |
| // Errno for CloseFile() to return. If this is set to anything other than |
| // 0, CloseFile() will return the error this is set to. |
| int32_t close_file_error_ = 0; |
| |
| // Errno for Truncate() to return. If this is set to anything other than |
| // 0, Truncate() will return the error this is set to. |
| int32_t truncate_error_ = 0; |
| |
| // Errno for GetDirectory() to return. If this is set to anything other than |
| // 0, GetDirectory() will return the error this is set to. |
| int32_t get_directory_error_ = 0; |
| |
| // Keeps track of open files and directories. |
| // Key: fd of the file/directory. |
| // Value: OpenInfo that corresponds with the key. |
| OpenEntries open_fds; |
| |
| // Struct to hold a single entry from readdir and readdirplus. The real API |
| // stores these in a list correlated to the open directory and returns |
| // a pointer into that list. This means that GetDirectoryEntryWithMetadata |
| // returns a pointer and the caller is not expected to free it. The fake |
| // only maintains the state for a single call to GetDirectoryEntry or |
| // GetDirectoryEntryWithMetadata. Subsequent calls overwrite the values |
| // in these structs. |
| struct libsmb_file_info file_info_; |
| alignas(struct smbc_dirent) uint8_t dirent_buf_[kDirEntBufSize]; |
| struct smbc_dirent* dirent_ = |
| reinterpret_cast<struct smbc_dirent*>(dirent_buf_); |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeSambaInterface); |
| }; |
| |
| } // namespace smbprovider |
| |
| #endif // SMBPROVIDER_FAKE_SAMBA_INTERFACE_H_ |