fs/FileInfo: new library providing GetFileInfo()

Replaces StatFile(), with a portable data object.
This commit is contained in:
Max Kellermann 2015-02-28 20:42:50 +01:00
parent 00583bc4a8
commit 90a61b6bab
11 changed files with 171 additions and 70 deletions

View File

@ -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

View File

@ -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;
} }

View File

@ -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()) {

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }

View File

@ -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,

View File

@ -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
View 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

View File

@ -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();
} }

View File

@ -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;
} }