fs/FileInfo: new library providing GetFileInfo()
Replaces StatFile(), with a portable data object.
This commit is contained in:
parent
00583bc4a8
commit
90a61b6bab
@ -556,6 +556,7 @@ libfs_a_SOURCES = \
|
|||||||
src/fs/Path.cxx src/fs/Path2.cxx src/fs/Path.hxx \
|
src/fs/Path.cxx src/fs/Path2.cxx src/fs/Path.hxx \
|
||||||
src/fs/AllocatedPath.cxx src/fs/AllocatedPath.hxx \
|
src/fs/AllocatedPath.cxx src/fs/AllocatedPath.hxx \
|
||||||
src/fs/FileSystem.cxx src/fs/FileSystem.hxx \
|
src/fs/FileSystem.cxx src/fs/FileSystem.hxx \
|
||||||
|
src/fs/FileInfo.hxx \
|
||||||
src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \
|
src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \
|
||||||
src/fs/CheckFile.cxx src/fs/CheckFile.hxx \
|
src/fs/CheckFile.cxx src/fs/CheckFile.hxx \
|
||||||
src/fs/DirectoryReader.hxx
|
src/fs/DirectoryReader.hxx
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "fs/Charset.hxx"
|
#include "fs/Charset.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
|
#include "fs/FileInfo.hxx"
|
||||||
#include "fs/DirectoryReader.hxx"
|
#include "fs/DirectoryReader.hxx"
|
||||||
#include "util/StringUtil.hxx"
|
#include "util/StringUtil.hxx"
|
||||||
#include "util/UriUtil.hxx"
|
#include "util/UriUtil.hxx"
|
||||||
@ -178,8 +179,8 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto path_fs = AllocatedPath::Build(parent_path_fs, name_fs);
|
const auto path_fs = AllocatedPath::Build(parent_path_fs, name_fs);
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode))
|
if (!GetFileInfo(path_fs, fi) || !fi.IsRegular())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::string name(name_fs_str,
|
std::string name(name_fs_str,
|
||||||
@ -189,7 +190,7 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
info.name = std::move(name_utf8);
|
info.name = std::move(name_utf8);
|
||||||
info.mtime = st.st_mtime;
|
info.mtime = fi.GetModificationTime();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/Traits.hxx"
|
#include "fs/Traits.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "decoder/DecoderList.hxx"
|
#include "decoder/DecoderList.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "tag/TagBuilder.hxx"
|
#include "tag/TagBuilder.hxx"
|
||||||
@ -148,8 +148,8 @@ DetachedSong::Update()
|
|||||||
const AllocatedPath path_fs =
|
const AllocatedPath path_fs =
|
||||||
AllocatedPath::FromUTF8(GetRealURI());
|
AllocatedPath::FromUTF8(GetRealURI());
|
||||||
|
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode))
|
if (!GetFileInfo(path_fs, fi) || !fi.IsRegular())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
TagBuilder tag_builder;
|
TagBuilder tag_builder;
|
||||||
@ -160,7 +160,7 @@ DetachedSong::Update()
|
|||||||
tag_scan_fallback(path_fs, &full_tag_handler,
|
tag_scan_fallback(path_fs, &full_tag_handler,
|
||||||
&tag_builder);
|
&tag_builder);
|
||||||
|
|
||||||
mtime = st.st_mtime;
|
mtime = fi.GetModificationTime();
|
||||||
tag_builder.Commit(tag);
|
tag_builder.Commit(tag);
|
||||||
return true;
|
return true;
|
||||||
} else if (IsRemote()) {
|
} else if (IsRemote()) {
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "Client.hxx"
|
#include "Client.hxx"
|
||||||
#include "protocol/Ack.hxx"
|
#include "protocol/Ack.hxx"
|
||||||
#include "fs/Path.hxx"
|
#include "fs/Path.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -47,13 +47,11 @@ Client::AllowFile(Path path_fs, Error &error) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (!StatFile(path_fs, st)) {
|
if (!GetFileInfo(path_fs, fi, error))
|
||||||
error.SetErrno();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) {
|
if (fi.GetUid() != (uid_t)uid && (fi.GetMode() & 0444) != 0444) {
|
||||||
/* client is not owner */
|
/* client is not owner */
|
||||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||||
return false;
|
return false;
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "TagFile.hxx"
|
#include "TagFile.hxx"
|
||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "fs/DirectoryReader.hxx"
|
#include "fs/DirectoryReader.hxx"
|
||||||
#include "TimePrint.hxx"
|
#include "TimePrint.hxx"
|
||||||
#include "ls.hxx"
|
#include "ls.hxx"
|
||||||
@ -99,22 +99,22 @@ handle_listfiles_local(Client &client, const char *path_utf8)
|
|||||||
|
|
||||||
const AllocatedPath full_fs =
|
const AllocatedPath full_fs =
|
||||||
AllocatedPath::Build(path_fs, name_fs);
|
AllocatedPath::Build(path_fs, name_fs);
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (!StatFile(full_fs, st, false))
|
if (!GetFileInfo(full_fs, fi, false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (S_ISREG(st.st_mode)) {
|
if (fi.IsRegular())
|
||||||
client_printf(client, "file: %s\n"
|
client_printf(client, "file: %s\n"
|
||||||
"size: %" PRIu64 "\n",
|
"size: %" PRIu64 "\n",
|
||||||
name_utf8.c_str(),
|
name_utf8.c_str(),
|
||||||
uint64_t(st.st_size));
|
fi.GetSize());
|
||||||
} else if (S_ISDIR(st.st_mode))
|
else if (fi.IsDirectory())
|
||||||
client_printf(client, "directory: %s\n",
|
client_printf(client, "directory: %s\n",
|
||||||
name_utf8.c_str());
|
name_utf8.c_str());
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
time_print(client, "Last-Modified", st.st_mtime);
|
time_print(client, "Last-Modified", fi.GetModificationTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "fs/io/TextFile.hxx"
|
#include "fs/io/TextFile.hxx"
|
||||||
#include "fs/io/BufferedOutputStream.hxx"
|
#include "fs/io/BufferedOutputStream.hxx"
|
||||||
#include "fs/io/FileOutputStream.hxx"
|
#include "fs/io/FileOutputStream.hxx"
|
||||||
|
#include "fs/FileInfo.hxx"
|
||||||
#include "config/Block.hxx"
|
#include "config/Block.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
#include "util/CharUtil.hxx"
|
#include "util/CharUtil.hxx"
|
||||||
@ -124,15 +125,13 @@ SimpleDatabase::Check(Error &error) const
|
|||||||
const auto dirPath = path.GetDirectoryName();
|
const auto dirPath = path.GetDirectoryName();
|
||||||
|
|
||||||
/* Check that the parent part of the path is a directory */
|
/* Check that the parent part of the path is a directory */
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (!StatFile(dirPath, st)) {
|
if (!GetFileInfo(dirPath, fi, error)) {
|
||||||
error.FormatErrno("Couldn't stat parent directory of db file "
|
error.AddPrefix("On parent directory of db file: ");
|
||||||
"\"%s\"",
|
|
||||||
path_utf8.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISDIR(st.st_mode)) {
|
if (!fi.IsDirectory()) {
|
||||||
error.Format(simple_db_domain,
|
error.Format(simple_db_domain,
|
||||||
"Couldn't create db file \"%s\" because the "
|
"Couldn't create db file \"%s\" because the "
|
||||||
"parent path is not a directory",
|
"parent path is not a directory",
|
||||||
@ -154,14 +153,11 @@ SimpleDatabase::Check(Error &error) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Path exists, now check if it's a regular file */
|
/* Path exists, now check if it's a regular file */
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (!StatFile(path, st)) {
|
if (!GetFileInfo(path, fi, error))
|
||||||
error.FormatErrno("Couldn't stat db file \"%s\"",
|
|
||||||
path_utf8.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!S_ISREG(st.st_mode)) {
|
if (!fi.IsRegular()) {
|
||||||
error.Format(simple_db_domain,
|
error.Format(simple_db_domain,
|
||||||
"db file \"%s\" is not a regular file",
|
"db file \"%s\" is not a regular file",
|
||||||
path_utf8.c_str());
|
path_utf8.c_str());
|
||||||
@ -193,9 +189,9 @@ SimpleDatabase::Load(Error &error)
|
|||||||
if (!db_load_internal(file, *root, error) || !file.Check(error))
|
if (!db_load_internal(file, *root, error) || !file.Check(error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (StatFile(path, st))
|
if (GetFileInfo(path, fi))
|
||||||
mtime = st.st_mtime;
|
mtime = fi.GetModificationTime();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -425,9 +421,9 @@ SimpleDatabase::Save(Error &error)
|
|||||||
if (!fos.Commit(error))
|
if (!fos.Commit(error))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (StatFile(path, st))
|
if (GetFileInfo(path, fi))
|
||||||
mtime = st.st_mtime;
|
mtime = fi.GetModificationTime();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#include "InotifyDomain.hxx"
|
#include "InotifyDomain.hxx"
|
||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
@ -179,7 +179,6 @@ recursive_watch_subdirectories(WatchDirectory *directory,
|
|||||||
}
|
}
|
||||||
|
|
||||||
while ((ent = readdir(dir))) {
|
while ((ent = readdir(dir))) {
|
||||||
struct stat st;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (skip_path(ent->d_name))
|
if (skip_path(ent->d_name))
|
||||||
@ -187,15 +186,15 @@ recursive_watch_subdirectories(WatchDirectory *directory,
|
|||||||
|
|
||||||
const auto child_path_fs =
|
const auto child_path_fs =
|
||||||
AllocatedPath::Build(path_fs, ent->d_name);
|
AllocatedPath::Build(path_fs, ent->d_name);
|
||||||
ret = StatFile(child_path_fs, st);
|
|
||||||
if (ret < 0) {
|
FileInfo fi;
|
||||||
FormatErrno(inotify_domain,
|
if (!GetFileInfo(child_path_fs, fi, error)) {
|
||||||
"Failed to stat %s",
|
LogError(error);
|
||||||
child_path_fs.c_str());
|
error.Clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISDIR(st.st_mode))
|
if (!fi.IsDirectory())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = inotify_source->Add(child_path_fs.c_str(), IN_MASK,
|
ret = inotify_source->Add(child_path_fs.c_str(), IN_MASK,
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "CheckFile.hxx"
|
#include "CheckFile.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
#include "config/ConfigError.hxx"
|
#include "config/ConfigError.hxx"
|
||||||
#include "FileSystem.hxx"
|
#include "FileInfo.hxx"
|
||||||
#include "Path.hxx"
|
#include "Path.hxx"
|
||||||
#include "AllocatedPath.hxx"
|
#include "AllocatedPath.hxx"
|
||||||
#include "DirectoryReader.hxx"
|
#include "DirectoryReader.hxx"
|
||||||
@ -32,15 +32,15 @@
|
|||||||
void
|
void
|
||||||
CheckDirectoryReadable(Path path_fs)
|
CheckDirectoryReadable(Path path_fs)
|
||||||
{
|
{
|
||||||
struct stat st;
|
Error error;
|
||||||
if (!StatFile(path_fs, st)) {
|
|
||||||
FormatErrno(config_domain,
|
FileInfo fi;
|
||||||
"Failed to stat directory \"%s\"",
|
if (!GetFileInfo(path_fs, fi, error)) {
|
||||||
path_fs.c_str());
|
LogError(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!S_ISDIR(st.st_mode)) {
|
if (!fi.IsDirectory()) {
|
||||||
FormatError(config_domain,
|
FormatError(config_domain,
|
||||||
"Not a directory: %s", path_fs.c_str());
|
"Not a directory: %s", path_fs.c_str());
|
||||||
return;
|
return;
|
||||||
@ -49,7 +49,7 @@ CheckDirectoryReadable(Path path_fs)
|
|||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
const auto x = AllocatedPath::Build(path_fs,
|
const auto x = AllocatedPath::Build(path_fs,
|
||||||
PathTraitsFS::CURRENT_DIRECTORY);
|
PathTraitsFS::CURRENT_DIRECTORY);
|
||||||
if (!StatFile(x, st) && errno == EACCES)
|
if (!GetFileInfo(x, fi) && errno == EACCES)
|
||||||
FormatError(config_domain,
|
FormatError(config_domain,
|
||||||
"No permission to traverse (\"execute\") directory: %s",
|
"No permission to traverse (\"execute\") directory: %s",
|
||||||
path_fs.c_str());
|
path_fs.c_str());
|
||||||
|
106
src/fs/FileInfo.hxx
Normal file
106
src/fs/FileInfo.hxx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2015 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_FS_FILE_INFO_HXX
|
||||||
|
#define MPD_FS_FILE_INFO_HXX
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "Path.hxx"
|
||||||
|
#include "util/Error.hxx"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
class FileInfo {
|
||||||
|
friend bool GetFileInfo(Path path, FileInfo &info,
|
||||||
|
bool follow_symlinks);
|
||||||
|
friend bool GetFileInfo(Path path, FileInfo &info,
|
||||||
|
Error &error);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool IsRegular() const {
|
||||||
|
return S_ISREG(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDirectory() const {
|
||||||
|
return S_ISDIR(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetSize() const {
|
||||||
|
return st.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t GetModificationTime() const {
|
||||||
|
return st.st_mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
uid_t GetUid() const {
|
||||||
|
return st.st_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_t GetMode() const {
|
||||||
|
return st.st_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_t GetDevice() const {
|
||||||
|
return st.st_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
ino_t GetInode() const {
|
||||||
|
return st.st_ino;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
GetFileInfo(Path path, FileInfo &info, bool follow_symlinks=true)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
(void)follow_symlinks;
|
||||||
|
return stat(path.c_str(), &info.st) == 0;
|
||||||
|
#else
|
||||||
|
int ret = follow_symlinks
|
||||||
|
? stat(path.c_str(), &info.st)
|
||||||
|
: lstat(path.c_str(), &info.st);
|
||||||
|
return ret == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
GetFileInfo(Path path, FileInfo &info, bool follow_symlinks, Error &error)
|
||||||
|
{
|
||||||
|
bool success = GetFileInfo(path, info, follow_symlinks);
|
||||||
|
if (!success) {
|
||||||
|
const auto path_utf8 = path.ToUTF8();
|
||||||
|
error.FormatErrno("Failed to access %s", path_utf8.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
GetFileInfo(Path path, FileInfo &info, Error &error)
|
||||||
|
{
|
||||||
|
return GetFileInfo(path, info, true, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -25,6 +25,7 @@
|
|||||||
#include "../Timer.hxx"
|
#include "../Timer.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileSystem.hxx"
|
||||||
|
#include "fs/FileInfo.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
@ -105,8 +106,8 @@ FifoOutput::Close()
|
|||||||
output = -1;
|
output = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
FileInfo fi;
|
||||||
if (created && StatFile(path, st))
|
if (created && GetFileInfo(path, fi))
|
||||||
Delete();
|
Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "storage/StorageInterface.hxx"
|
#include "storage/StorageInterface.hxx"
|
||||||
#include "storage/FileInfo.hxx"
|
#include "storage/FileInfo.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
#include "fs/FileSystem.hxx"
|
#include "fs/FileInfo.hxx"
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
#include "fs/DirectoryReader.hxx"
|
#include "fs/DirectoryReader.hxx"
|
||||||
|
|
||||||
@ -81,26 +81,25 @@ private:
|
|||||||
static bool
|
static bool
|
||||||
Stat(Path path, bool follow, StorageFileInfo &info, Error &error)
|
Stat(Path path, bool follow, StorageFileInfo &info, Error &error)
|
||||||
{
|
{
|
||||||
struct stat st;
|
FileInfo src;
|
||||||
if (!StatFile(path, st, follow)) {
|
if (!GetFileInfo(path, src, follow, error))
|
||||||
error.SetErrno();
|
|
||||||
|
|
||||||
const auto path_utf8 = path.ToUTF8();
|
|
||||||
error.FormatPrefix("Failed to stat %s: ", path_utf8.c_str());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (S_ISREG(st.st_mode))
|
if (src.IsRegular())
|
||||||
info.type = StorageFileInfo::Type::REGULAR;
|
info.type = StorageFileInfo::Type::REGULAR;
|
||||||
else if (S_ISDIR(st.st_mode))
|
else if (src.IsDirectory())
|
||||||
info.type = StorageFileInfo::Type::DIRECTORY;
|
info.type = StorageFileInfo::Type::DIRECTORY;
|
||||||
else
|
else
|
||||||
info.type = StorageFileInfo::Type::OTHER;
|
info.type = StorageFileInfo::Type::OTHER;
|
||||||
|
|
||||||
info.size = st.st_size;
|
info.size = src.GetSize();
|
||||||
info.mtime = st.st_mtime;
|
info.mtime = src.GetModificationTime();
|
||||||
info.device = st.st_dev;
|
#ifdef WIN32
|
||||||
info.inode = st.st_ino;
|
info.device = info.inode = 0;
|
||||||
|
#else
|
||||||
|
info.device = src.GetDevice();
|
||||||
|
info.inode = src.GetInode();
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user