| // 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. |
| |
| #include "smbprovider/smbprovider_helper.h" |
| |
| #include <errno.h> |
| #include <string.h> |
| |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/string_piece.h> |
| #include <libsmbclient.h> |
| |
| #include "smbprovider/constants.h" |
| #include "smbprovider/proto.h" |
| |
| namespace smbprovider { |
| |
| std::string AppendPath(const std::string& base_path, |
| const std::string& relative_path) { |
| const base::FilePath path(base_path); |
| base::FilePath relative(relative_path); |
| if (relative.IsAbsolute() && relative_path.size() > 0) { |
| // Remove the beginning "/" since FilePath#Append() cannot append an |
| // 'absolute' path. |
| relative = base::FilePath( |
| base::StringPiece(relative_path.c_str() + 1, relative_path.size() - 1)); |
| } |
| return path.Append(relative).value(); |
| } |
| |
| bool IsSelfOrParentDir(const std::string& entry_name) { |
| return entry_name == kEntrySelf || entry_name == kEntryParent; |
| } |
| |
| bool IsFileOrDir(uint32_t smbc_type) { |
| return smbc_type == SMBC_FILE || smbc_type == SMBC_DIR; |
| } |
| |
| bool IsSmbShare(uint32_t smbc_type) { |
| return smbc_type == SMBC_FILE_SHARE; |
| } |
| |
| bool IsSymlink(uint16_t file_attrs) { |
| return file_attrs & kFileAttributeReparsePoint; |
| } |
| |
| ErrorType GetErrorFromErrno(int32_t error_code) { |
| DCHECK_GT(error_code, 0); |
| switch (error_code) { |
| case EPERM: |
| case EACCES: |
| return ERROR_ACCESS_DENIED; |
| case ENOENT: |
| return ERROR_NOT_FOUND; |
| case EMFILE: |
| case ENFILE: |
| return ERROR_TOO_MANY_OPENED; |
| case ENOTDIR: |
| return ERROR_NOT_A_DIRECTORY; |
| case EISDIR: |
| return ERROR_NOT_A_FILE; |
| case ENOTEMPTY: |
| return ERROR_NOT_EMPTY; |
| case EEXIST: |
| return ERROR_EXISTS; |
| case EINVAL: |
| return ERROR_INVALID_OPERATION; |
| case ECONNABORTED: |
| return ERROR_SMB1_UNSUPPORTED; |
| case EBADF: |
| case ENODEV: |
| case ETIMEDOUT: |
| return ERROR_OPERATION_FAILED; |
| default: |
| LOG(WARNING) << "Unexpected error code " << error_code << ": " |
| << strerror(error_code); |
| return ERROR_FAILED; |
| } |
| } |
| |
| // EINVAL is returned when Samba is unable to parse a hostname |
| // (eg. \\qnap\testshare). This problem is rooted in Samba, a proper fix would |
| // be to patch the Samba library. |
| ErrorType GetErrorFromErrnoForReadDir(int32_t error_code) { |
| if (error_code == EINVAL) { |
| return ERROR_NOT_FOUND; |
| } |
| |
| return GetErrorFromErrno(error_code); |
| } |
| |
| bool IsDirectory(const struct stat& stat_info) { |
| return S_ISDIR(stat_info.st_mode); |
| } |
| |
| bool IsFile(const struct stat& stat_info) { |
| return S_ISREG(stat_info.st_mode); |
| } |
| |
| void LogAndSetError(const char* operation_name, |
| int32_t mount_id, |
| ErrorType error_received, |
| int32_t* error_code) { |
| LogOperationError(operation_name, mount_id, error_received); |
| *error_code = static_cast<int32_t>(error_received); |
| } |
| |
| void LogOperationError(const char* operation_name, |
| int32_t mount_id, |
| ErrorType error_received) { |
| LOG(ERROR) << "Cannot " << operation_name << " on Mount [" << mount_id |
| << "]: " << error_received; |
| } |
| |
| void LogAndSetDBusParseError(const char* operation_name, int32_t* error_code) { |
| LogAndSetError(operation_name, -1, ERROR_DBUS_PARSE_FAILED, error_code); |
| } |
| |
| int32_t GetDirectoryEntryProtoFromStat(const std::string& full_path, |
| const struct stat& stat_info, |
| ProtoBlob* proto_blob) { |
| DCHECK(proto_blob); |
| bool is_directory = IsDirectory(stat_info); |
| int64_t size = is_directory ? 0 : stat_info.st_size; |
| const base::FilePath path(full_path); |
| |
| DirectoryEntryProto entry; |
| entry.set_is_directory(is_directory); |
| entry.set_name(path.BaseName().value()); |
| entry.set_size(size); |
| entry.set_last_modified_time(stat_info.st_mtime); |
| return static_cast<int32_t>(SerializeProtoToBlob(entry, proto_blob)); |
| } |
| |
| bool IsValidOpenFileFlags(int32_t flags) { |
| return flags == O_RDONLY || flags == O_RDWR || flags == O_WRONLY; |
| } |
| |
| bool ReadFromFD(const WriteFileOptionsProto& options, |
| const base::ScopedFD& fd, |
| int32_t* error, |
| std::vector<uint8_t>* buffer) { |
| DCHECK(buffer); |
| DCHECK(error); |
| |
| if (!fd.is_valid()) { |
| LogAndSetError(options, ERROR_DBUS_PARSE_FAILED, error); |
| return false; |
| } |
| |
| buffer->resize(options.length()); |
| if (!base::ReadFromFD(fd.get(), reinterpret_cast<char*>(buffer->data()), |
| buffer->size())) { |
| LogAndSetError(options, ERROR_IO, error); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int32_t GetOpenFilePermissions(const bool writeable) { |
| return writeable ? O_RDWR : O_RDONLY; |
| } |
| |
| int32_t GetOpenFilePermissions(const OpenFileOptionsProto& options) { |
| return GetOpenFilePermissions(options.writeable()); |
| } |
| |
| int32_t GetOpenFilePermissions(const TruncateOptionsProto& unused) { |
| return O_WRONLY; |
| } |
| |
| int32_t GetOpenFilePermissions(const CopyEntryOptionsProto& unused) { |
| // OpenFile is Read-Only for CopyEntry since we only need to read the source. |
| return O_RDONLY; |
| } |
| |
| PathParts SplitPath(const std::string& full_path) { |
| DCHECK(!full_path.empty()); |
| base::FilePath path(full_path); |
| std::vector<std::string> result; |
| path.GetComponents(&result); |
| return result; |
| } |
| |
| std::string RemoveURLScheme(const std::string& smb_url) { |
| DCHECK_EQ(0, smb_url.compare(0, 6, kSmbUrlScheme)); |
| return smb_url.substr(5, std::string::npos); |
| } |
| |
| std::string GetFileName(const std::string& full_path) { |
| base::FilePath file_path(RemoveURLScheme(full_path)); |
| return file_path.BaseName().value(); |
| } |
| |
| std::string GetDirPath(const std::string& full_path) { |
| std::string path = RemoveURLScheme(full_path); |
| return base::FilePath(path).DirName().value(); |
| } |
| |
| bool ShouldReportCreateDirError(int32_t result, bool ignore_existing) { |
| if (result == 0) { |
| return false; |
| } |
| return !(result == EEXIST && ignore_existing); |
| } |
| |
| std::ostream& operator<<(std::ostream& out, const ErrorType error) { |
| switch (error) { |
| case ERROR_NONE: |
| return out << "ERROR_NONE"; |
| case ERROR_OK: |
| return out << "ERROR_OK"; |
| case ERROR_FAILED: |
| return out << "ERROR_FAILED"; |
| case ERROR_IN_USE: |
| return out << "ERROR_IN_USE"; |
| case ERROR_EXISTS: |
| return out << "ERROR_EXISTS"; |
| case ERROR_NOT_FOUND: |
| return out << "ERROR_NOT_FOUND"; |
| case ERROR_ACCESS_DENIED: |
| return out << "ERROR_ACCESS_DENIED"; |
| case ERROR_TOO_MANY_OPENED: |
| return out << "ERROR_TOO_MANY_OPENED"; |
| case ERROR_NO_MEMORY: |
| return out << "ERROR_NO_MEMORY"; |
| case ERROR_NO_SPACE: |
| return out << "ERROR_NO_SPACE"; |
| case ERROR_NOT_A_DIRECTORY: |
| return out << "ERROR_NOT_A_DIRECTORY"; |
| case ERROR_INVALID_OPERATION: |
| return out << "ERROR_INVALID_OPERATION"; |
| case ERROR_SECURITY: |
| return out << "ERROR_SECURITY"; |
| case ERROR_ABORT: |
| return out << "ERROR_ABORT"; |
| case ERROR_NOT_A_FILE: |
| return out << "ERROR_NOT_A_FILE"; |
| case ERROR_NOT_EMPTY: |
| return out << "ERROR_NOT_EMPTY"; |
| case ERROR_INVALID_URL: |
| return out << "ERROR_INVALID_URL"; |
| case ERROR_IO: |
| return out << "ERROR_IO"; |
| case ERROR_PROVIDER_ERROR_COUNT: |
| return out << "ERROR_PROVIDER_ERROR_COUNT"; |
| case ERROR_DBUS_PARSE_FAILED: |
| return out << "ERROR_DBUS_PARSE_FAILED"; |
| case ERROR_COPY_PENDING: |
| return out << "ERROR_COPY_PENDING"; |
| case ERROR_COPY_FAILED: |
| return out << "ERROR_COPY_FAILED"; |
| case ERROR_SMB1_UNSUPPORTED: |
| return out << "ERROR_SMB1_UNSUPPORTED"; |
| case ERROR_OPERATION_PENDING: |
| return out << "ERROR_OPERATION_PENDING"; |
| case ERROR_OPERATION_FAILED: |
| return out << "ERROR_OPERATION_FAILED"; |
| default: |
| return out << "ERROR_" |
| << static_cast<std::underlying_type_t<ErrorType>>(error); |
| } |
| } |
| |
| } // namespace smbprovider |