From c04dcc2e7d834218ef2d4194331e383402495ae1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:22 +0200 Subject: Adding upstream version 2:20.4+dfsg. Signed-off-by: Daniel Baumann --- xbmc/platform/posix/CMakeLists.txt | 21 + xbmc/platform/posix/CPUInfoPosix.cpp | 60 ++ xbmc/platform/posix/CPUInfoPosix.h | 23 + xbmc/platform/posix/ConvUtils.cpp | 23 + xbmc/platform/posix/ConvUtils.h | 15 + xbmc/platform/posix/Filesystem.cpp | 128 ++++ xbmc/platform/posix/MessagePrinter.cpp | 40 ++ xbmc/platform/posix/PlatformDefs.h | 153 +++++ xbmc/platform/posix/PlatformPosix.cpp | 63 ++ xbmc/platform/posix/PlatformPosix.h | 25 + xbmc/platform/posix/PosixMountProvider.cpp | 144 ++++ xbmc/platform/posix/PosixMountProvider.h | 37 ++ xbmc/platform/posix/PosixResourceCounter.cpp | 71 ++ xbmc/platform/posix/PosixResourceCounter.h | 31 + xbmc/platform/posix/PosixTimezone.cpp | 272 ++++++++ xbmc/platform/posix/PosixTimezone.h | 58 ++ xbmc/platform/posix/XHandle.cpp | 138 ++++ xbmc/platform/posix/XHandle.h | 59 ++ xbmc/platform/posix/XHandlePublic.h | 19 + xbmc/platform/posix/XTimeUtils.cpp | 228 +++++++ xbmc/platform/posix/filesystem/CMakeLists.txt | 18 + xbmc/platform/posix/filesystem/PosixDirectory.cpp | 207 ++++++ xbmc/platform/posix/filesystem/PosixDirectory.h | 29 + xbmc/platform/posix/filesystem/PosixFile.cpp | 388 +++++++++++ xbmc/platform/posix/filesystem/PosixFile.h | 47 ++ xbmc/platform/posix/filesystem/SMBDirectory.cpp | 372 +++++++++++ xbmc/platform/posix/filesystem/SMBDirectory.h | 33 + xbmc/platform/posix/filesystem/SMBFile.cpp | 721 +++++++++++++++++++++ xbmc/platform/posix/filesystem/SMBFile.h | 93 +++ xbmc/platform/posix/filesystem/SMBWSDiscovery.cpp | 126 ++++ xbmc/platform/posix/filesystem/SMBWSDiscovery.h | 107 +++ .../posix/filesystem/SMBWSDiscoveryListener.cpp | 608 +++++++++++++++++ .../posix/filesystem/SMBWSDiscoveryListener.h | 128 ++++ xbmc/platform/posix/main.cpp | 75 +++ xbmc/platform/posix/network/CMakeLists.txt | 5 + xbmc/platform/posix/network/NetworkPosix.cpp | 150 +++++ xbmc/platform/posix/network/NetworkPosix.h | 61 ++ xbmc/platform/posix/storage/discs/CMakeLists.txt | 7 + .../posix/storage/discs/DiscDriveHandlerPosix.cpp | 152 +++++ .../posix/storage/discs/DiscDriveHandlerPosix.h | 58 ++ xbmc/platform/posix/threads/CMakeLists.txt | 9 + xbmc/platform/posix/threads/RecursiveMutex.cpp | 43 ++ xbmc/platform/posix/threads/RecursiveMutex.h | 48 ++ xbmc/platform/posix/threads/ThreadImplPosix.cpp | 35 + xbmc/platform/posix/threads/ThreadImplPosix.h | 23 + xbmc/platform/posix/utils/CMakeLists.txt | 10 + xbmc/platform/posix/utils/FileHandle.h | 31 + xbmc/platform/posix/utils/Mmap.cpp | 27 + xbmc/platform/posix/utils/Mmap.h | 53 ++ .../platform/posix/utils/PosixInterfaceForCLog.cpp | 29 + xbmc/platform/posix/utils/PosixInterfaceForCLog.h | 22 + xbmc/platform/posix/utils/SharedMemory.cpp | 118 ++++ xbmc/platform/posix/utils/SharedMemory.h | 62 ++ 53 files changed, 5503 insertions(+) create mode 100644 xbmc/platform/posix/CMakeLists.txt create mode 100644 xbmc/platform/posix/CPUInfoPosix.cpp create mode 100644 xbmc/platform/posix/CPUInfoPosix.h create mode 100644 xbmc/platform/posix/ConvUtils.cpp create mode 100644 xbmc/platform/posix/ConvUtils.h create mode 100644 xbmc/platform/posix/Filesystem.cpp create mode 100644 xbmc/platform/posix/MessagePrinter.cpp create mode 100644 xbmc/platform/posix/PlatformDefs.h create mode 100644 xbmc/platform/posix/PlatformPosix.cpp create mode 100644 xbmc/platform/posix/PlatformPosix.h create mode 100644 xbmc/platform/posix/PosixMountProvider.cpp create mode 100644 xbmc/platform/posix/PosixMountProvider.h create mode 100644 xbmc/platform/posix/PosixResourceCounter.cpp create mode 100644 xbmc/platform/posix/PosixResourceCounter.h create mode 100644 xbmc/platform/posix/PosixTimezone.cpp create mode 100644 xbmc/platform/posix/PosixTimezone.h create mode 100644 xbmc/platform/posix/XHandle.cpp create mode 100644 xbmc/platform/posix/XHandle.h create mode 100644 xbmc/platform/posix/XHandlePublic.h create mode 100644 xbmc/platform/posix/XTimeUtils.cpp create mode 100644 xbmc/platform/posix/filesystem/CMakeLists.txt create mode 100644 xbmc/platform/posix/filesystem/PosixDirectory.cpp create mode 100644 xbmc/platform/posix/filesystem/PosixDirectory.h create mode 100644 xbmc/platform/posix/filesystem/PosixFile.cpp create mode 100644 xbmc/platform/posix/filesystem/PosixFile.h create mode 100644 xbmc/platform/posix/filesystem/SMBDirectory.cpp create mode 100644 xbmc/platform/posix/filesystem/SMBDirectory.h create mode 100644 xbmc/platform/posix/filesystem/SMBFile.cpp create mode 100644 xbmc/platform/posix/filesystem/SMBFile.h create mode 100644 xbmc/platform/posix/filesystem/SMBWSDiscovery.cpp create mode 100644 xbmc/platform/posix/filesystem/SMBWSDiscovery.h create mode 100644 xbmc/platform/posix/filesystem/SMBWSDiscoveryListener.cpp create mode 100644 xbmc/platform/posix/filesystem/SMBWSDiscoveryListener.h create mode 100644 xbmc/platform/posix/main.cpp create mode 100644 xbmc/platform/posix/network/CMakeLists.txt create mode 100644 xbmc/platform/posix/network/NetworkPosix.cpp create mode 100644 xbmc/platform/posix/network/NetworkPosix.h create mode 100644 xbmc/platform/posix/storage/discs/CMakeLists.txt create mode 100644 xbmc/platform/posix/storage/discs/DiscDriveHandlerPosix.cpp create mode 100644 xbmc/platform/posix/storage/discs/DiscDriveHandlerPosix.h create mode 100644 xbmc/platform/posix/threads/CMakeLists.txt create mode 100644 xbmc/platform/posix/threads/RecursiveMutex.cpp create mode 100644 xbmc/platform/posix/threads/RecursiveMutex.h create mode 100644 xbmc/platform/posix/threads/ThreadImplPosix.cpp create mode 100644 xbmc/platform/posix/threads/ThreadImplPosix.h create mode 100644 xbmc/platform/posix/utils/CMakeLists.txt create mode 100644 xbmc/platform/posix/utils/FileHandle.h create mode 100644 xbmc/platform/posix/utils/Mmap.cpp create mode 100644 xbmc/platform/posix/utils/Mmap.h create mode 100644 xbmc/platform/posix/utils/PosixInterfaceForCLog.cpp create mode 100644 xbmc/platform/posix/utils/PosixInterfaceForCLog.h create mode 100644 xbmc/platform/posix/utils/SharedMemory.cpp create mode 100644 xbmc/platform/posix/utils/SharedMemory.h (limited to 'xbmc/platform/posix') diff --git a/xbmc/platform/posix/CMakeLists.txt b/xbmc/platform/posix/CMakeLists.txt new file mode 100644 index 0000000..2d8d4db --- /dev/null +++ b/xbmc/platform/posix/CMakeLists.txt @@ -0,0 +1,21 @@ +set(SOURCES ConvUtils.cpp + CPUInfoPosix.cpp + Filesystem.cpp + MessagePrinter.cpp + PlatformPosix.cpp + PosixMountProvider.cpp + PosixResourceCounter.cpp + PosixTimezone.cpp + XHandle.cpp + XTimeUtils.cpp) + +set(HEADERS ConvUtils.h + CPUInfoPosix.h + PlatformDefs.h + PlatformPosix.h + PosixMountProvider.h + PosixResourceCounter.h + PosixTimezone.h + XHandle.h) + +core_add_library(platform_posix) diff --git a/xbmc/platform/posix/CPUInfoPosix.cpp b/xbmc/platform/posix/CPUInfoPosix.cpp new file mode 100644 index 0000000..9d158d0 --- /dev/null +++ b/xbmc/platform/posix/CPUInfoPosix.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "CPUInfoPosix.h" + +#include "ServiceBroker.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" + +bool CCPUInfoPosix::GetTemperature(CTemperature& temperature) +{ + return CheckUserTemperatureCommand(temperature); +} + +bool CCPUInfoPosix::CheckUserTemperatureCommand(CTemperature& temperature) +{ + temperature.SetValid(false); + + auto settingComponent = CServiceBroker::GetSettingsComponent(); + if (!settingComponent) + return false; + + auto advancedSettings = settingComponent->GetAdvancedSettings(); + if (!advancedSettings) + return false; + + std::string cmd = advancedSettings->m_cpuTempCmd; + + if (cmd.empty()) + return false; + + int value = {}; + char scale = {}; + + auto p = popen(cmd.c_str(), "r"); + if (p) + { + int ret = fscanf(p, "%d %c", &value, &scale); + pclose(p); + + if (ret < 2) + return false; + } + + if (scale == 'C' || scale == 'c') + temperature = CTemperature::CreateFromCelsius(value); + else if (scale == 'F' || scale == 'f') + temperature = CTemperature::CreateFromFahrenheit(value); + else + return false; + + temperature.SetValid(true); + + return true; +} diff --git a/xbmc/platform/posix/CPUInfoPosix.h b/xbmc/platform/posix/CPUInfoPosix.h new file mode 100644 index 0000000..af94b1b --- /dev/null +++ b/xbmc/platform/posix/CPUInfoPosix.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/CPUInfo.h" + +class CCPUInfoPosix : public CCPUInfo +{ +public: + virtual bool GetTemperature(CTemperature& temperature) override; + +protected: + CCPUInfoPosix() = default; + virtual ~CCPUInfoPosix() = default; + + bool CheckUserTemperatureCommand(CTemperature& temperature); +}; diff --git a/xbmc/platform/posix/ConvUtils.cpp b/xbmc/platform/posix/ConvUtils.cpp new file mode 100644 index 0000000..6ad3da2 --- /dev/null +++ b/xbmc/platform/posix/ConvUtils.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include +#include +#include + +#include "PlatformDefs.h" + +DWORD GetLastError() +{ + return errno; +} + +void SetLastError(DWORD dwErrCode) +{ + errno = dwErrCode; +} diff --git a/xbmc/platform/posix/ConvUtils.h b/xbmc/platform/posix/ConvUtils.h new file mode 100644 index 0000000..2e7c16a --- /dev/null +++ b/xbmc/platform/posix/ConvUtils.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "PlatformDefs.h" // DWORD ... + +DWORD GetLastError(); +void SetLastError(DWORD dwErrCode); + diff --git a/xbmc/platform/posix/Filesystem.cpp b/xbmc/platform/posix/Filesystem.cpp new file mode 100644 index 0000000..0db3af5 --- /dev/null +++ b/xbmc/platform/posix/Filesystem.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "platform/Filesystem.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/URIUtils.h" + +#if defined(TARGET_LINUX) +#include +#elif defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) +#include +#include +#elif defined(TARGET_ANDROID) +#include +#endif + +#include +#include +#include +#include + +#include + +namespace KODI +{ +namespace PLATFORM +{ +namespace FILESYSTEM +{ + +space_info space(const std::string& path, std::error_code& ec) +{ + ec.clear(); + space_info sp; +#if defined(TARGET_LINUX) + struct statvfs64 fsInfo; + auto result = statvfs64(CSpecialProtocol::TranslatePath(path).c_str(), &fsInfo); +#else + struct statfs fsInfo; + // is 64-bit on android and darwin (10.6SDK + any iOS) + auto result = statfs(CSpecialProtocol::TranslatePath(path).c_str(), &fsInfo); +#endif + + if (result != 0) + { + ec.assign(result, std::system_category()); + sp.available = static_cast(-1); + sp.capacity = static_cast(-1); + sp.free = static_cast(-1); + return sp; + } + sp.available = static_cast(fsInfo.f_bavail * fsInfo.f_bsize); + sp.capacity = static_cast(fsInfo.f_blocks * fsInfo.f_bsize); + sp.free = static_cast(fsInfo.f_bfree * fsInfo.f_bsize); + + return sp; +} + +std::string temp_directory_path(std::error_code &ec) +{ + ec.clear(); + + auto result = getenv("TMPDIR"); + if (result) + return URIUtils::AppendSlash(result); + + return "/tmp/"; +} + +std::string create_temp_directory(std::error_code &ec) +{ + char buf[PATH_MAX]; + + auto path = temp_directory_path(ec); + + strncpy(buf, (path + "xbmctempXXXXXX").c_str(), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + auto tmp = mkdtemp(buf); + if (!tmp) + { + ec.assign(errno, std::system_category()); + return std::string(); + } + + ec.clear(); + return std::string(tmp); +} + +std::string temp_file_path(const std::string& suffix, std::error_code& ec) +{ + char tmp[PATH_MAX]; + + auto tempPath = create_temp_directory(ec); + if (ec) + return std::string(); + + tempPath = URIUtils::AddFileToFolder(tempPath, "xbmctempfileXXXXXX" + suffix); + if (tempPath.length() >= PATH_MAX) + { + ec.assign(EOVERFLOW, std::system_category()); + return std::string(); + } + + strncpy(tmp, tempPath.c_str(), sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + + auto fd = mkstemps(tmp, suffix.length()); + if (fd < 0) + { + ec.assign(errno, std::system_category()); + return std::string(); + } + + close(fd); + + ec.clear(); + return std::string(tmp); +} + +} +} +} diff --git a/xbmc/platform/posix/MessagePrinter.cpp b/xbmc/platform/posix/MessagePrinter.cpp new file mode 100644 index 0000000..6621740 --- /dev/null +++ b/xbmc/platform/posix/MessagePrinter.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "platform/MessagePrinter.h" + +#include "CompileInfo.h" + +#include + +void CMessagePrinter::DisplayMessage(const std::string& message) +{ + fprintf(stdout, "%s\n", message.c_str()); +} + +void CMessagePrinter::DisplayWarning(const std::string& warning) +{ + fprintf(stderr, "%s\n", warning.c_str()); +} + +void CMessagePrinter::DisplayError(const std::string& error) +{ + fprintf(stderr,"%s\n", error.c_str()); +} + +void CMessagePrinter::DisplayHelpMessage(const std::vector>& help) +{ + //very crude implementation, pretty it up when possible + std::string message; + for (const auto& line : help) + { + message.append(line.first + "\t" + line.second + "\n"); + } + + fprintf(stdout, "%s\n", message.c_str()); +} diff --git a/xbmc/platform/posix/PlatformDefs.h b/xbmc/platform/posix/PlatformDefs.h new file mode 100644 index 0000000..e6e59fe --- /dev/null +++ b/xbmc/platform/posix/PlatformDefs.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#if defined(TARGET_DARWIN) +#include +#include +#include +#ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS +#endif +#include +#include +#if defined(TARGET_DARWIN_OSX) +#include +#endif + +#elif defined(TARGET_FREEBSD) +#include +#include +#else +#include +#endif + +#include +#include + +#if defined(__ppc__) || defined(__powerpc__) +#define PIXEL_ASHIFT 0 +#define PIXEL_RSHIFT 8 +#define PIXEL_GSHIFT 16 +#define PIXEL_BSHIFT 24 +#else +#define PIXEL_ASHIFT 24 +#define PIXEL_RSHIFT 16 +#define PIXEL_GSHIFT 8 +#define PIXEL_BSHIFT 0 +#endif + +#include + +#define _fdopen fdopen +#define _vsnprintf vsnprintf + +#define __stdcall +#define __cdecl +#define WINAPI __stdcall +#undef APIENTRY +struct CXHandle; // forward declaration +typedef CXHandle* HANDLE; + +typedef void* HINSTANCE; +typedef void* HMODULE; + +typedef unsigned int DWORD; +#define INVALID_HANDLE_VALUE ((HANDLE)~0U) + +#define MAXWORD 0xffff + +typedef union _LARGE_INTEGER +{ + struct { + DWORD LowPart; + int32_t HighPart; + } u; + long long QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; + + typedef union _ULARGE_INTEGER { + struct { + DWORD LowPart; + DWORD HighPart; + } u; + unsigned long long QuadPart; +} ULARGE_INTEGER; + +// Network +#define SOCKET_ERROR (-1) +#define INVALID_SOCKET (~0) +#define closesocket(s) close(s) +#define ioctlsocket(s, f, v) ioctl(s, f, v) +#define WSAGetLastError() (errno) +#define WSAECONNRESET ECONNRESET + +typedef int SOCKET; + +// Thread +typedef int (*LPTHREAD_START_ROUTINE)(void *); + +// File +#define O_BINARY 0 +#define _O_TRUNC O_TRUNC +#define _O_RDONLY O_RDONLY +#define _O_WRONLY O_WRONLY + +#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) + #define stat64 stat + #define __stat64 stat + #define fstat64 fstat + typedef int64_t off64_t; + #if defined(TARGET_FREEBSD) + #define statfs64 statfs + #endif +#else + #define __stat64 stat64 +#endif + +struct _stati64 { + dev_t st_dev; + ino_t st_ino; + unsigned short st_mode; + short st_nlink; + short st_uid; + short st_gid; + dev_t st_rdev; + long long st_size; + time_t _st_atime; + time_t _st_mtime; + time_t _st_ctime; +}; + +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define FILE_END 2 + +#define _S_IFREG S_IFREG +#define _S_IFDIR S_IFDIR +#define MAX_PATH PATH_MAX + +// CreateFile defines +#define FILE_FLAG_NO_BUFFERING 0x20000000 +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000 + +#define CREATE_NEW 1 +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 +#define OPEN_ALWAYS 4 +#define TRUNCATE_EXISTING 5 + +#define FILE_READ_DATA ( 0x0001 ) +#define FILE_WRITE_DATA ( 0x0002 ) diff --git a/xbmc/platform/posix/PlatformPosix.cpp b/xbmc/platform/posix/PlatformPosix.cpp new file mode 100644 index 0000000..b1caa26 --- /dev/null +++ b/xbmc/platform/posix/PlatformPosix.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PlatformPosix.h" + +#include "filesystem/SpecialProtocol.h" + +#include +#include + +#ifdef HAS_DBUS +#include +#endif + +std::atomic_flag CPlatformPosix::ms_signalFlag; + +bool CPlatformPosix::InitStageOne() +{ + + if (!CPlatform::InitStageOne()) + return false; + + // Initialize to "set" state + ms_signalFlag.test_and_set(); + + // Initialize timezone information variables + tzset(); + + // set special://envhome + if (getenv("HOME")) + { + CSpecialProtocol::SetEnvHomePath(getenv("HOME")); + } + else + { + fprintf(stderr, "The HOME environment variable is not set!\n"); + return false; + } + +#ifdef HAS_DBUS + // call 'dbus_threads_init_default' before any other dbus calls in order to + // avoid race conditions with other threads using dbus connections + dbus_threads_init_default(); +#endif + + return true; +} + +bool CPlatformPosix::TestQuitFlag() +{ + // Keep set, return true when it was cleared before + return !ms_signalFlag.test_and_set(); +} + +void CPlatformPosix::RequestQuit() +{ + ms_signalFlag.clear(); +} diff --git a/xbmc/platform/posix/PlatformPosix.h b/xbmc/platform/posix/PlatformPosix.h new file mode 100644 index 0000000..08513b9 --- /dev/null +++ b/xbmc/platform/posix/PlatformPosix.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "platform/Platform.h" + +#include + +class CPlatformPosix : public CPlatform +{ +public: + bool InitStageOne() override; + + static bool TestQuitFlag(); + static void RequestQuit(); + +private: + static std::atomic_flag ms_signalFlag; +}; diff --git a/xbmc/platform/posix/PosixMountProvider.cpp b/xbmc/platform/posix/PosixMountProvider.cpp new file mode 100644 index 0000000..d139be1 --- /dev/null +++ b/xbmc/platform/posix/PosixMountProvider.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PosixMountProvider.h" + +#include "utils/RegExp.h" +#include "utils/URIUtils.h" +#include "utils/log.h" + +#include + +CPosixMountProvider::CPosixMountProvider() +{ + m_removableLength = 0; + PumpDriveChangeEvents(NULL); +} + +void CPosixMountProvider::Initialize() +{ + CLog::Log(LOGDEBUG, "Selected Posix mount as storage provider"); +} + +void CPosixMountProvider::GetDrives(VECSOURCES &drives) +{ + std::vector result; + + CRegExp reMount; +#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) + reMount.RegComp("on (.+) \\(([^,]+)"); +#else + reMount.RegComp("on (.+) type ([^ ]+)"); +#endif + char line[1024]; + + FILE* pipe = popen("mount", "r"); + + if (pipe) + { + while (fgets(line, sizeof(line) - 1, pipe)) + { + if (reMount.RegFind(line) != -1) + { + bool accepted = false; + std::string mountStr = reMount.GetReplaceString("\\1"); + std::string fsStr = reMount.GetReplaceString("\\2"); + const char* mount = mountStr.c_str(); + const char* fs = fsStr.c_str(); + + // Here we choose which filesystems are approved + if (strcmp(fs, "fuseblk") == 0 || strcmp(fs, "vfat") == 0 + || strcmp(fs, "ext2") == 0 || strcmp(fs, "ext3") == 0 + || strcmp(fs, "reiserfs") == 0 || strcmp(fs, "xfs") == 0 + || strcmp(fs, "ntfs-3g") == 0 || strcmp(fs, "iso9660") == 0 + || strcmp(fs, "exfat") == 0 + || strcmp(fs, "fusefs") == 0 || strcmp(fs, "hfs") == 0) + accepted = true; + + // Ignore root + if (strcmp(mount, "/") == 0) + accepted = false; + + if(accepted) + result.emplace_back(mount); + } + } + pclose(pipe); + } + + for (unsigned int i = 0; i < result.size(); i++) + { + CMediaSource share; + share.strPath = result[i]; + share.strName = URIUtils::GetFileName(result[i]); + share.m_ignore = true; + drives.push_back(share); + } +} + +std::vector CPosixMountProvider::GetDiskUsage() +{ + std::vector result; + char line[1024]; + +#if defined(TARGET_DARWIN) + FILE* pipe = popen("df -hT ufs,cd9660,hfs,udf", "r"); +#elif defined(TARGET_FREEBSD) + FILE* pipe = popen("df -h -t ufs,cd9660,hfs,udf,zfs", "r"); +#else + FILE* pipe = popen("df -h", "r"); +#endif + + static const char* excludes[] = {"rootfs","devtmpfs","tmpfs","none","/dev/loop", "udev", NULL}; + + if (pipe) + { + while (fgets(line, sizeof(line) - 1, pipe)) + { + bool ok=true; + for (int i=0;excludes[i];++i) + { + if (strstr(line,excludes[i])) + { + ok=false; + break; + } + } + if (ok) + result.emplace_back(line); + } + pclose(pipe); + } + + return result; +} + +bool CPosixMountProvider::Eject(const std::string& mountpath) +{ + +#if !defined(TARGET_DARWIN_EMBEDDED) + // just go ahead and try to umount the disk + // if it does umount, life is good, if not, no loss. + std::string cmd = "umount \"" + mountpath + "\""; + int status = system(cmd.c_str()); + + if (status == 0) + return true; +#endif + + return false; +} + +bool CPosixMountProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback) +{ + VECSOURCES drives; + GetRemovableDrives(drives); + bool changed = drives.size() != m_removableLength; + m_removableLength = drives.size(); + return changed; +} diff --git a/xbmc/platform/posix/PosixMountProvider.h b/xbmc/platform/posix/PosixMountProvider.h new file mode 100644 index 0000000..170c644 --- /dev/null +++ b/xbmc/platform/posix/PosixMountProvider.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "storage/IStorageProvider.h" + +#include +#include + +class CPosixMountProvider : public IStorageProvider +{ +public: + CPosixMountProvider(); + ~CPosixMountProvider() override = default; + + void Initialize() override; + void Stop() override { } + + void GetLocalDrives(VECSOURCES &localDrives) override { GetDrives(localDrives); } + void GetRemovableDrives(VECSOURCES &removableDrives) override { /*GetDrives(removableDrives);*/ } + + std::vector GetDiskUsage() override; + + bool Eject(const std::string& mountpath) override; + + bool PumpDriveChangeEvents(IStorageEventsCallback *callback) override; +private: + void GetDrives(VECSOURCES &drives); + + unsigned int m_removableLength; +}; diff --git a/xbmc/platform/posix/PosixResourceCounter.cpp b/xbmc/platform/posix/PosixResourceCounter.cpp new file mode 100644 index 0000000..d82ef72 --- /dev/null +++ b/xbmc/platform/posix/PosixResourceCounter.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PosixResourceCounter.h" + +#include "utils/log.h" + +#include + +#include "PlatformDefs.h" + +CPosixResourceCounter::CPosixResourceCounter() +{ + Reset(); +} + +CPosixResourceCounter::~CPosixResourceCounter() = default; + +double CPosixResourceCounter::GetCPUUsage() +{ + struct timeval tmNow; + if (gettimeofday(&tmNow, NULL) == -1) + CLog::Log(LOGERROR, "error {} in gettimeofday", errno); + else + { + double dElapsed = ( ((double)tmNow.tv_sec + (double)tmNow.tv_usec / 1000000.0) - + ((double)m_tmLastCheck.tv_sec + (double)m_tmLastCheck.tv_usec / 1000000.0) ); + + if (dElapsed >= 3.0) + { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) == -1) + CLog::Log(LOGERROR, "error {} in getrusage", errno); + else + { + double dUser = ( ((double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1000000.0) - + ((double)m_usage.ru_utime.tv_sec + (double)m_usage.ru_utime.tv_usec / 1000000.0) ); + double dSys = ( ((double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1000000.0) - + ((double)m_usage.ru_stime.tv_sec + (double)m_usage.ru_stime.tv_usec / 1000000.0) ); + + m_tmLastCheck = tmNow; + m_usage = usage; + m_dLastUsage = ((dUser+dSys) / dElapsed) * 100.0; + return m_dLastUsage; + } + } + } + + return m_dLastUsage; +} + +void CPosixResourceCounter::Reset() +{ + if (gettimeofday(&m_tmLastCheck, NULL) == -1) + CLog::Log(LOGERROR, "error {} in gettimeofday", errno); + + if (getrusage(RUSAGE_SELF, &m_usage) == -1) + CLog::Log(LOGERROR, "error {} in getrusage", errno); + + m_dLastUsage = 0.0; +} + + + + + diff --git a/xbmc/platform/posix/PosixResourceCounter.h b/xbmc/platform/posix/PosixResourceCounter.h new file mode 100644 index 0000000..ef61b15 --- /dev/null +++ b/xbmc/platform/posix/PosixResourceCounter.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +class CPosixResourceCounter +{ +public: + CPosixResourceCounter(); + virtual ~CPosixResourceCounter(); + + double GetCPUUsage(); + void Reset(); + +protected: + struct rusage m_usage; + struct timeval m_tmLastCheck; + double m_dLastUsage; +}; + diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp new file mode 100644 index 0000000..6f276a3 --- /dev/null +++ b/xbmc/platform/posix/PosixTimezone.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include +#ifdef TARGET_ANDROID +#include "platform/android/bionic_supplement/bionic_supplement.h" +#endif +#include "PlatformDefs.h" +#include "PosixTimezone.h" +#include "utils/SystemInfo.h" + +#include "ServiceBroker.h" +#include "utils/StringUtils.h" +#include "XBDateTime.h" +#include "settings/lib/Setting.h" +#include "settings/lib/SettingDefinitions.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include + +#include + +CPosixTimezone::CPosixTimezone() +{ + char* line = NULL; + size_t linelen = 0; + int nameonfourthfield = 0; + std::string s; + std::vector tokens; + + // Load timezones + FILE* fp = fopen("/usr/share/zoneinfo/zone.tab", "r"); + if (fp) + { + std::string countryCode; + std::string timezoneName; + + while (getdelim(&line, &linelen, '\n', fp) > 0) + { + tokens.clear(); + s = line; + StringUtils::Trim(s); + + if (s.length() == 0) + continue; + + if (s[0] == '#') + continue; + + StringUtils::Tokenize(s, tokens, " \t"); + if (tokens.size() < 3) + continue; + + countryCode = tokens[0]; + timezoneName = tokens[2]; + + if (m_timezonesByCountryCode.count(countryCode) == 0) + { + std::vector timezones; + timezones.push_back(timezoneName); + m_timezonesByCountryCode[countryCode] = timezones; + } + else + { + std::vector& timezones = m_timezonesByCountryCode[countryCode]; + timezones.push_back(timezoneName); + } + + m_countriesByTimezoneName[timezoneName] = countryCode; + } + fclose(fp); + } + + if (line) + { + free(line); + line = NULL; + linelen = 0; + } + + // Load countries + fp = fopen("/usr/share/zoneinfo/iso3166.tab", "r"); + if (!fp) + { + fp = fopen("/usr/share/misc/iso3166", "r"); + nameonfourthfield = 1; + } + if (fp) + { + std::string countryCode; + std::string countryName; + + while (getdelim(&line, &linelen, '\n', fp) > 0) + { + s = line; + StringUtils::Trim(s); + + //! @todo STRING_CLEANUP + if (s.length() == 0) + continue; + + if (s[0] == '#') + continue; + + // Search for the first non space from the 2nd character and on + int i = 2; + while (s[i] == ' ' || s[i] == '\t') i++; + + if (nameonfourthfield) + { + // skip three letter + while (s[i] != ' ' && s[i] != '\t') i++; + while (s[i] == ' ' || s[i] == '\t') i++; + // skip number + while (s[i] != ' ' && s[i] != '\t') i++; + while (s[i] == ' ' || s[i] == '\t') i++; + } + + countryCode = s.substr(0, 2); + countryName = s.substr(i); + + m_counties.push_back(countryName); + m_countryByCode[countryCode] = countryName; + m_countryByName[countryName] = countryCode; + } + sort(m_counties.begin(), m_counties.end(), sortstringbyname()); + fclose(fp); + } + free(line); +} + +void CPosixTimezone::OnSettingChanged(const std::shared_ptr& setting) +{ + if (setting == NULL) + return; + + const std::string &settingId = setting->GetId(); + if (settingId == CSettings::SETTING_LOCALE_TIMEZONE) + { + SetTimezone(std::static_pointer_cast(setting)->GetValue()); + + CDateTime::ResetTimezoneBias(); + } + else if (settingId == CSettings::SETTING_LOCALE_TIMEZONECOUNTRY) + { + // nothing to do here. Changing locale.timezonecountry will trigger an + // update of locale.timezone and automatically adjust its value + // and execute OnSettingChanged() for it as well (see above) + } +} + +void CPosixTimezone::OnSettingsLoaded() +{ + SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE)); + CDateTime::ResetTimezoneBias(); +} + +std::vector CPosixTimezone::GetCounties() +{ + return m_counties; +} + +std::vector CPosixTimezone::GetTimezonesByCountry(const std::string& country) +{ + return m_timezonesByCountryCode[m_countryByName[country]]; +} + +std::string CPosixTimezone::GetCountryByTimezone(const std::string& timezone) +{ +#if defined(TARGET_DARWIN) + return "?"; +#else + return m_countryByCode[m_countriesByTimezoneName[timezone]]; +#endif +} + +void CPosixTimezone::SetTimezone(const std::string& timezoneName) +{ +#if !defined(TARGET_DARWIN) + bool use_timezone = true; +#else + bool use_timezone = false; +#endif + + if (use_timezone) + { + static char env_var[255]; + sprintf(env_var, "TZ=:%s", timezoneName.c_str()); + putenv(env_var); + tzset(); + } +} + +std::string CPosixTimezone::GetOSConfiguredTimezone() +{ + char timezoneName[255]; + + // try Slackware approach first + ssize_t rlrc = readlink("/etc/localtime-copied-from" + , timezoneName, sizeof(timezoneName)-1); + + // RHEL and maybe other distros make /etc/localtime a symlink + if (rlrc == -1) + rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName)-1); + + if (rlrc != -1) + { + timezoneName[rlrc] = '\0'; + + char* p = strrchr(timezoneName,'/'); + if (p) + { // we want the previous '/' + char* q = p; + *q = 0; + p = strrchr(timezoneName,'/'); + *q = '/'; + if (p) + p++; + } + return p; + } + + // now try Debian approach + timezoneName[0] = 0; + FILE* fp = fopen("/etc/timezone", "r"); + if (fp) + { + if (fgets(timezoneName, sizeof(timezoneName), fp)) + timezoneName[strlen(timezoneName)-1] = '\0'; + fclose(fp); + } + + return timezoneName; +} + +void CPosixTimezone::SettingOptionsTimezoneCountriesFiller( + const std::shared_ptr& setting, + std::vector& list, + std::string& current, + void* data) +{ + std::vector countries = g_timezone.GetCounties(); + for (unsigned int i = 0; i < countries.size(); i++) + list.emplace_back(countries[i], countries[i]); +} + +void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr& setting, + std::vector& list, + std::string& current, + void* data) +{ + current = std::static_pointer_cast(setting)->GetValue(); + bool found = false; + std::vector timezones = g_timezone.GetTimezonesByCountry(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY)); + for (unsigned int i = 0; i < timezones.size(); i++) + { + if (!found && StringUtils::EqualsNoCase(timezones[i], current)) + found = true; + + list.emplace_back(timezones[i], timezones[i]); + } + + if (!found && !timezones.empty()) + current = timezones[0]; +} + +CPosixTimezone g_timezone; diff --git a/xbmc/platform/posix/PosixTimezone.h b/xbmc/platform/posix/PosixTimezone.h new file mode 100644 index 0000000..076e87f --- /dev/null +++ b/xbmc/platform/posix/PosixTimezone.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "settings/lib/ISettingCallback.h" +#include "settings/lib/ISettingsHandler.h" + +#include +#include +#include + +class CSetting; +struct StringSettingOption; + +class CPosixTimezone : public ISettingCallback, public ISettingsHandler +{ +public: + CPosixTimezone(); + + void OnSettingChanged(const std::shared_ptr& setting) override; + + void OnSettingsLoaded() override; + + std::string GetOSConfiguredTimezone(); + + std::vector GetCounties(); + std::vector GetTimezonesByCountry(const std::string& country); + std::string GetCountryByTimezone(const std::string& timezone); + + void SetTimezone(const std::string& timezone); + int m_IsDST = 0; + + static void SettingOptionsTimezoneCountriesFiller(const std::shared_ptr& setting, + std::vector& list, + std::string& current, + void* data); + static void SettingOptionsTimezonesFiller(const std::shared_ptr& setting, + std::vector& list, + std::string& current, + void* data); + +private: + std::vector m_counties; + std::map m_countryByCode; + std::map m_countryByName; + + std::map > m_timezonesByCountryCode; + std::map m_countriesByTimezoneName; +}; + +extern CPosixTimezone g_timezone; + diff --git a/xbmc/platform/posix/XHandle.cpp b/xbmc/platform/posix/XHandle.cpp new file mode 100644 index 0000000..be7a78b --- /dev/null +++ b/xbmc/platform/posix/XHandle.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "XHandle.h" + +#include "utils/log.h" + +#include +#include + +int CXHandle::m_objectTracker[10] = {}; + +HANDLE WINAPI GetCurrentProcess(void) { + return (HANDLE)-1; // -1 a special value - pseudo handle +} + +CXHandle::CXHandle() +{ + Init(); + m_objectTracker[m_type]++; +} + +CXHandle::CXHandle(HandleType nType) +{ + Init(); + m_type=nType; + m_objectTracker[m_type]++; +} + +CXHandle::CXHandle(const CXHandle &src) +{ + // we shouldn't get here EVER. however, if we do - try to make the best. copy what we can + // and most importantly - not share any pointer. + CLog::Log(LOGWARNING, "{}, copy handle.", __FUNCTION__); + + Init(); + + if (src.m_hMutex) + m_hMutex = new CCriticalSection(); + + fd = src.fd; + m_bManualEvent = src.m_bManualEvent; + m_tmCreation = time(NULL); + m_FindFileResults = src.m_FindFileResults; + m_nFindFileIterator = src.m_nFindFileIterator; + m_FindFileDir = src.m_FindFileDir; + m_iOffset = src.m_iOffset; + m_bCDROM = src.m_bCDROM; + m_objectTracker[m_type]++; +} + +CXHandle::~CXHandle() +{ + + m_objectTracker[m_type]--; + + if (RecursionCount > 0) { + CLog::Log(LOGERROR, "{}, destroying handle with recursion count {}", __FUNCTION__, + RecursionCount); + assert(false); + } + + if (m_nRefCount > 1) { + CLog::Log(LOGERROR, "{}, destroying handle with ref count {}", __FUNCTION__, m_nRefCount); + assert(false); + } + + if (m_hMutex) { + delete m_hMutex; + } + + if (m_internalLock) { + delete m_internalLock; + } + + if (m_hCond) { + delete m_hCond; + } + + if ( fd != 0 ) { + close(fd); + } + +} + +void CXHandle::Init() +{ + fd=0; + m_hMutex=NULL; + m_hCond=NULL; + m_type = HND_NULL; + RecursionCount=0; + m_bManualEvent=false; + m_bEventSet=false; + m_nFindFileIterator=0 ; + m_nRefCount=1; + m_tmCreation = time(NULL); + m_internalLock = new CCriticalSection(); +} + +void CXHandle::ChangeType(HandleType newType) { + m_objectTracker[m_type]--; + m_type = newType; + m_objectTracker[m_type]++; +} + +void CXHandle::DumpObjectTracker() { + for (int i=0; i< 10; i++) { + CLog::Log(LOGDEBUG, "object {} --> {} instances", i, m_objectTracker[i]); + } +} + +bool CloseHandle(HANDLE hObject) { + if (!hObject) + return false; + + if (hObject == INVALID_HANDLE_VALUE || hObject == (HANDLE)-1) + return true; + + bool bDelete = false; + { + std::unique_lock lock((*hObject->m_internalLock)); + if (--hObject->m_nRefCount == 0) + bDelete = true; + } + + if (bDelete) + delete hObject; + + return true; +} + + diff --git a/xbmc/platform/posix/XHandle.h b/xbmc/platform/posix/XHandle.h new file mode 100644 index 0000000..158817e --- /dev/null +++ b/xbmc/platform/posix/XHandle.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "XHandlePublic.h" +#include "threads/Condition.h" +#include "threads/CriticalSection.h" + +#include +#include +#include + +#include "PlatformDefs.h" + +struct CXHandle { + +public: + typedef enum { HND_NULL = 0, HND_FILE, HND_EVENT, HND_MUTEX, HND_FIND_FILE } HandleType; + + CXHandle(); + explicit CXHandle(HandleType nType); + CXHandle(const CXHandle &src); + + virtual ~CXHandle(); + void Init(); + inline HandleType GetType() { return m_type; } + void ChangeType(HandleType newType); + + XbmcThreads::ConditionVariable *m_hCond; + std::list m_hParents; + + // simulate mutex and critical section + CCriticalSection *m_hMutex; + int RecursionCount; // for mutex - for compatibility with TARGET_WINDOWS critical section + int fd; + bool m_bManualEvent; + time_t m_tmCreation; + std::vector m_FindFileResults; + int m_nFindFileIterator; + std::string m_FindFileDir; + off64_t m_iOffset; + bool m_bCDROM; + bool m_bEventSet; + int m_nRefCount; + CCriticalSection *m_internalLock; + + static void DumpObjectTracker(); + +protected: + HandleType m_type; + static int m_objectTracker[10]; + +}; diff --git a/xbmc/platform/posix/XHandlePublic.h b/xbmc/platform/posix/XHandlePublic.h new file mode 100644 index 0000000..6d1f840 --- /dev/null +++ b/xbmc/platform/posix/XHandlePublic.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +struct CXHandle; +typedef CXHandle* HANDLE; +typedef HANDLE* LPHANDLE; + +bool CloseHandle(HANDLE hObject); + +#define DUPLICATE_CLOSE_SOURCE 0x00000001 +#define DUPLICATE_SAME_ACCESS 0x00000002 + diff --git a/xbmc/platform/posix/XTimeUtils.cpp b/xbmc/platform/posix/XTimeUtils.cpp new file mode 100644 index 0000000..e78e5cf --- /dev/null +++ b/xbmc/platform/posix/XTimeUtils.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "utils/XTimeUtils.h" + +#include "PosixTimezone.h" + +#include +#include +#include + +#include + +#if defined(TARGET_ANDROID) && !defined(__LP64__) +#include +#endif + +#define WIN32_TIME_OFFSET ((unsigned long long)(369 * 365 + 89) * 24 * 3600 * 10000000) + +namespace KODI +{ +namespace TIME +{ + +/* + * A Leap year is any year that is divisible by four, but not by 100 unless also + * divisible by 400 + */ +#define IsLeapYear(y) ((!(y % 4)) ? (((!(y % 400)) && (y % 100)) ? 1 : 0) : 0) + +uint32_t GetTimeZoneInformation(TimeZoneInformation* timeZoneInformation) +{ + if (!timeZoneInformation) + return KODI_TIME_ZONE_ID_INVALID; + + struct tm t; + time_t tt = time(NULL); + if (localtime_r(&tt, &t)) + timeZoneInformation->bias = -t.tm_gmtoff / 60; + + timeZoneInformation->standardName = tzname[0]; + timeZoneInformation->daylightName = tzname[1]; + + return KODI_TIME_ZONE_ID_UNKNOWN; +} + +void GetLocalTime(SystemTime* systemTime) +{ + const time_t t = time(NULL); + struct tm now; + + localtime_r(&t, &now); + systemTime->year = now.tm_year + 1900; + systemTime->month = now.tm_mon + 1; + systemTime->dayOfWeek = now.tm_wday; + systemTime->day = now.tm_mday; + systemTime->hour = now.tm_hour; + systemTime->minute = now.tm_min; + systemTime->second = now.tm_sec; + systemTime->milliseconds = 0; + // NOTE: localtime_r() is not required to set this, but we Assume that it's set here. + g_timezone.m_IsDST = now.tm_isdst; +} + +int FileTimeToLocalFileTime(const FileTime* fileTime, FileTime* localFileTime) +{ + ULARGE_INTEGER l; + l.u.LowPart = fileTime->lowDateTime; + l.u.HighPart = fileTime->highDateTime; + + time_t ft; + struct tm tm_ft; + FileTimeToTimeT(fileTime, &ft); + localtime_r(&ft, &tm_ft); + + l.QuadPart += static_cast(tm_ft.tm_gmtoff) * 10000000; + + localFileTime->lowDateTime = l.u.LowPart; + localFileTime->highDateTime = l.u.HighPart; + return 1; +} + +int SystemTimeToFileTime(const SystemTime* systemTime, FileTime* fileTime) +{ + static const int dayoffset[12] = {0, 31, 59, 90, 120, 151, 182, 212, 243, 273, 304, 334}; +#if defined(TARGET_DARWIN) + static std::mutex timegm_lock; +#endif + + struct tm sysTime = {}; + sysTime.tm_year = systemTime->year - 1900; + sysTime.tm_mon = systemTime->month - 1; + sysTime.tm_wday = systemTime->dayOfWeek; + sysTime.tm_mday = systemTime->day; + sysTime.tm_hour = systemTime->hour; + sysTime.tm_min = systemTime->minute; + sysTime.tm_sec = systemTime->second; + sysTime.tm_yday = dayoffset[sysTime.tm_mon] + (sysTime.tm_mday - 1); + sysTime.tm_isdst = g_timezone.m_IsDST; + + // If this is a leap year, and we're past the 28th of Feb, increment tm_yday. + if (IsLeapYear(systemTime->year) && (sysTime.tm_yday > 58)) + sysTime.tm_yday++; + +#if defined(TARGET_DARWIN) + std::lock_guard lock(timegm_lock); +#endif + +#if defined(TARGET_ANDROID) && !defined(__LP64__) + time64_t t = timegm64(&sysTime); +#else + time_t t = timegm(&sysTime); +#endif + + LARGE_INTEGER result; + result.QuadPart = (long long)t * 10000000 + (long long)systemTime->milliseconds * 10000; + result.QuadPart += WIN32_TIME_OFFSET; + + fileTime->lowDateTime = result.u.LowPart; + fileTime->highDateTime = result.u.HighPart; + + return 1; +} + +long CompareFileTime(const FileTime* fileTime1, const FileTime* fileTime2) +{ + ULARGE_INTEGER t1; + t1.u.LowPart = fileTime1->lowDateTime; + t1.u.HighPart = fileTime1->highDateTime; + + ULARGE_INTEGER t2; + t2.u.LowPart = fileTime2->lowDateTime; + t2.u.HighPart = fileTime2->highDateTime; + + if (t1.QuadPart == t2.QuadPart) + return 0; + else if (t1.QuadPart < t2.QuadPart) + return -1; + else + return 1; +} + +int FileTimeToSystemTime(const FileTime* fileTime, SystemTime* systemTime) +{ + LARGE_INTEGER file; + file.u.LowPart = fileTime->lowDateTime; + file.u.HighPart = fileTime->highDateTime; + + file.QuadPart -= WIN32_TIME_OFFSET; + file.QuadPart /= 10000; /* to milliseconds */ + systemTime->milliseconds = file.QuadPart % 1000; + file.QuadPart /= 1000; /* to seconds */ + + time_t ft = file.QuadPart; + + struct tm tm_ft; + gmtime_r(&ft, &tm_ft); + + systemTime->year = tm_ft.tm_year + 1900; + systemTime->month = tm_ft.tm_mon + 1; + systemTime->dayOfWeek = tm_ft.tm_wday; + systemTime->day = tm_ft.tm_mday; + systemTime->hour = tm_ft.tm_hour; + systemTime->minute = tm_ft.tm_min; + systemTime->second = tm_ft.tm_sec; + + return 1; +} + +int LocalFileTimeToFileTime(const FileTime* localFileTime, FileTime* fileTime) +{ + ULARGE_INTEGER l; + l.u.LowPart = localFileTime->lowDateTime; + l.u.HighPart = localFileTime->highDateTime; + + l.QuadPart += (unsigned long long) timezone * 10000000; + + fileTime->lowDateTime = l.u.LowPart; + fileTime->highDateTime = l.u.HighPart; + + return 1; +} + + +int FileTimeToTimeT(const FileTime* localFileTime, time_t* pTimeT) +{ + if (!localFileTime || !pTimeT) + return false; + + ULARGE_INTEGER fileTime; + fileTime.u.LowPart = localFileTime->lowDateTime; + fileTime.u.HighPart = localFileTime->highDateTime; + + fileTime.QuadPart -= WIN32_TIME_OFFSET; + fileTime.QuadPart /= 10000; /* to milliseconds */ + fileTime.QuadPart /= 1000; /* to seconds */ + + time_t ft = fileTime.QuadPart; + + struct tm tm_ft; + localtime_r(&ft, &tm_ft); + + *pTimeT = mktime(&tm_ft); + return 1; +} + +int TimeTToFileTime(time_t timeT, FileTime* localFileTime) +{ + if (!localFileTime) + return false; + + ULARGE_INTEGER result; + result.QuadPart = (unsigned long long) timeT * 10000000; + result.QuadPart += WIN32_TIME_OFFSET; + + localFileTime->lowDateTime = result.u.LowPart; + localFileTime->highDateTime = result.u.HighPart; + + return 1; +} + +} // namespace TIME +} // namespace KODI diff --git a/xbmc/platform/posix/filesystem/CMakeLists.txt b/xbmc/platform/posix/filesystem/CMakeLists.txt new file mode 100644 index 0000000..badb1a8 --- /dev/null +++ b/xbmc/platform/posix/filesystem/CMakeLists.txt @@ -0,0 +1,18 @@ +set(SOURCES PosixDirectory.cpp + PosixFile.cpp) + +set(HEADERS PosixDirectory.h + PosixFile.h) + +if(SMBCLIENT_FOUND) + list(APPEND SOURCES SMBDirectory.cpp + SMBFile.cpp + SMBWSDiscovery.cpp + SMBWSDiscoveryListener.cpp) + list(APPEND HEADERS SMBDirectory.h + SMBFile.h + SMBWSDiscovery.h + SMBWSDiscoveryListener.h) +endif() + +core_add_library(platform_posix_filesystem) diff --git a/xbmc/platform/posix/filesystem/PosixDirectory.cpp b/xbmc/platform/posix/filesystem/PosixDirectory.cpp new file mode 100644 index 0000000..6685c0e --- /dev/null +++ b/xbmc/platform/posix/filesystem/PosixDirectory.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PosixDirectory.h" + +#include "FileItem.h" +#include "URL.h" +#include "utils/AliasShortcutUtils.h" +#include "utils/CharsetConverter.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/XTimeUtils.h" + +#include +#include + +using namespace XFILE; + +CPosixDirectory::CPosixDirectory(void) = default; + +CPosixDirectory::~CPosixDirectory(void) = default; + +bool CPosixDirectory::GetDirectory(const CURL& url, CFileItemList &items) +{ + std::string root = url.Get(); + + if (IsAliasShortcut(root, true)) + TranslateAliasShortcut(root); + + DIR *dir = opendir(root.c_str()); + if (!dir) + return false; + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + std::string itemLabel(entry->d_name); + CCharsetConverter::unknownToUTF8(itemLabel); + CFileItemPtr pItem(new CFileItem(itemLabel)); + std::string itemPath(URIUtils::AddFileToFolder(root, entry->d_name)); + + bool bStat = false; + struct stat buffer; + + // Unix-based readdir implementations may return an incorrect dirent.d_ino value that + // is not equal to the (correct) stat() obtained one. In this case the file type + // could not be determined and the value of dirent.d_type is set to DT_UNKNOWN. + // In order to get a correct value we have to incur the cost of calling stat. + if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) + { + if (stat(itemPath.c_str(), &buffer) == 0) + bStat = true; + } + + if (entry->d_type == DT_DIR || (bStat && S_ISDIR(buffer.st_mode))) + { + pItem->m_bIsFolder = true; + URIUtils::AddSlashAtEnd(itemPath); + } + else + { + pItem->m_bIsFolder = false; + } + + if (StringUtils::StartsWith(entry->d_name, ".")) + pItem->SetProperty("file:hidden", true); + + pItem->SetPath(itemPath); + + if (!(m_flags & DIR_FLAG_NO_FILE_INFO)) + { + if (bStat || stat(pItem->GetPath().c_str(), &buffer) == 0) + { + KODI::TIME::FileTime fileTime, localTime; + KODI::TIME::TimeTToFileTime(buffer.st_mtime, &fileTime); + KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime); + pItem->m_dateTime = localTime; + + if (!pItem->m_bIsFolder) + pItem->m_dwSize = buffer.st_size; + } + } + items.Add(pItem); + } + closedir(dir); + return true; +} + +bool CPosixDirectory::Create(const CURL& url) +{ + if (!Create(url.Get())) + return Exists(url); + + return true; +} + +bool CPosixDirectory::Create(const std::string& path) +{ + if (mkdir(path.c_str(), 0755) != 0) + { + if (errno == ENOENT) + { + auto sep = path.rfind('/'); + if (sep == std::string::npos) + return false; + + if (Create(path.substr(0, sep))) + return Create(path); + } + + return false; + } + return true; +} + +bool CPosixDirectory::Remove(const CURL& url) +{ + if (rmdir(url.Get().c_str()) == 0) + return true; + + return !Exists(url); +} + +bool CPosixDirectory::RemoveRecursive(const CURL& url) +{ + std::string root = url.Get(); + + if (IsAliasShortcut(root, true)) + TranslateAliasShortcut(root); + + DIR *dir = opendir(root.c_str()); + if (!dir) + return false; + + bool success(true); + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + + std::string itemLabel(entry->d_name); + CCharsetConverter::unknownToUTF8(itemLabel); + std::string itemPath(URIUtils::AddFileToFolder(root, entry->d_name)); + + bool bStat = false; + struct stat buffer; + + // Unix-based readdir implementations may return an incorrect dirent.d_ino value that + // is not equal to the (correct) stat() obtained one. In this case the file type + // could not be determined and the value of dirent.d_type is set to DT_UNKNOWN. + // In order to get a correct value we have to incur the cost of calling stat. + if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) + { + if (stat(itemPath.c_str(), &buffer) == 0) + bStat = true; + } + + if (entry->d_type == DT_DIR || (bStat && S_ISDIR(buffer.st_mode))) + { + if (!RemoveRecursive(CURL{ itemPath })) + { + success = false; + break; + } + } + else + { + if (unlink(itemPath.c_str()) != 0) + { + success = false; + break; + } + } + } + + closedir(dir); + + if (success) + { + if (rmdir(root.c_str()) != 0) + success = false; + } + + return success; +} + +bool CPosixDirectory::Exists(const CURL& url) +{ + std::string path = url.Get(); + + if (IsAliasShortcut(path, true)) + TranslateAliasShortcut(path); + + struct stat buffer; + if (stat(path.c_str(), &buffer) != 0) + return false; + return S_ISDIR(buffer.st_mode) ? true : false; +} diff --git a/xbmc/platform/posix/filesystem/PosixDirectory.h b/xbmc/platform/posix/filesystem/PosixDirectory.h new file mode 100644 index 0000000..eb323b6 --- /dev/null +++ b/xbmc/platform/posix/filesystem/PosixDirectory.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "filesystem/IDirectory.h" + +namespace XFILE +{ + +class CPosixDirectory : public IDirectory +{ +public: + CPosixDirectory(void); + ~CPosixDirectory(void) override; + bool GetDirectory(const CURL& url, CFileItemList &items) override; + bool Create(const CURL& url) override; + bool Exists(const CURL& url) override; + bool Remove(const CURL& url) override; + bool RemoveRecursive(const CURL& url) override; +private: + bool Create(const std::string& path); +}; +} diff --git a/xbmc/platform/posix/filesystem/PosixFile.cpp b/xbmc/platform/posix/filesystem/PosixFile.cpp new file mode 100644 index 0000000..3010136 --- /dev/null +++ b/xbmc/platform/posix/filesystem/PosixFile.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PosixFile.h" + +#include "URL.h" +#include "filesystem/File.h" +#include "utils/AliasShortcutUtils.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(HAVE_STATX) // use statx if available to get file birth date +#include +#endif +#include + +using namespace XFILE; + +CPosixFile::~CPosixFile() +{ + if (m_fd >= 0) + close(m_fd); +} + +// local helper +static std::string getFilename(const CURL& url) +{ + std::string filename(url.GetFileName()); + if (IsAliasShortcut(filename, false)) + TranslateAliasShortcut(filename); + + return filename; +} + + +bool CPosixFile::Open(const CURL& url) +{ + if (m_fd >= 0) + return false; + + const std::string filename(getFilename(url)); + if (filename.empty()) + return false; + + m_fd = open(filename.c_str(), O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH); + m_filePos = 0; + + return m_fd != -1; +} + +bool CPosixFile::OpenForWrite(const CURL& url, bool bOverWrite /* = false*/ ) +{ + if (m_fd >= 0) + return false; + + const std::string filename(getFilename(url)); + if (filename.empty()) + return false; + + m_fd = open(filename.c_str(), O_RDWR | O_CREAT | (bOverWrite ? O_TRUNC : 0), S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (m_fd < 0) + return false; + + m_filePos = 0; + m_allowWrite = true; + + return true; +} + +void CPosixFile::Close() +{ + if (m_fd >= 0) + { + close(m_fd); + m_fd = -1; + m_filePos = -1; + m_lastDropPos = -1; + m_allowWrite = false; + } +} + + +ssize_t CPosixFile::Read(void* lpBuf, size_t uiBufSize) +{ + if (m_fd < 0) + return -1; + + assert(lpBuf != NULL || uiBufSize == 0); + if (lpBuf == NULL && uiBufSize != 0) + return -1; + + if (uiBufSize > SSIZE_MAX) + uiBufSize = SSIZE_MAX; + + const ssize_t res = read(m_fd, lpBuf, uiBufSize); + if (res < 0) + { + Seek(0, SEEK_CUR); // force update file position + return -1; + } + + if (m_filePos >= 0) + { + m_filePos += res; // if m_filePos was known - update it +#if defined(HAVE_POSIX_FADVISE) + // Drop the cache between then last drop and 16 MB behind where we + // are now, to make sure the file doesn't displace everything else. + // However, never throw out the first 16 MB of the file, as it might + // be the header etc., and never ask the OS to drop in chunks of + // less than 1 MB. + const int64_t end_drop = m_filePos - 16 * 1024 * 1024; + if (end_drop >= 17 * 1024 * 1024) + { + const int64_t start_drop = std::max(m_lastDropPos, 16 * 1024 * 1024); + if (end_drop - start_drop >= 1 * 1024 * 1024 && + posix_fadvise(m_fd, start_drop, end_drop - start_drop, POSIX_FADV_DONTNEED) == 0) + m_lastDropPos = end_drop; + } +#endif + } + + return res; +} + +ssize_t CPosixFile::Write(const void* lpBuf, size_t uiBufSize) +{ + if (m_fd < 0) + return -1; + + assert(lpBuf != NULL || uiBufSize == 0); + if ((lpBuf == NULL && uiBufSize != 0) || !m_allowWrite) + return -1; + + if (uiBufSize > SSIZE_MAX) + uiBufSize = SSIZE_MAX; + + const ssize_t res = write(m_fd, lpBuf, uiBufSize); + if (res < 0) + { + Seek(0, SEEK_CUR); // force update file position + return -1; + } + + if (m_filePos >= 0) + m_filePos += res; // if m_filePos was known - update it + + return res; +} + +int64_t CPosixFile::Seek(int64_t iFilePosition, int iWhence /* = SEEK_SET*/) +{ + if (m_fd < 0) + return -1; + +#ifdef TARGET_ANDROID + //! @todo properly support with detection in configure + //! Android special case: Android doesn't substitute off64_t for off_t and similar functions + m_filePos = lseek64(m_fd, (off64_t)iFilePosition, iWhence); +#else // !TARGET_ANDROID + const off_t filePosOffT = (off_t) iFilePosition; + // check for parameter overflow + if (sizeof(int64_t) != sizeof(off_t) && iFilePosition != filePosOffT) + return -1; + + m_filePos = lseek(m_fd, filePosOffT, iWhence); +#endif // !TARGET_ANDROID + + return m_filePos; +} + +int CPosixFile::Truncate(int64_t size) +{ + if (m_fd < 0) + return -1; + + const off_t sizeOffT = (off_t) size; + // check for parameter overflow + if (sizeof(int64_t) != sizeof(off_t) && size != sizeOffT) + return -1; + + return ftruncate(m_fd, sizeOffT); +} + +int64_t CPosixFile::GetPosition() +{ + if (m_fd < 0) + return -1; + + if (m_filePos < 0) + m_filePos = lseek(m_fd, 0, SEEK_CUR); + + return m_filePos; +} + +int64_t CPosixFile::GetLength() +{ + if (m_fd < 0) + return -1; + + struct stat64 st; + if (fstat64(m_fd, &st) != 0) + return -1; + + return st.st_size; +} + +void CPosixFile::Flush() +{ + if (m_fd >= 0) + fsync(m_fd); +} + +int CPosixFile::IoControl(EIoControl request, void* param) +{ + if (m_fd < 0) + return -1; + + if (request == IOCTRL_NATIVE) + { + if(!param) + return -1; + return ioctl(m_fd, ((SNativeIoControl*)param)->request, ((SNativeIoControl*)param)->param); + } + else if (request == IOCTRL_SEEK_POSSIBLE) + { + if (GetPosition() < 0) + return -1; // current position is unknown, can't test seeking + else if (m_filePos > 0) + { + const int64_t orgPos = m_filePos; + // try to seek one byte back + const bool seekPossible = (Seek(orgPos - 1, SEEK_SET) == (orgPos - 1)); + // restore file position + if (Seek(orgPos, SEEK_SET) != orgPos) + return 0; // seeking is not possible + + return seekPossible ? 1 : 0; + } + else + { // m_filePos == 0 + // try to seek one byte forward + const bool seekPossible = (Seek(1, SEEK_SET) == 1); + // restore file position + if (Seek(0, SEEK_SET) != 0) + return 0; // seeking is not possible + + if (seekPossible) + return 1; + + if (GetLength() <= 0) + return -1; // size of file is zero or can be zero, can't test seeking + else + return 0; // size of file is 1 byte or more and seeking not possible + } + } + + return -1; +} + + +bool CPosixFile::Delete(const CURL& url) +{ + const std::string filename(getFilename(url)); + if (filename.empty()) + return false; + + if (unlink(filename.c_str()) == 0) + return true; + + if (errno == EACCES || errno == EPERM) + CLog::LogF(LOGWARNING, "Can't access file \"{}\"", filename); + + return false; +} + +bool CPosixFile::Rename(const CURL& url, const CURL& urlnew) +{ + const std::string name(getFilename(url)), newName(getFilename(urlnew)); + if (name.empty() || newName.empty()) + return false; + + if (name == newName) + return true; + + if (rename(name.c_str(), newName.c_str()) == 0) + return true; + + if (errno == EACCES || errno == EPERM) + CLog::LogF(LOGWARNING, "Can't access file \"{}\" for rename to \"{}\"", name, newName); + + // rename across mount points - need to copy/delete + if (errno == EXDEV) + { + CLog::LogF(LOGDEBUG, + "Source file \"{}\" and target file \"{}\" are located on different filesystems, " + "copy&delete will be used instead of rename", + name, newName); + if (XFILE::CFile::Copy(name, newName)) + { + if (XFILE::CFile::Delete(name)) + return true; + else + XFILE::CFile::Delete(newName); + } + } + + return false; +} + +bool CPosixFile::Exists(const CURL& url) +{ + const std::string filename(getFilename(url)); + if (filename.empty()) + return false; + + struct stat64 st; + return stat64(filename.c_str(), &st) == 0 && !S_ISDIR(st.st_mode); +} + +int CPosixFile::Stat(const CURL& url, struct __stat64* buffer) +{ + assert(buffer != NULL); + const std::string filename(getFilename(url)); + if (filename.empty() || !buffer) + return -1; + +// Use statx to get file creation date (btime) which isn't available with just stat. This fills the +// buffer with the same data as the Windows implementation. Useful for the music library so that +// tags can be updated without changing the date they were added to the library (as m/ctime does) + +#if defined(HAVE_STATX) + int dirfd = AT_FDCWD; + int flags = AT_STATX_SYNC_AS_STAT; + unsigned int mask = STATX_BASIC_STATS | STATX_BTIME; + struct statx stxbuf = {}; + long ret = 0; + ret = statx(dirfd, filename.c_str(), flags, mask, &stxbuf); + if (ret == 0) + { + *buffer = {}; + buffer->st_mtime = stxbuf.stx_mtime.tv_sec; // modification time + if (stxbuf.stx_btime.tv_sec != 0) + buffer->st_ctime = stxbuf.stx_btime.tv_sec; // birth (creation) time + else + buffer->st_ctime = stxbuf.stx_ctime.tv_sec; // change time (of metadata or file) + // fill everything else we might need (statx buffer is slightly different to stat buffer so + // can't just return the statx buffer) Note we might not need all this but lets fill it for + // completeness + buffer->st_atime = stxbuf.stx_atime.tv_sec; + buffer->st_size = stxbuf.stx_size; + buffer->st_blksize = stxbuf.stx_blksize; + buffer->st_blocks = stxbuf.stx_blocks; + buffer->st_ino = stxbuf.stx_ino; + buffer->st_nlink = stxbuf.stx_nlink; + buffer->st_uid = stxbuf.stx_uid; + buffer->st_gid = stxbuf.stx_gid; + buffer->st_mode = stxbuf.stx_mode; + buffer->st_rdev = makedev(stxbuf.stx_rdev_major, stxbuf.stx_rdev_minor); + buffer->st_dev = makedev(stxbuf.stx_dev_major, stxbuf.stx_dev_minor); + } + return ret; +#else + return stat64(filename.c_str(), buffer); +#endif +} + +int CPosixFile::Stat(struct __stat64* buffer) +{ + assert(buffer != NULL); + if (m_fd < 0 || !buffer) + return -1; + + return fstat64(m_fd, buffer); +} diff --git a/xbmc/platform/posix/filesystem/PosixFile.h b/xbmc/platform/posix/filesystem/PosixFile.h new file mode 100644 index 0000000..aa5e0a0 --- /dev/null +++ b/xbmc/platform/posix/filesystem/PosixFile.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "filesystem/IFile.h" + +namespace XFILE +{ + + class CPosixFile : public IFile + { + public: + ~CPosixFile() override; + + bool Open(const CURL& url) override; + bool OpenForWrite(const CURL& url, bool bOverWrite = false) override; + void Close() override; + + ssize_t Read(void* lpBuf, size_t uiBufSize) override; + ssize_t Write(const void* lpBuf, size_t uiBufSize) override; + int64_t Seek(int64_t iFilePosition, int iWhence = SEEK_SET) override; + int Truncate(int64_t size) override; + int64_t GetPosition() override; + int64_t GetLength() override; + void Flush() override; + int IoControl(EIoControl request, void* param) override; + + bool Delete(const CURL& url) override; + bool Rename(const CURL& url, const CURL& urlnew) override; + bool Exists(const CURL& url) override; + int Stat(const CURL& url, struct __stat64* buffer) override; + int Stat(struct __stat64* buffer) override; + + protected: + int m_fd = -1; + int64_t m_filePos = -1; + int64_t m_lastDropPos = -1; + bool m_allowWrite = false; + }; + +} diff --git a/xbmc/platform/posix/filesystem/SMBDirectory.cpp b/xbmc/platform/posix/filesystem/SMBDirectory.cpp new file mode 100644 index 0000000..e2305af --- /dev/null +++ b/xbmc/platform/posix/filesystem/SMBDirectory.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +/* +* know bugs: +* - when opening a server for the first time with ip address and the second time +* with server name, access to the server is denied. +* - when browsing entire network, user can't go back one step +* share = smb://, user selects a workgroup, user selects a server. +* doing ".." will go back to smb:// (entire network) and not to workgroup list. +* +* debugging is set to a max of 10 for release builds (see local.h) +*/ + +#include "SMBDirectory.h" + +#include "FileItem.h" +#include "PasswordManager.h" +#include "ServiceBroker.h" +#include "guilib/LocalizeStrings.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/XTimeUtils.h" +#include "utils/log.h" + +#include "platform/posix/filesystem/SMBWSDiscovery.h" + +#include + +#include + +struct CachedDirEntry +{ + unsigned int type; + std::string name; +}; + +using namespace XFILE; + +CSMBDirectory::CSMBDirectory(void) +{ + smb.AddActiveConnection(); +} + +CSMBDirectory::~CSMBDirectory(void) +{ + smb.AddIdleConnection(); +} + +bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) +{ + // We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] + + /* samba isn't thread safe with old interface, always lock */ + std::unique_lock lock(smb); + + smb.Init(); + + //Separate roots for the authentication and the containing items to allow browsing to work correctly + std::string strRoot = url.Get(); + std::string strAuth; + + lock.unlock(); // OpenDir is locked + + // if url provided does not having anything except smb protocol + // Do a WS-Discovery search to find possible smb servers to mimic smbv1 behaviour + if (strRoot == "smb://") + { + auto settingsComponent = CServiceBroker::GetSettingsComponent(); + if (!settingsComponent) + return false; + + auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + if (!settings) + return false; + + // Check WS-Discovery daemon enabled, if not return as smb:// cant be handled further + if (settings->GetBool(CSettings::SETTING_SERVICES_WSDISCOVERY)) + { + WSDiscovery::CWSDiscoveryPosix& WSInstance = + dynamic_cast(CServiceBroker::GetWSDiscovery()); + return WSInstance.GetServerList(items); + } + else + { + return false; + } + } + + int fd = OpenDir(url, strAuth); + if (fd < 0) + return false; + + URIUtils::AddSlashAtEnd(strRoot); + URIUtils::AddSlashAtEnd(strAuth); + + std::string strFile; + + // need to keep the samba lock for as short as possible. + // so we first cache all directory entries and then go over them again asking for stat + // "stat" is locked each time. that way the lock is freed between stat requests + std::vector vecEntries; + struct smbc_dirent* dirEnt; + + lock.lock(); + if (!smb.IsSmbValid()) + return false; + while ((dirEnt = smbc_readdir(fd))) + { + CachedDirEntry aDir; + aDir.type = dirEnt->smbc_type; + aDir.name = dirEnt->name; + vecEntries.push_back(aDir); + } + smbc_closedir(fd); + lock.unlock(); + + for (size_t i=0; i