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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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