fs/FileInfo: new library providing GetFileInfo()
Replaces StatFile(), with a portable data object.
This commit is contained in:
		| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann