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/AllocatedPath.cxx src/fs/AllocatedPath.hxx \
|
||||
src/fs/FileSystem.cxx src/fs/FileSystem.hxx \
|
||||
src/fs/FileInfo.hxx \
|
||||
src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \
|
||||
src/fs/CheckFile.cxx src/fs/CheckFile.hxx \
|
||||
src/fs/DirectoryReader.hxx
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/Charset.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/DirectoryReader.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
@ -178,8 +179,8 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
|
||||
return false;
|
||||
|
||||
const auto path_fs = AllocatedPath::Build(parent_path_fs, name_fs);
|
||||
struct stat st;
|
||||
if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode))
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(path_fs, fi) || !fi.IsRegular())
|
||||
return false;
|
||||
|
||||
std::string name(name_fs_str,
|
||||
@ -189,7 +190,7 @@ LoadPlaylistFileInfo(PlaylistInfo &info,
|
||||
return false;
|
||||
|
||||
info.name = std::move(name_utf8);
|
||||
info.mtime = st.st_mtime;
|
||||
info.mtime = fi.GetModificationTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "util/Error.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "decoder/DecoderList.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "tag/TagBuilder.hxx"
|
||||
@ -148,8 +148,8 @@ DetachedSong::Update()
|
||||
const AllocatedPath path_fs =
|
||||
AllocatedPath::FromUTF8(GetRealURI());
|
||||
|
||||
struct stat st;
|
||||
if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode))
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(path_fs, fi) || !fi.IsRegular())
|
||||
return false;
|
||||
|
||||
TagBuilder tag_builder;
|
||||
@ -160,7 +160,7 @@ DetachedSong::Update()
|
||||
tag_scan_fallback(path_fs, &full_tag_handler,
|
||||
&tag_builder);
|
||||
|
||||
mtime = st.st_mtime;
|
||||
mtime = fi.GetModificationTime();
|
||||
tag_builder.Commit(tag);
|
||||
return true;
|
||||
} else if (IsRemote()) {
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "Client.hxx"
|
||||
#include "protocol/Ack.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
@ -47,13 +47,11 @@ Client::AllowFile(Path path_fs, Error &error) const
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (!StatFile(path_fs, st)) {
|
||||
error.SetErrno();
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(path_fs, fi, error))
|
||||
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 */
|
||||
error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied");
|
||||
return false;
|
||||
|
@ -36,7 +36,7 @@
|
||||
#include "TagFile.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/DirectoryReader.hxx"
|
||||
#include "TimePrint.hxx"
|
||||
#include "ls.hxx"
|
||||
@ -99,22 +99,22 @@ handle_listfiles_local(Client &client, const char *path_utf8)
|
||||
|
||||
const AllocatedPath full_fs =
|
||||
AllocatedPath::Build(path_fs, name_fs);
|
||||
struct stat st;
|
||||
if (!StatFile(full_fs, st, false))
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(full_fs, fi, false))
|
||||
continue;
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
if (fi.IsRegular())
|
||||
client_printf(client, "file: %s\n"
|
||||
"size: %" PRIu64 "\n",
|
||||
name_utf8.c_str(),
|
||||
uint64_t(st.st_size));
|
||||
} else if (S_ISDIR(st.st_mode))
|
||||
fi.GetSize());
|
||||
else if (fi.IsDirectory())
|
||||
client_printf(client, "directory: %s\n",
|
||||
name_utf8.c_str());
|
||||
else
|
||||
continue;
|
||||
|
||||
time_print(client, "Last-Modified", st.st_mtime);
|
||||
time_print(client, "Last-Modified", fi.GetModificationTime());
|
||||
}
|
||||
|
||||
return CommandResult::OK;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "fs/io/BufferedOutputStream.hxx"
|
||||
#include "fs/io/FileOutputStream.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/CharUtil.hxx"
|
||||
@ -124,15 +125,13 @@ SimpleDatabase::Check(Error &error) const
|
||||
const auto dirPath = path.GetDirectoryName();
|
||||
|
||||
/* Check that the parent part of the path is a directory */
|
||||
struct stat st;
|
||||
if (!StatFile(dirPath, st)) {
|
||||
error.FormatErrno("Couldn't stat parent directory of db file "
|
||||
"\"%s\"",
|
||||
path_utf8.c_str());
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(dirPath, fi, error)) {
|
||||
error.AddPrefix("On parent directory of db file: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
if (!fi.IsDirectory()) {
|
||||
error.Format(simple_db_domain,
|
||||
"Couldn't create db file \"%s\" because the "
|
||||
"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 */
|
||||
struct stat st;
|
||||
if (!StatFile(path, st)) {
|
||||
error.FormatErrno("Couldn't stat db file \"%s\"",
|
||||
path_utf8.c_str());
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(path, fi, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
if (!fi.IsRegular()) {
|
||||
error.Format(simple_db_domain,
|
||||
"db file \"%s\" is not a regular file",
|
||||
path_utf8.c_str());
|
||||
@ -193,9 +189,9 @@ SimpleDatabase::Load(Error &error)
|
||||
if (!db_load_internal(file, *root, error) || !file.Check(error))
|
||||
return false;
|
||||
|
||||
struct stat st;
|
||||
if (StatFile(path, st))
|
||||
mtime = st.st_mtime;
|
||||
FileInfo fi;
|
||||
if (GetFileInfo(path, fi))
|
||||
mtime = fi.GetModificationTime();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -425,9 +421,9 @@ SimpleDatabase::Save(Error &error)
|
||||
if (!fos.Commit(error))
|
||||
return false;
|
||||
|
||||
struct stat st;
|
||||
if (StatFile(path, st))
|
||||
mtime = st.st_mtime;
|
||||
FileInfo fi;
|
||||
if (GetFileInfo(path, fi))
|
||||
mtime = fi.GetModificationTime();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "InotifyDomain.hxx"
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
@ -179,7 +179,6 @@ recursive_watch_subdirectories(WatchDirectory *directory,
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir))) {
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
if (skip_path(ent->d_name))
|
||||
@ -187,15 +186,15 @@ recursive_watch_subdirectories(WatchDirectory *directory,
|
||||
|
||||
const auto child_path_fs =
|
||||
AllocatedPath::Build(path_fs, ent->d_name);
|
||||
ret = StatFile(child_path_fs, st);
|
||||
if (ret < 0) {
|
||||
FormatErrno(inotify_domain,
|
||||
"Failed to stat %s",
|
||||
child_path_fs.c_str());
|
||||
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(child_path_fs, fi, error)) {
|
||||
LogError(error);
|
||||
error.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
if (!fi.IsDirectory())
|
||||
continue;
|
||||
|
||||
ret = inotify_source->Add(child_path_fs.c_str(), IN_MASK,
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "CheckFile.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "config/ConfigError.hxx"
|
||||
#include "FileSystem.hxx"
|
||||
#include "FileInfo.hxx"
|
||||
#include "Path.hxx"
|
||||
#include "AllocatedPath.hxx"
|
||||
#include "DirectoryReader.hxx"
|
||||
@ -32,15 +32,15 @@
|
||||
void
|
||||
CheckDirectoryReadable(Path path_fs)
|
||||
{
|
||||
struct stat st;
|
||||
if (!StatFile(path_fs, st)) {
|
||||
FormatErrno(config_domain,
|
||||
"Failed to stat directory \"%s\"",
|
||||
path_fs.c_str());
|
||||
Error error;
|
||||
|
||||
FileInfo fi;
|
||||
if (!GetFileInfo(path_fs, fi, error)) {
|
||||
LogError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
if (!fi.IsDirectory()) {
|
||||
FormatError(config_domain,
|
||||
"Not a directory: %s", path_fs.c_str());
|
||||
return;
|
||||
@ -49,7 +49,7 @@ CheckDirectoryReadable(Path path_fs)
|
||||
#ifndef WIN32
|
||||
const auto x = AllocatedPath::Build(path_fs,
|
||||
PathTraitsFS::CURRENT_DIRECTORY);
|
||||
if (!StatFile(x, st) && errno == EACCES)
|
||||
if (!GetFileInfo(x, fi) && errno == EACCES)
|
||||
FormatError(config_domain,
|
||||
"No permission to traverse (\"execute\") directory: %s",
|
||||
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 "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
@ -105,8 +106,8 @@ FifoOutput::Close()
|
||||
output = -1;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (created && StatFile(path, st))
|
||||
FileInfo fi;
|
||||
if (created && GetFileInfo(path, fi))
|
||||
Delete();
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "storage/StorageInterface.hxx"
|
||||
#include "storage/FileInfo.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/DirectoryReader.hxx"
|
||||
|
||||
@ -81,26 +81,25 @@ private:
|
||||
static bool
|
||||
Stat(Path path, bool follow, StorageFileInfo &info, Error &error)
|
||||
{
|
||||
struct stat st;
|
||||
if (!StatFile(path, st, follow)) {
|
||||
error.SetErrno();
|
||||
|
||||
const auto path_utf8 = path.ToUTF8();
|
||||
error.FormatPrefix("Failed to stat %s: ", path_utf8.c_str());
|
||||
FileInfo src;
|
||||
if (!GetFileInfo(path, src, follow, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode))
|
||||
if (src.IsRegular())
|
||||
info.type = StorageFileInfo::Type::REGULAR;
|
||||
else if (S_ISDIR(st.st_mode))
|
||||
else if (src.IsDirectory())
|
||||
info.type = StorageFileInfo::Type::DIRECTORY;
|
||||
else
|
||||
info.type = StorageFileInfo::Type::OTHER;
|
||||
|
||||
info.size = st.st_size;
|
||||
info.mtime = st.st_mtime;
|
||||
info.device = st.st_dev;
|
||||
info.inode = st.st_ino;
|
||||
info.size = src.GetSize();
|
||||
info.mtime = src.GetModificationTime();
|
||||
#ifdef WIN32
|
||||
info.device = info.inode = 0;
|
||||
#else
|
||||
info.device = src.GetDevice();
|
||||
info.inode = src.GetInode();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user