mirror of
https://github.com/mhx/dwarfs.git
synced 2025-09-18 17:00:30 -04:00
WIP file_stat
This commit is contained in:
parent
46cf71139f
commit
fd157eb611
@ -351,6 +351,7 @@ list(
|
||||
src/dwarfs/entry.cpp
|
||||
src/dwarfs/error.cpp
|
||||
src/dwarfs/file_scanner.cpp
|
||||
src/dwarfs/file_type.cpp
|
||||
src/dwarfs/filesystem_extractor.cpp
|
||||
src/dwarfs/filesystem_v2.cpp
|
||||
src/dwarfs/filesystem_writer.cpp
|
||||
@ -366,7 +367,7 @@ list(
|
||||
src/dwarfs/nilsimsa.cpp
|
||||
src/dwarfs/option_map.cpp
|
||||
src/dwarfs/options.cpp
|
||||
src/dwarfs/os_access_posix.cpp
|
||||
src/dwarfs/os_access_generic.cpp
|
||||
src/dwarfs/progress.cpp
|
||||
src/dwarfs/scanner.cpp
|
||||
src/dwarfs/similarity.cpp
|
||||
|
@ -30,11 +30,10 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <folly/small_vector.h>
|
||||
|
||||
#include "dwarfs/entry_interface.h"
|
||||
#include "dwarfs/file_stat.h"
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
@ -68,16 +67,16 @@ class entry : public entry_interface {
|
||||
public:
|
||||
enum type_t { E_FILE, E_DIR, E_LINK, E_DEVICE, E_OTHER };
|
||||
|
||||
entry(const std::string& name, std::shared_ptr<entry> parent,
|
||||
const struct ::stat& st);
|
||||
entry(std::string const& name, std::shared_ptr<entry> parent,
|
||||
file_stat const& st);
|
||||
|
||||
bool has_parent() const;
|
||||
std::shared_ptr<entry> parent() const;
|
||||
void set_name(const std::string& name);
|
||||
void set_name(std::string const& name);
|
||||
std::string path() const override;
|
||||
std::string dpath() const override;
|
||||
const std::string& name() const override { return name_; }
|
||||
size_t size() const override { return stat_.st_size; }
|
||||
std::string const& name() const override { return name_; }
|
||||
size_t size() const override { return stat_.size; }
|
||||
virtual type_t type() const = 0;
|
||||
std::string type_string() const override;
|
||||
bool is_directory() const override;
|
||||
@ -88,11 +87,11 @@ class entry : public entry_interface {
|
||||
void update(global_entry_data& data) const;
|
||||
virtual void accept(entry_visitor& v, bool preorder = false) = 0;
|
||||
virtual void scan(os_access& os, progress& prog) = 0;
|
||||
const struct ::stat& status() const { return stat_; }
|
||||
file_stat const& status() const { return stat_; }
|
||||
void set_entry_index(uint32_t index) { entry_index_ = index; }
|
||||
std::optional<uint32_t> const& entry_index() const { return entry_index_; }
|
||||
uint64_t raw_inode_num() const { return stat_.st_ino; }
|
||||
uint64_t num_hard_links() const { return stat_.st_nlink; }
|
||||
uint64_t raw_inode_num() const { return stat_.ino; }
|
||||
uint64_t num_hard_links() const { return stat_.nlink; }
|
||||
virtual void set_inode_num(uint32_t ino) = 0;
|
||||
virtual std::optional<uint32_t> const& inode_num() const = 0;
|
||||
|
||||
@ -110,19 +109,19 @@ class entry : public entry_interface {
|
||||
uint64_t get_ctime() const override;
|
||||
void set_ctime(uint64_t ctime) override;
|
||||
|
||||
void override_size(size_t size) { stat_.st_size = size; }
|
||||
void override_size(size_t size) { stat_.size = size; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::weak_ptr<entry> parent_;
|
||||
struct ::stat stat_;
|
||||
file_stat stat_;
|
||||
std::optional<uint32_t> entry_index_;
|
||||
};
|
||||
|
||||
class file : public entry {
|
||||
public:
|
||||
file(const std::string& name, std::shared_ptr<entry> parent,
|
||||
const struct ::stat& st)
|
||||
file_stat const& st)
|
||||
: entry(name, std::move(parent), st) {}
|
||||
|
||||
type_t type() const override;
|
||||
|
101
include/dwarfs/file_stat.h
Normal file
101
include/dwarfs/file_stat.h
Normal file
@ -0,0 +1,101 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/**
|
||||
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||
*
|
||||
* This file is part of dwarfs.
|
||||
*
|
||||
* dwarfs 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "dwarfs/file_type.h"
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
struct file_stat {
|
||||
using perms_type = std::underlying_type_t<std::filesystem::perms>;
|
||||
using mode_type = uint32_t;
|
||||
using dev_type = uint64_t;
|
||||
using ino_type = uint64_t;
|
||||
using nlink_type = uint64_t;
|
||||
using uid_type = uint32_t;
|
||||
using gid_type = uint32_t;
|
||||
using off_type = int64_t;
|
||||
using blksize_type = int64_t;
|
||||
using blkcnt_type = int64_t;
|
||||
using time_type = int64_t;
|
||||
|
||||
std::filesystem::file_status status() const {
|
||||
return file_mode_to_status(mode);
|
||||
};
|
||||
|
||||
posix_file_type::value type() const {
|
||||
return static_cast<posix_file_type::value>(mode & posix_file_type::mask);
|
||||
};
|
||||
|
||||
perms_type permissions() const { return mode & 07777; };
|
||||
|
||||
void set_permissions(perms_type perms) { mode = type() | (perms & 07777); }
|
||||
|
||||
bool is_directory() const { return type() == posix_file_type::directory; }
|
||||
|
||||
bool is_regular_file() const { return type() == posix_file_type::regular; }
|
||||
|
||||
bool is_symlink() const { return type() == posix_file_type::symlink; }
|
||||
|
||||
bool is_device() const {
|
||||
auto t = type();
|
||||
return t == posix_file_type::block || t == posix_file_type::character;
|
||||
}
|
||||
|
||||
dev_type dev;
|
||||
ino_type ino;
|
||||
nlink_type nlink;
|
||||
mode_type mode;
|
||||
uid_type uid;
|
||||
gid_type gid;
|
||||
dev_type rdev;
|
||||
off_type size;
|
||||
blksize_type blksize;
|
||||
blkcnt_type blocks;
|
||||
time_type atime;
|
||||
time_type mtime;
|
||||
time_type ctime;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void copy_file_stat(T* out, file_stat const& in) {
|
||||
out->st_dev = in.dev;
|
||||
out->st_ino = in.ino;
|
||||
out->st_nlink = in.nlink;
|
||||
out->st_mode = in.mode;
|
||||
out->st_uid = in.uid;
|
||||
out->st_gid = in.gid;
|
||||
out->st_rdev = in.rdev;
|
||||
out->st_size = in.size;
|
||||
out->st_blksize = in.blksize;
|
||||
out->st_blocks = in.blocks;
|
||||
out->st_atime = in.atime;
|
||||
out->st_mtime = in.mtime;
|
||||
out->st_ctime = in.ctime;
|
||||
}
|
||||
|
||||
} // namespace dwarfs
|
45
include/dwarfs/file_type.h
Normal file
45
include/dwarfs/file_type.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/**
|
||||
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||
*
|
||||
* This file is part of dwarfs.
|
||||
*
|
||||
* dwarfs 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
struct posix_file_type {
|
||||
enum value : uint16_t {
|
||||
mask = 0170000,
|
||||
socket = 0140000,
|
||||
symlink = 0120000,
|
||||
regular = 0100000,
|
||||
block = 0060000,
|
||||
directory = 0040000,
|
||||
character = 0020000,
|
||||
fifo = 0010000,
|
||||
};
|
||||
};
|
||||
|
||||
std::filesystem::file_status file_mode_to_status(uint16_t mode);
|
||||
uint16_t file_status_to_mode(std::filesystem::file_status status);
|
||||
|
||||
} // namespace dwarfs
|
@ -32,23 +32,20 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <folly/Expected.h>
|
||||
#include <folly/dynamic.h>
|
||||
|
||||
#include "dwarfs/fstypes.h"
|
||||
#include "dwarfs/metadata_types.h"
|
||||
|
||||
struct stat;
|
||||
struct statvfs;
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
struct cache_tidy_config;
|
||||
struct filesystem_options;
|
||||
struct rewrite_options;
|
||||
struct iovec_read_buf;
|
||||
struct file_stat;
|
||||
struct vfs_stat;
|
||||
|
||||
class filesystem_writer;
|
||||
class logger;
|
||||
@ -107,7 +104,7 @@ class filesystem_v2 {
|
||||
return impl_->find(inode, name);
|
||||
}
|
||||
|
||||
int getattr(inode_view entry, struct ::stat* stbuf) const {
|
||||
int getattr(inode_view entry, file_stat* stbuf) const {
|
||||
return impl_->getattr(entry, stbuf);
|
||||
}
|
||||
|
||||
@ -134,7 +131,7 @@ class filesystem_v2 {
|
||||
return impl_->readlink(entry);
|
||||
}
|
||||
|
||||
int statvfs(struct ::statvfs* stbuf) const { return impl_->statvfs(stbuf); }
|
||||
int statvfs(vfs_stat* stbuf) const { return impl_->statvfs(stbuf); }
|
||||
|
||||
int open(inode_view entry) const { return impl_->open(entry); }
|
||||
|
||||
@ -176,7 +173,7 @@ class filesystem_v2 {
|
||||
virtual std::optional<inode_view> find(int inode) const = 0;
|
||||
virtual std::optional<inode_view>
|
||||
find(int inode, const char* name) const = 0;
|
||||
virtual int getattr(inode_view entry, struct ::stat* stbuf) const = 0;
|
||||
virtual int getattr(inode_view entry, file_stat* stbuf) const = 0;
|
||||
virtual int
|
||||
access(inode_view entry, int mode, uid_t uid, gid_t gid) const = 0;
|
||||
virtual std::optional<directory_view> opendir(inode_view entry) const = 0;
|
||||
@ -186,7 +183,7 @@ class filesystem_v2 {
|
||||
virtual int readlink(inode_view entry, std::string* buf) const = 0;
|
||||
virtual folly::Expected<std::string, int>
|
||||
readlink(inode_view entry) const = 0;
|
||||
virtual int statvfs(struct ::statvfs* stbuf) const = 0;
|
||||
virtual int statvfs(vfs_stat* stbuf) const = 0;
|
||||
virtual int open(inode_view entry) const = 0;
|
||||
virtual ssize_t
|
||||
read(uint32_t inode, char* buf, size_t size, off_t offset) const = 0;
|
||||
|
@ -32,23 +32,19 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <folly/Expected.h>
|
||||
#include <folly/dynamic.h>
|
||||
|
||||
#include "dwarfs/metadata_types.h"
|
||||
|
||||
struct stat;
|
||||
struct statvfs;
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
class logger;
|
||||
|
||||
struct metadata_options;
|
||||
|
||||
struct filesystem_info;
|
||||
struct file_stat;
|
||||
struct vfs_stat;
|
||||
|
||||
namespace thrift::metadata {
|
||||
class metadata;
|
||||
@ -98,7 +94,7 @@ class metadata_v2 {
|
||||
return impl_->find(inode, name);
|
||||
}
|
||||
|
||||
int getattr(inode_view iv, struct ::stat* stbuf) const {
|
||||
int getattr(inode_view iv, file_stat* stbuf) const {
|
||||
return impl_->getattr(iv, stbuf);
|
||||
}
|
||||
|
||||
@ -127,7 +123,7 @@ class metadata_v2 {
|
||||
return impl_->readlink(iv);
|
||||
}
|
||||
|
||||
int statvfs(struct ::statvfs* stbuf) const { return impl_->statvfs(stbuf); }
|
||||
int statvfs(vfs_stat* stbuf) const { return impl_->statvfs(stbuf); }
|
||||
|
||||
std::optional<chunk_range> get_chunks(int inode) const {
|
||||
return impl_->get_chunks(inode);
|
||||
@ -163,7 +159,7 @@ class metadata_v2 {
|
||||
virtual std::optional<inode_view>
|
||||
find(int inode, const char* name) const = 0;
|
||||
|
||||
virtual int getattr(inode_view iv, struct ::stat* stbuf) const = 0;
|
||||
virtual int getattr(inode_view iv, file_stat* stbuf) const = 0;
|
||||
|
||||
virtual std::optional<directory_view> opendir(inode_view iv) const = 0;
|
||||
|
||||
@ -180,7 +176,7 @@ class metadata_v2 {
|
||||
|
||||
virtual folly::Expected<std::string, int> readlink(inode_view iv) const = 0;
|
||||
|
||||
virtual int statvfs(struct ::statvfs* stbuf) const = 0;
|
||||
virtual int statvfs(vfs_stat* stbuf) const = 0;
|
||||
|
||||
virtual std::optional<chunk_range> get_chunks(int inode) const = 0;
|
||||
|
||||
|
@ -21,10 +21,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "dwarfs/file_stat.h"
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
@ -34,7 +35,7 @@ class dir_reader {
|
||||
public:
|
||||
virtual ~dir_reader() = default;
|
||||
|
||||
virtual bool read(std::string& name) const = 0;
|
||||
virtual bool read(std::string& name) = 0;
|
||||
};
|
||||
|
||||
class os_access {
|
||||
@ -42,11 +43,11 @@ class os_access {
|
||||
virtual ~os_access() = default;
|
||||
|
||||
virtual std::shared_ptr<dir_reader>
|
||||
opendir(const std::string& path) const = 0;
|
||||
virtual void lstat(const std::string& path, struct ::stat* st) const = 0;
|
||||
virtual std::string readlink(const std::string& path, size_t size) const = 0;
|
||||
opendir(std::string const& path) const = 0;
|
||||
virtual file_stat symlink_info(std::string const& path) const = 0;
|
||||
virtual std::string read_symlink(std::string const& path) const = 0;
|
||||
virtual std::shared_ptr<mmif>
|
||||
map_file(const std::string& path, size_t size) const = 0;
|
||||
virtual int access(const std::string& path, int mode) const = 0;
|
||||
map_file(std::string const& path, size_t size) const = 0;
|
||||
virtual int access(std::string const& path, int mode) const = 0;
|
||||
};
|
||||
} // namespace dwarfs
|
||||
|
@ -31,13 +31,13 @@ namespace dwarfs {
|
||||
|
||||
class mmif;
|
||||
|
||||
class os_access_posix : public os_access {
|
||||
class os_access_generic : public os_access {
|
||||
public:
|
||||
std::shared_ptr<dir_reader> opendir(const std::string& path) const override;
|
||||
void lstat(const std::string& path, struct ::stat* st) const override;
|
||||
std::string readlink(const std::string& path, size_t size) const override;
|
||||
std::shared_ptr<dir_reader> opendir(std::string const& path) const override;
|
||||
file_stat symlink_info(std::string const& path) const override;
|
||||
std::string read_symlink(std::string const& path) const override;
|
||||
std::shared_ptr<mmif>
|
||||
map_file(const std::string& path, size_t size) const override;
|
||||
int access(const std::string& path, int mode) const override;
|
||||
map_file(std::string const& path, size_t size) const override;
|
||||
int access(std::string const& path, int mode) const override;
|
||||
};
|
||||
} // namespace dwarfs
|
49
include/dwarfs/vfs_stat.h
Normal file
49
include/dwarfs/vfs_stat.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/**
|
||||
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||
*
|
||||
* This file is part of dwarfs.
|
||||
*
|
||||
* dwarfs 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
struct vfs_stat {
|
||||
using blkcnt_type = uint64_t;
|
||||
using filcnt_type = uint64_t;
|
||||
|
||||
uint64_t bsize;
|
||||
uint64_t frsize;
|
||||
blkcnt_type blocks;
|
||||
filcnt_type files;
|
||||
uint64_t namemax;
|
||||
bool readonly;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void copy_vfs_stat(T* out, vfs_stat const& in) {
|
||||
out->f_bsize = in.bsize;
|
||||
out->f_frsize = in.frsize;
|
||||
out->f_blocks = in.blocks;
|
||||
out->f_files = in.files;
|
||||
out->f_namemax = in.namemax;
|
||||
}
|
||||
|
||||
} // namespace dwarfs
|
@ -28,6 +28,7 @@
|
||||
#include "dwarfs/checksum.h"
|
||||
#include "dwarfs/entry.h"
|
||||
#include "dwarfs/error.h"
|
||||
#include "dwarfs/file_type.h"
|
||||
#include "dwarfs/global_entry_data.h"
|
||||
#include "dwarfs/inode.h"
|
||||
#include "dwarfs/mmif.h"
|
||||
@ -40,11 +41,11 @@
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
entry::entry(const std::string& name, std::shared_ptr<entry> parent,
|
||||
const struct ::stat& st)
|
||||
: name_(name)
|
||||
, parent_(std::move(parent))
|
||||
, stat_(st) {}
|
||||
entry::entry(std::string const& name, std::shared_ptr<entry> parent,
|
||||
file_stat const& st)
|
||||
: name_{name}
|
||||
, parent_{std::move(parent)}
|
||||
, stat_{st} {}
|
||||
|
||||
bool entry::has_parent() const {
|
||||
if (parent_.lock()) {
|
||||
@ -75,80 +76,79 @@ std::string entry::dpath() const {
|
||||
}
|
||||
|
||||
std::string entry::type_string() const {
|
||||
auto mode = stat_.st_mode;
|
||||
|
||||
if (S_ISREG(mode)) {
|
||||
switch (stat_.type()) {
|
||||
case posix_file_type::regular:
|
||||
return "file";
|
||||
} else if (S_ISDIR(mode)) {
|
||||
case posix_file_type::directory:
|
||||
return "directory";
|
||||
} else if (S_ISLNK(mode)) {
|
||||
case posix_file_type::symlink:
|
||||
return "link";
|
||||
} else if (S_ISCHR(mode)) {
|
||||
case posix_file_type::character:
|
||||
return "chardev";
|
||||
} else if (S_ISBLK(mode)) {
|
||||
case posix_file_type::block:
|
||||
return "blockdev";
|
||||
} else if (S_ISFIFO(mode)) {
|
||||
case posix_file_type::fifo:
|
||||
return "fifo";
|
||||
} else if (S_ISSOCK(mode)) {
|
||||
case posix_file_type::socket:
|
||||
return "socket";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
DWARFS_THROW(runtime_error, fmt::format("unknown file type: {:#06x}", mode));
|
||||
DWARFS_THROW(runtime_error, fmt::format("unknown file type: {:#06x}",
|
||||
fmt::underlying(stat_.type())));
|
||||
}
|
||||
|
||||
bool entry::is_directory() const { return S_ISDIR(stat_.st_mode); }
|
||||
bool entry::is_directory() const { return stat_.is_directory(); }
|
||||
|
||||
void entry::walk(std::function<void(entry*)> const& f) { f(this); }
|
||||
|
||||
void entry::walk(std::function<void(const entry*)> const& f) const { f(this); }
|
||||
|
||||
void entry::update(global_entry_data& data) const {
|
||||
data.add_uid(stat_.st_uid);
|
||||
data.add_gid(stat_.st_gid);
|
||||
data.add_mode(stat_.st_mode & 0xFFFF);
|
||||
data.add_atime(stat_.st_atime);
|
||||
data.add_mtime(stat_.st_mtime);
|
||||
data.add_ctime(stat_.st_ctime);
|
||||
data.add_uid(stat_.uid);
|
||||
data.add_gid(stat_.gid);
|
||||
data.add_mode(stat_.mode);
|
||||
data.add_atime(stat_.atime);
|
||||
data.add_mtime(stat_.mtime);
|
||||
data.add_ctime(stat_.ctime);
|
||||
}
|
||||
|
||||
void entry::pack(thrift::metadata::inode_data& entry_v2,
|
||||
global_entry_data const& data) const {
|
||||
entry_v2.mode_index() = data.get_mode_index(stat_.st_mode & 0xFFFF);
|
||||
entry_v2.owner_index() = data.get_uid_index(stat_.st_uid);
|
||||
entry_v2.group_index() = data.get_gid_index(stat_.st_gid);
|
||||
entry_v2.atime_offset() = data.get_atime_offset(stat_.st_atime);
|
||||
entry_v2.mtime_offset() = data.get_mtime_offset(stat_.st_mtime);
|
||||
entry_v2.ctime_offset() = data.get_ctime_offset(stat_.st_ctime);
|
||||
entry_v2.mode_index() = data.get_mode_index(stat_.mode);
|
||||
entry_v2.owner_index() = data.get_uid_index(stat_.uid);
|
||||
entry_v2.group_index() = data.get_gid_index(stat_.gid);
|
||||
entry_v2.atime_offset() = data.get_atime_offset(stat_.atime);
|
||||
entry_v2.mtime_offset() = data.get_mtime_offset(stat_.mtime);
|
||||
entry_v2.ctime_offset() = data.get_ctime_offset(stat_.ctime);
|
||||
}
|
||||
|
||||
entry::type_t file::type() const { return E_FILE; }
|
||||
|
||||
uint16_t entry::get_permissions() const { return stat_.st_mode & 07777; }
|
||||
uint16_t entry::get_permissions() const { return stat_.permissions(); }
|
||||
|
||||
void entry::set_permissions(uint16_t perm) {
|
||||
stat_.st_mode &= ~07777;
|
||||
stat_.st_mode |= perm;
|
||||
}
|
||||
void entry::set_permissions(uint16_t perm) { stat_.set_permissions(perm); }
|
||||
|
||||
uint16_t entry::get_uid() const { return stat_.st_uid; }
|
||||
uint16_t entry::get_uid() const { return stat_.uid; }
|
||||
|
||||
void entry::set_uid(uint16_t uid) { stat_.st_uid = uid; }
|
||||
void entry::set_uid(uint16_t uid) { stat_.uid = uid; }
|
||||
|
||||
uint16_t entry::get_gid() const { return stat_.st_gid; }
|
||||
uint16_t entry::get_gid() const { return stat_.gid; }
|
||||
|
||||
void entry::set_gid(uint16_t gid) { stat_.st_gid = gid; }
|
||||
void entry::set_gid(uint16_t gid) { stat_.gid = gid; }
|
||||
|
||||
uint64_t entry::get_atime() const { return stat_.st_atime; }
|
||||
uint64_t entry::get_atime() const { return stat_.atime; }
|
||||
|
||||
void entry::set_atime(uint64_t atime) { stat_.st_atime = atime; }
|
||||
void entry::set_atime(uint64_t atime) { stat_.atime = atime; }
|
||||
|
||||
uint64_t entry::get_mtime() const { return stat_.st_mtime; }
|
||||
uint64_t entry::get_mtime() const { return stat_.mtime; }
|
||||
|
||||
void entry::set_mtime(uint64_t mtime) { stat_.st_atime = mtime; }
|
||||
void entry::set_mtime(uint64_t mtime) { stat_.mtime = mtime; }
|
||||
|
||||
uint64_t entry::get_ctime() const { return stat_.st_ctime; }
|
||||
uint64_t entry::get_ctime() const { return stat_.ctime; }
|
||||
|
||||
void entry::set_ctime(uint64_t ctime) { stat_.st_atime = ctime; }
|
||||
void entry::set_ctime(uint64_t ctime) { stat_.ctime = ctime; }
|
||||
|
||||
std::string_view file::hash() const {
|
||||
auto& h = data_->hash;
|
||||
@ -374,43 +374,54 @@ const std::string& link::linkname() const { return link_; }
|
||||
void link::accept(entry_visitor& v, bool) { v.visit(this); }
|
||||
|
||||
void link::scan(os_access& os, progress& prog) {
|
||||
link_ = os.readlink(path(), size());
|
||||
link_ = os.read_symlink(path());
|
||||
prog.original_size += size();
|
||||
prog.symlink_size += size();
|
||||
}
|
||||
|
||||
entry::type_t device::type() const {
|
||||
auto mode = status().st_mode;
|
||||
return S_ISCHR(mode) || S_ISBLK(mode) ? E_DEVICE : E_OTHER;
|
||||
switch (status().type()) {
|
||||
case posix_file_type::character:
|
||||
case posix_file_type::block:
|
||||
return E_DEVICE;
|
||||
default:
|
||||
return E_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
void device::accept(entry_visitor& v, bool) { v.visit(this); }
|
||||
|
||||
void device::scan(os_access&, progress&) {}
|
||||
|
||||
uint64_t device::device_id() const { return status().st_rdev; }
|
||||
uint64_t device::device_id() const { return status().rdev; }
|
||||
|
||||
class entry_factory_ : public entry_factory {
|
||||
public:
|
||||
std::shared_ptr<entry> create(os_access& os, const std::string& name,
|
||||
std::shared_ptr<entry> parent) override {
|
||||
const std::string& p = parent ? parent->path() + "/" + name : name;
|
||||
struct ::stat st;
|
||||
// TODO: std::filesystem::path
|
||||
std::string const& p = parent ? parent->path() + "/" + name : name;
|
||||
|
||||
os.lstat(p, &st);
|
||||
auto mode = st.st_mode;
|
||||
auto st = os.symlink_info(p);
|
||||
|
||||
if (S_ISREG(mode)) {
|
||||
switch (st.type()) {
|
||||
case posix_file_type::regular:
|
||||
return std::make_shared<file>(name, std::move(parent), st);
|
||||
} else if (S_ISDIR(mode)) {
|
||||
|
||||
case posix_file_type::directory:
|
||||
return std::make_shared<dir>(name, std::move(parent), st);
|
||||
} else if (S_ISLNK(mode)) {
|
||||
|
||||
case posix_file_type::symlink:
|
||||
return std::make_shared<link>(name, std::move(parent), st);
|
||||
} else if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) ||
|
||||
S_ISSOCK(mode)) {
|
||||
|
||||
case posix_file_type::character:
|
||||
case posix_file_type::block:
|
||||
case posix_file_type::fifo:
|
||||
case posix_file_type::socket:
|
||||
return std::make_shared<device>(name, std::move(parent), st);
|
||||
} else {
|
||||
default:
|
||||
// TODO: warn
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
98
src/dwarfs/file_type.cpp
Normal file
98
src/dwarfs/file_type.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/**
|
||||
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||
*
|
||||
* This file is part of dwarfs.
|
||||
*
|
||||
* dwarfs 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "dwarfs/file_type.h"
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
fs::file_status file_mode_to_status(uint16_t mode) {
|
||||
fs::file_type ft;
|
||||
|
||||
switch (mode & posix_file_type::mask) {
|
||||
case posix_file_type::socket:
|
||||
ft = fs::file_type::socket;
|
||||
break;
|
||||
case posix_file_type::symlink:
|
||||
ft = fs::file_type::symlink;
|
||||
break;
|
||||
case posix_file_type::regular:
|
||||
ft = fs::file_type::regular;
|
||||
break;
|
||||
case posix_file_type::block:
|
||||
ft = fs::file_type::block;
|
||||
break;
|
||||
case posix_file_type::directory:
|
||||
ft = fs::file_type::directory;
|
||||
break;
|
||||
case posix_file_type::character:
|
||||
ft = fs::file_type::character;
|
||||
break;
|
||||
case posix_file_type::fifo:
|
||||
ft = fs::file_type::fifo;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(fmt::format("invalid file mode: {:#06x}", mode));
|
||||
break;
|
||||
}
|
||||
|
||||
return fs::file_status(ft, fs::perms(mode & 07777));
|
||||
}
|
||||
|
||||
uint16_t file_status_to_mode(std::filesystem::file_status status) {
|
||||
posix_file_type::value ft;
|
||||
|
||||
switch (status.type()) {
|
||||
case fs::file_type::socket:
|
||||
ft = posix_file_type::socket;
|
||||
break;
|
||||
case fs::file_type::symlink:
|
||||
ft = posix_file_type::symlink;
|
||||
break;
|
||||
case fs::file_type::regular:
|
||||
ft = posix_file_type::regular;
|
||||
break;
|
||||
case fs::file_type::block:
|
||||
ft = posix_file_type::block;
|
||||
break;
|
||||
case fs::file_type::directory:
|
||||
ft = posix_file_type::directory;
|
||||
break;
|
||||
case fs::file_type::character:
|
||||
ft = posix_file_type::character;
|
||||
break;
|
||||
case fs::file_type::fifo:
|
||||
ft = posix_file_type::fifo;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
fmt::format("invalid file type: {}", fmt::underlying(status.type())));
|
||||
break;
|
||||
}
|
||||
|
||||
return static_cast<uint16_t>(ft) |
|
||||
static_cast<uint16_t>(status.permissions());
|
||||
}
|
||||
|
||||
} // namespace dwarfs
|
@ -25,9 +25,6 @@
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <sys/statvfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
@ -35,11 +32,13 @@
|
||||
#include <folly/ScopeGuard.h>
|
||||
#include <folly/system/ThreadName.h>
|
||||
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/filesystem_extractor.h"
|
||||
#include "dwarfs/filesystem_v2.h"
|
||||
#include "dwarfs/fstypes.h"
|
||||
#include "dwarfs/logger.h"
|
||||
#include "dwarfs/options.h"
|
||||
#include "dwarfs/vfs_stat.h"
|
||||
#include "dwarfs/worker_group.h"
|
||||
|
||||
namespace dwarfs {
|
||||
@ -230,13 +229,13 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
|
||||
|
||||
sem.post(opts.max_queued_bytes);
|
||||
|
||||
struct ::statvfs vfs;
|
||||
vfs_stat vfs;
|
||||
fs.statvfs(&vfs);
|
||||
|
||||
std::atomic<size_t> hard_error{0};
|
||||
std::atomic<size_t> soft_error{0};
|
||||
std::atomic<uint64_t> bytes_written{0};
|
||||
uint64_t const bytes_total{vfs.f_blocks};
|
||||
uint64_t const bytes_total{vfs.blocks};
|
||||
|
||||
auto do_archive = [&](::archive_entry* ae,
|
||||
inode_view entry) { // TODO: inode vs. entry
|
||||
@ -322,14 +321,19 @@ bool filesystem_extractor_<LoggerPolicy>::extract(
|
||||
auto inode = entry.inode();
|
||||
|
||||
auto ae = ::archive_entry_new();
|
||||
struct ::stat stbuf;
|
||||
file_stat stbuf;
|
||||
|
||||
if (fs.getattr(inode, &stbuf) != 0) {
|
||||
DWARFS_THROW(runtime_error, "getattr() failed");
|
||||
}
|
||||
|
||||
struct ::stat st;
|
||||
|
||||
::memset(&st, 0, sizeof(st));
|
||||
copy_file_stat(&st, stbuf);
|
||||
|
||||
::archive_entry_set_pathname(ae, entry.path().c_str());
|
||||
::archive_entry_copy_stat(ae, &stbuf);
|
||||
::archive_entry_copy_stat(ae, &st);
|
||||
|
||||
if (S_ISLNK(inode.mode())) {
|
||||
std::string link;
|
||||
|
@ -25,9 +25,6 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "dwarfs/block_cache.h"
|
||||
@ -328,7 +325,7 @@ class filesystem_ final : public filesystem_v2::impl {
|
||||
std::optional<inode_view> find(const char* path) const override;
|
||||
std::optional<inode_view> find(int inode) const override;
|
||||
std::optional<inode_view> find(int inode, const char* name) const override;
|
||||
int getattr(inode_view entry, struct ::stat* stbuf) const override;
|
||||
int getattr(inode_view entry, file_stat* stbuf) const override;
|
||||
int access(inode_view entry, int mode, uid_t uid, gid_t gid) const override;
|
||||
std::optional<directory_view> opendir(inode_view entry) const override;
|
||||
std::optional<std::pair<inode_view, std::string>>
|
||||
@ -336,7 +333,7 @@ class filesystem_ final : public filesystem_v2::impl {
|
||||
size_t dirsize(directory_view dir) const override;
|
||||
int readlink(inode_view entry, std::string* buf) const override;
|
||||
folly::Expected<std::string, int> readlink(inode_view entry) const override;
|
||||
int statvfs(struct ::statvfs* stbuf) const override;
|
||||
int statvfs(vfs_stat* stbuf) const override;
|
||||
int open(inode_view entry) const override;
|
||||
ssize_t
|
||||
read(uint32_t inode, char* buf, size_t size, off_t offset) const override;
|
||||
@ -494,7 +491,7 @@ filesystem_<LoggerPolicy>::find(int inode, const char* name) const {
|
||||
|
||||
template <typename LoggerPolicy>
|
||||
int filesystem_<LoggerPolicy>::getattr(inode_view entry,
|
||||
struct ::stat* stbuf) const {
|
||||
file_stat* stbuf) const {
|
||||
return meta_.getattr(entry, stbuf);
|
||||
}
|
||||
|
||||
@ -534,7 +531,7 @@ filesystem_<LoggerPolicy>::readlink(inode_view entry) const {
|
||||
}
|
||||
|
||||
template <typename LoggerPolicy>
|
||||
int filesystem_<LoggerPolicy>::statvfs(struct ::statvfs* stbuf) const {
|
||||
int filesystem_<LoggerPolicy>::statvfs(vfs_stat* stbuf) const {
|
||||
// TODO: not sure if that's the right abstraction...
|
||||
return meta_.statvfs(stbuf);
|
||||
}
|
||||
|
@ -47,12 +47,14 @@
|
||||
#include <fsst.h>
|
||||
|
||||
#include "dwarfs/error.h"
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/fstypes.h"
|
||||
#include "dwarfs/logger.h"
|
||||
#include "dwarfs/metadata_v2.h"
|
||||
#include "dwarfs/options.h"
|
||||
#include "dwarfs/string_table.h"
|
||||
#include "dwarfs/util.h"
|
||||
#include "dwarfs/vfs_stat.h"
|
||||
|
||||
#include "dwarfs/gen-cpp2/metadata_layouts.h"
|
||||
#include "dwarfs/gen-cpp2/metadata_types_custom_protocol.h"
|
||||
@ -403,7 +405,7 @@ class metadata_ final : public metadata_v2::impl {
|
||||
std::optional<inode_view> find(int inode) const override;
|
||||
std::optional<inode_view> find(int inode, const char* name) const override;
|
||||
|
||||
int getattr(inode_view iv, struct ::stat* stbuf) const override;
|
||||
int getattr(inode_view iv, file_stat* stbuf) const override;
|
||||
|
||||
std::optional<directory_view> opendir(inode_view iv) const override;
|
||||
|
||||
@ -422,7 +424,7 @@ class metadata_ final : public metadata_v2::impl {
|
||||
|
||||
folly::Expected<std::string, int> readlink(inode_view iv) const override;
|
||||
|
||||
int statvfs(struct ::statvfs* stbuf) const override;
|
||||
int statvfs(vfs_stat* stbuf) const override;
|
||||
|
||||
std::optional<chunk_range> get_chunks(int inode) const override;
|
||||
|
||||
@ -799,7 +801,7 @@ template <typename LoggerPolicy>
|
||||
void metadata_<LoggerPolicy>::dump(
|
||||
std::ostream& os, int detail_level, filesystem_info const& fsinfo,
|
||||
std::function<void(const std::string&, uint32_t)> const& icb) const {
|
||||
struct ::statvfs stbuf;
|
||||
vfs_stat stbuf;
|
||||
statvfs(&stbuf);
|
||||
|
||||
if (auto version = meta_.dwarfs_version()) {
|
||||
@ -815,10 +817,10 @@ void metadata_<LoggerPolicy>::dump(
|
||||
}
|
||||
|
||||
if (detail_level > 0) {
|
||||
os << "block size: " << size_with_unit(stbuf.f_bsize) << std::endl;
|
||||
os << "block size: " << size_with_unit(stbuf.bsize) << std::endl;
|
||||
os << "block count: " << fsinfo.block_count << std::endl;
|
||||
os << "inode count: " << stbuf.f_files << std::endl;
|
||||
os << "original filesystem size: " << size_with_unit(stbuf.f_blocks)
|
||||
os << "inode count: " << stbuf.files << std::endl;
|
||||
os << "original filesystem size: " << size_with_unit(stbuf.blocks)
|
||||
<< std::endl;
|
||||
os << "compressed block size: "
|
||||
<< size_with_unit(fsinfo.compressed_block_size)
|
||||
@ -966,11 +968,11 @@ template <typename LoggerPolicy>
|
||||
folly::dynamic metadata_<LoggerPolicy>::as_dynamic() const {
|
||||
folly::dynamic obj = folly::dynamic::object;
|
||||
|
||||
struct ::statvfs stbuf;
|
||||
vfs_stat stbuf;
|
||||
statvfs(&stbuf);
|
||||
|
||||
obj["statvfs"] = folly::dynamic::object("f_bsize", stbuf.f_bsize)(
|
||||
"f_files", stbuf.f_files)("f_blocks", stbuf.f_blocks);
|
||||
obj["statvfs"] = folly::dynamic::object("f_bsize", stbuf.bsize)(
|
||||
"f_files", stbuf.files)("f_blocks", stbuf.blocks);
|
||||
|
||||
obj["root"] = as_dynamic(root_);
|
||||
|
||||
@ -1211,8 +1213,7 @@ metadata_<LoggerPolicy>::find(int inode, const char* name) const {
|
||||
}
|
||||
|
||||
template <typename LoggerPolicy>
|
||||
int metadata_<LoggerPolicy>::getattr(inode_view iv,
|
||||
struct ::stat* stbuf) const {
|
||||
int metadata_<LoggerPolicy>::getattr(inode_view iv, file_stat* stbuf) const {
|
||||
::memset(stbuf, 0, sizeof(*stbuf));
|
||||
|
||||
auto mode = iv.mode();
|
||||
@ -1227,31 +1228,32 @@ int metadata_<LoggerPolicy>::getattr(inode_view iv,
|
||||
}
|
||||
}
|
||||
|
||||
stbuf->st_mode = mode;
|
||||
stbuf->mode = mode;
|
||||
|
||||
if (options_.readonly) {
|
||||
stbuf->st_mode &= READ_ONLY_MASK;
|
||||
stbuf->mode &= READ_ONLY_MASK;
|
||||
}
|
||||
|
||||
stbuf->st_size = S_ISDIR(mode) ? make_directory_view(iv).entry_count()
|
||||
: file_size(iv, mode);
|
||||
stbuf->st_ino = inode + inode_offset_;
|
||||
stbuf->st_blocks = (stbuf->st_size + 511) / 512;
|
||||
stbuf->st_uid = iv.getuid();
|
||||
stbuf->st_gid = iv.getgid();
|
||||
stbuf->st_mtime = resolution * (timebase + iv.mtime_offset());
|
||||
stbuf->size = stbuf->is_directory() ? make_directory_view(iv).entry_count()
|
||||
: file_size(iv, mode);
|
||||
stbuf->ino = inode + inode_offset_;
|
||||
stbuf->blksize = 512;
|
||||
stbuf->blocks = (stbuf->size + 511) / 512;
|
||||
stbuf->uid = iv.getuid();
|
||||
stbuf->gid = iv.getgid();
|
||||
stbuf->mtime = resolution * (timebase + iv.mtime_offset());
|
||||
if (mtime_only) {
|
||||
stbuf->st_atime = stbuf->st_ctime = stbuf->st_mtime;
|
||||
stbuf->atime = stbuf->ctime = stbuf->mtime;
|
||||
} else {
|
||||
stbuf->st_atime = resolution * (timebase + iv.atime_offset());
|
||||
stbuf->st_ctime = resolution * (timebase + iv.ctime_offset());
|
||||
stbuf->atime = resolution * (timebase + iv.atime_offset());
|
||||
stbuf->ctime = resolution * (timebase + iv.ctime_offset());
|
||||
}
|
||||
stbuf->st_nlink = options_.enable_nlink && S_ISREG(mode)
|
||||
? DWARFS_NOTHROW(nlinks_.at(inode - file_inode_offset_))
|
||||
: 1;
|
||||
stbuf->nlink = options_.enable_nlink && stbuf->is_regular_file()
|
||||
? DWARFS_NOTHROW(nlinks_.at(inode - file_inode_offset_))
|
||||
: 1;
|
||||
|
||||
if (S_ISBLK(mode) || S_ISCHR(mode)) {
|
||||
stbuf->st_rdev = get_device_id(inode);
|
||||
if (stbuf->is_device()) {
|
||||
stbuf->rdev = get_device_id(inode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1358,20 +1360,20 @@ metadata_<LoggerPolicy>::readlink(inode_view iv) const {
|
||||
}
|
||||
|
||||
template <typename LoggerPolicy>
|
||||
int metadata_<LoggerPolicy>::statvfs(struct ::statvfs* stbuf) const {
|
||||
int metadata_<LoggerPolicy>::statvfs(vfs_stat* stbuf) const {
|
||||
::memset(stbuf, 0, sizeof(*stbuf));
|
||||
|
||||
stbuf->f_bsize = meta_.block_size();
|
||||
stbuf->f_frsize = 1UL;
|
||||
stbuf->f_blocks = meta_.total_fs_size();
|
||||
stbuf->bsize = meta_.block_size();
|
||||
stbuf->frsize = 1UL;
|
||||
stbuf->blocks = meta_.total_fs_size();
|
||||
if (!options_.enable_nlink) {
|
||||
if (auto ths = meta_.total_hardlink_size()) {
|
||||
stbuf->f_blocks += *ths;
|
||||
stbuf->blocks += *ths;
|
||||
}
|
||||
}
|
||||
stbuf->f_files = inode_count_;
|
||||
stbuf->f_flag = ST_RDONLY;
|
||||
stbuf->f_namemax = PATH_MAX;
|
||||
stbuf->files = inode_count_;
|
||||
stbuf->readonly = true;
|
||||
stbuf->namemax = PATH_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
182
src/dwarfs/os_access_generic.cpp
Normal file
182
src/dwarfs/os_access_generic.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/**
|
||||
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||
*
|
||||
* This file is part of dwarfs.
|
||||
*
|
||||
* dwarfs 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <folly/portability/Windows.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "dwarfs/error.h"
|
||||
#include "dwarfs/mmap.h"
|
||||
#include "dwarfs/os_access_generic.h"
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
uint64_t time_from_filetime(FILETIME const& ft) {
|
||||
static constexpr uint64_t FT_TICKS_PER_SECOND = UINT64_C(10000000);
|
||||
static constexpr uint64_t FT_EPOCH_OFFSET = UINT64_C(11644473600);
|
||||
uint64_t ticks =
|
||||
(static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||
return (ticks / FT_TICKS_PER_SECOND) - FT_EPOCH_OFFSET;
|
||||
}
|
||||
|
||||
file_stat make_file_stat(std::string const& path) {
|
||||
auto status = fs::symlink_status(path);
|
||||
|
||||
file_stat rv;
|
||||
rv.mode = file_status_to_mode(status);
|
||||
|
||||
if (status.type() == fs::file_type::symlink) {
|
||||
::WIN32_FILE_ATTRIBUTE_DATA info;
|
||||
if (::GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &info) ==
|
||||
0) {
|
||||
throw std::system_error(::GetLastError(), std::system_category(),
|
||||
"GetFileAttributesExA");
|
||||
}
|
||||
rv.dev = 0;
|
||||
rv.ino = 0;
|
||||
rv.nlink = 0;
|
||||
rv.uid = 0;
|
||||
rv.gid = 0;
|
||||
rv.rdev = 0;
|
||||
rv.size =
|
||||
(static_cast<uint64_t>(info.nFileSizeHigh) << 32) + info.nFileSizeLow;
|
||||
rv.blksize = 0;
|
||||
rv.blocks = 0;
|
||||
rv.atime = time_from_filetime(info.ftLastAccessTime);
|
||||
rv.mtime = time_from_filetime(info.ftLastWriteTime);
|
||||
rv.ctime = time_from_filetime(info.ftCreationTime);
|
||||
} else {
|
||||
struct ::__stat64 st;
|
||||
if (::_stat64(path.c_str(), &st) != 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "_stat64");
|
||||
}
|
||||
rv.dev = st.st_dev;
|
||||
rv.ino = st.st_ino;
|
||||
rv.nlink = st.st_nlink;
|
||||
rv.uid = st.st_uid;
|
||||
rv.gid = st.st_gid;
|
||||
rv.rdev = st.st_rdev;
|
||||
rv.size = st.st_size;
|
||||
rv.blksize = st.st_blksize;
|
||||
rv.blocks = st.st_blocks;
|
||||
rv.atime = st.st_atime;
|
||||
rv.mtime = st.st_mtime;
|
||||
rv.ctime = st.st_ctime;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
file_stat make_file_stat(std::string const& path) {
|
||||
struct ::stat st;
|
||||
|
||||
if (::lstat(path.c_str(), &st) != 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "lstat");
|
||||
}
|
||||
|
||||
file_stat rv;
|
||||
rv.dev = st.st_dev;
|
||||
rv.ino = st.st_ino;
|
||||
rv.nlink = st.st_nlink;
|
||||
rv.mode = st.st_mode;
|
||||
rv.uid = st.st_uid;
|
||||
rv.gid = st.st_gid;
|
||||
rv.rdev = st.st_rdev;
|
||||
rv.size = st.st_size;
|
||||
rv.blksize = st.st_blksize;
|
||||
rv.blocks = st.st_blocks;
|
||||
rv.atime = st.st_atim.tv_sec;
|
||||
rv.mtime = st.st_mtim.tv_sec;
|
||||
rv.ctime = st.st_ctim.tv_sec;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class generic_dir_reader final : public dir_reader {
|
||||
public:
|
||||
explicit generic_dir_reader(const std::string& path)
|
||||
: it_(fs::directory_iterator(path)) {}
|
||||
|
||||
bool read(std::string& name) override {
|
||||
if (it_ != fs::directory_iterator()) {
|
||||
name.assign(it_->path().filename().string());
|
||||
++it_;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
fs::directory_iterator it_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<dir_reader>
|
||||
os_access_generic::opendir(std::string const& path) const {
|
||||
return std::make_shared<generic_dir_reader>(path);
|
||||
}
|
||||
|
||||
file_stat os_access_generic::symlink_info(std::string const& path) const {
|
||||
return make_file_stat(path);
|
||||
}
|
||||
|
||||
std::string os_access_generic::read_symlink(std::string const& path) const {
|
||||
return fs::read_symlink(path).string();
|
||||
}
|
||||
|
||||
std::shared_ptr<mmif>
|
||||
os_access_generic::map_file(std::string const& path, size_t size) const {
|
||||
return std::make_shared<mmap>(path, size);
|
||||
}
|
||||
|
||||
int os_access_generic::access(std::string const& path, int mode) const {
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
return ::access(path.c_str(), mode);
|
||||
#endif
|
||||
}
|
||||
} // namespace dwarfs
|
@ -1,104 +0,0 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/**
|
||||
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||
*
|
||||
* This file is part of dwarfs.
|
||||
*
|
||||
* dwarfs 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dwarfs 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 dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "dwarfs/error.h"
|
||||
#include "dwarfs/mmap.h"
|
||||
#include "dwarfs/os_access_posix.h"
|
||||
|
||||
namespace dwarfs {
|
||||
|
||||
class posix_dir_reader final : public dir_reader {
|
||||
public:
|
||||
explicit posix_dir_reader(const std::string& path)
|
||||
: dir_(::opendir(path.c_str())) {
|
||||
if (!dir_) {
|
||||
DWARFS_THROW(system_error, fmt::format("opendir('{}')", path));
|
||||
}
|
||||
}
|
||||
|
||||
~posix_dir_reader() noexcept override {
|
||||
if (dir_) {
|
||||
::closedir(dir_);
|
||||
}
|
||||
}
|
||||
|
||||
bool read(std::string& name) const override {
|
||||
errno = 0;
|
||||
|
||||
auto ent = readdir(dir_);
|
||||
|
||||
if (ent) {
|
||||
name.assign(ent->d_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errno == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWARFS_THROW(system_error, "readdir");
|
||||
}
|
||||
|
||||
private:
|
||||
DIR* dir_;
|
||||
};
|
||||
|
||||
std::shared_ptr<dir_reader>
|
||||
os_access_posix::opendir(const std::string& path) const {
|
||||
return std::make_shared<posix_dir_reader>(path);
|
||||
}
|
||||
|
||||
void os_access_posix::lstat(const std::string& path, struct ::stat* st) const {
|
||||
if (::lstat(path.c_str(), st) == -1) {
|
||||
DWARFS_THROW(system_error, fmt::format("lstat('{}')", path));
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
os_access_posix::readlink(const std::string& path, size_t size) const {
|
||||
std::vector<char> linkname(size);
|
||||
ssize_t rv = ::readlink(path.c_str(), &linkname[0], size);
|
||||
|
||||
if (rv == static_cast<ssize_t>(size)) {
|
||||
return std::string(linkname.begin(), linkname.end());
|
||||
}
|
||||
|
||||
DWARFS_THROW(system_error, fmt::format("readlink('{}')", path));
|
||||
}
|
||||
|
||||
std::shared_ptr<mmif>
|
||||
os_access_posix::map_file(const std::string& path, size_t size) const {
|
||||
return std::make_shared<mmap>(path, size);
|
||||
}
|
||||
|
||||
int os_access_posix::access(const std::string& path, int mode) const {
|
||||
return ::access(path.c_str(), mode);
|
||||
}
|
||||
} // namespace dwarfs
|
@ -44,6 +44,7 @@
|
||||
#endif
|
||||
|
||||
#include "dwarfs/error.h"
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/filesystem_v2.h"
|
||||
#include "dwarfs/fstypes.h"
|
||||
#include "dwarfs/logger.h"
|
||||
@ -53,6 +54,7 @@
|
||||
#include "dwarfs/tool.h"
|
||||
#include "dwarfs/util.h"
|
||||
#include "dwarfs/version.h"
|
||||
#include "dwarfs/vfs_stat.h"
|
||||
#include "dwarfs_tool_main.h"
|
||||
|
||||
namespace dwarfs {
|
||||
@ -159,11 +161,14 @@ void op_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
|
||||
auto entry = userdata->fs.find(parent, name);
|
||||
|
||||
if (entry) {
|
||||
struct ::fuse_entry_param e;
|
||||
file_stat stbuf;
|
||||
|
||||
err = userdata->fs.getattr(*entry, &e.attr);
|
||||
err = userdata->fs.getattr(*entry, &stbuf);
|
||||
|
||||
if (err == 0) {
|
||||
struct ::fuse_entry_param e;
|
||||
|
||||
copy_file_stat(&e.attr, stbuf);
|
||||
e.generation = 1;
|
||||
e.ino = e.attr.st_ino;
|
||||
e.attr_timeout = std::numeric_limits<double>::max();
|
||||
@ -199,12 +204,17 @@ void op_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info*) {
|
||||
auto entry = userdata->fs.find(ino);
|
||||
|
||||
if (entry) {
|
||||
struct ::stat stbuf;
|
||||
file_stat stbuf;
|
||||
|
||||
err = userdata->fs.getattr(*entry, &stbuf);
|
||||
|
||||
if (err == 0) {
|
||||
fuse_reply_attr(req, &stbuf, std::numeric_limits<double>::max());
|
||||
struct ::stat st;
|
||||
|
||||
::memset(&st, 0, sizeof(st));
|
||||
copy_file_stat(&st, stbuf);
|
||||
|
||||
fuse_reply_attr(req, &st, std::numeric_limits<double>::max());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -380,10 +390,13 @@ void op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
|
||||
if (dir) {
|
||||
off_t lastoff = userdata->fs.dirsize(*dir);
|
||||
struct stat stbuf;
|
||||
file_stat stbuf;
|
||||
struct ::stat st;
|
||||
std::vector<char> buf(size);
|
||||
size_t written = 0;
|
||||
|
||||
::memset(&st, 0, sizeof(st));
|
||||
|
||||
while (off < lastoff && written < size) {
|
||||
auto res = userdata->fs.readdir(*dir, off);
|
||||
assert(res);
|
||||
@ -392,12 +405,13 @@ void op_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
std::string name(name_view);
|
||||
|
||||
userdata->fs.getattr(entry, &stbuf);
|
||||
copy_file_stat(&st, stbuf);
|
||||
|
||||
assert(written < buf.size());
|
||||
|
||||
size_t needed =
|
||||
fuse_add_direntry(req, &buf[written], buf.size() - written,
|
||||
name.c_str(), &stbuf, off + 1);
|
||||
name.c_str(), &st, off + 1);
|
||||
|
||||
if (written + needed > buf.size()) {
|
||||
break;
|
||||
@ -435,12 +449,21 @@ void op_statfs(fuse_req_t req, fuse_ino_t /*ino*/) {
|
||||
int err = EIO;
|
||||
|
||||
try {
|
||||
struct ::statvfs buf;
|
||||
vfs_stat stbuf;
|
||||
|
||||
err = userdata->fs.statvfs(&buf);
|
||||
err = userdata->fs.statvfs(&stbuf);
|
||||
|
||||
if (err == 0) {
|
||||
fuse_reply_statfs(req, &buf);
|
||||
struct ::statvfs st;
|
||||
|
||||
::memset(&st, 0, sizeof(st));
|
||||
copy_vfs_stat(&st, stbuf);
|
||||
|
||||
if (stbuf.readonly) {
|
||||
st.f_flag |= ST_RDONLY;
|
||||
}
|
||||
|
||||
fuse_reply_statfs(req, &st);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/String.h>
|
||||
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/filesystem_v2.h"
|
||||
#include "dwarfs/fstypes.h"
|
||||
#include "dwarfs/logger.h"
|
||||
@ -109,9 +110,9 @@ int dwarfsbench_main(int argc, char** argv) {
|
||||
if (S_ISREG(inode_data.mode())) {
|
||||
wg.add_job([&fs, inode_data] {
|
||||
try {
|
||||
struct ::stat stbuf;
|
||||
file_stat stbuf;
|
||||
if (fs.getattr(inode_data, &stbuf) == 0) {
|
||||
std::vector<char> buf(stbuf.st_size);
|
||||
std::vector<char> buf(stbuf.size);
|
||||
int fh = fs.open(inode_data);
|
||||
fs.read(fh, buf.data(), buf.size());
|
||||
}
|
||||
|
@ -66,7 +66,7 @@
|
||||
#include "dwarfs/mmap.h"
|
||||
#include "dwarfs/options.h"
|
||||
#include "dwarfs/options_interface.h"
|
||||
#include "dwarfs/os_access_posix.h"
|
||||
#include "dwarfs/os_access_generic.h"
|
||||
#include "dwarfs/progress.h"
|
||||
#include "dwarfs/scanner.h"
|
||||
#include "dwarfs/script.h"
|
||||
@ -1076,7 +1076,7 @@ int mkdwarfs_main(int argc, char** argv) {
|
||||
options.file_order.mode == file_order_mode::NILSIMSA;
|
||||
|
||||
scanner s(lgr, wg_scanner, cfg, entry_factory::create(),
|
||||
std::make_shared<os_access_posix>(), std::move(script),
|
||||
std::make_shared<os_access_generic>(), std::move(script),
|
||||
options);
|
||||
|
||||
if (input_list) {
|
||||
|
214
test/dwarfs.cpp
214
test/dwarfs.cpp
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
@ -36,6 +37,8 @@
|
||||
#include "dwarfs/block_compressor.h"
|
||||
#include "dwarfs/builtin_script.h"
|
||||
#include "dwarfs/entry.h"
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/file_type.h"
|
||||
#include "dwarfs/filesystem_v2.h"
|
||||
#include "dwarfs/filesystem_writer.h"
|
||||
#include "dwarfs/logger.h"
|
||||
@ -43,6 +46,7 @@
|
||||
#include "dwarfs/options.h"
|
||||
#include "dwarfs/progress.h"
|
||||
#include "dwarfs/scanner.h"
|
||||
#include "dwarfs/vfs_stat.h"
|
||||
|
||||
#include "filter_test_data.h"
|
||||
#include "loremipsum.h"
|
||||
@ -51,6 +55,8 @@
|
||||
|
||||
using namespace dwarfs;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string const default_file_hash_algo{"xxh3-128"};
|
||||
@ -199,19 +205,19 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
|
||||
// fs.dump(std::cerr, 9);
|
||||
|
||||
struct ::statvfs vfsbuf;
|
||||
vfs_stat vfsbuf;
|
||||
fs.statvfs(&vfsbuf);
|
||||
|
||||
EXPECT_EQ(1 << block_size_bits, vfsbuf.f_bsize);
|
||||
EXPECT_EQ(1, vfsbuf.f_frsize);
|
||||
EXPECT_EQ(1 << block_size_bits, vfsbuf.bsize);
|
||||
EXPECT_EQ(1, vfsbuf.frsize);
|
||||
if (enable_nlink) {
|
||||
EXPECT_EQ(access_fail ? 2046934 : 2056934, vfsbuf.f_blocks);
|
||||
EXPECT_EQ(access_fail ? 2046934 : 2056934, vfsbuf.blocks);
|
||||
} else {
|
||||
EXPECT_EQ(access_fail ? 2070390 : 2080390, vfsbuf.f_blocks);
|
||||
EXPECT_EQ(access_fail ? 2070390 : 2080390, vfsbuf.blocks);
|
||||
}
|
||||
EXPECT_EQ(11 + 2 * with_devices + with_specials, vfsbuf.f_files);
|
||||
EXPECT_EQ(ST_RDONLY, vfsbuf.f_flag);
|
||||
EXPECT_GT(vfsbuf.f_namemax, 0);
|
||||
EXPECT_EQ(11 + 2 * with_devices + with_specials, vfsbuf.files);
|
||||
EXPECT_TRUE(vfsbuf.readonly);
|
||||
EXPECT_GT(vfsbuf.namemax, 0);
|
||||
|
||||
std::ostringstream dumpss;
|
||||
|
||||
@ -220,36 +226,36 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
EXPECT_GT(dumpss.str().size(), 1000) << dumpss.str();
|
||||
|
||||
auto entry = fs.find("/foo.pl");
|
||||
struct ::stat st;
|
||||
file_stat st;
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, 23456);
|
||||
EXPECT_EQ(st.st_uid, set_uid ? 0 : 1337);
|
||||
EXPECT_EQ(st.st_gid, 0);
|
||||
EXPECT_EQ(st.st_atime, set_time ? 4711 : keep_all_times ? 4001 : 4002);
|
||||
EXPECT_EQ(st.st_mtime, set_time ? 4711 : keep_all_times ? 4002 : 4002);
|
||||
EXPECT_EQ(st.st_ctime, set_time ? 4711 : keep_all_times ? 4003 : 4002);
|
||||
EXPECT_EQ(st.size, 23456);
|
||||
EXPECT_EQ(st.uid, set_uid ? 0 : 1337);
|
||||
EXPECT_EQ(st.gid, 0);
|
||||
EXPECT_EQ(st.atime, set_time ? 4711 : keep_all_times ? 4001 : 4002);
|
||||
EXPECT_EQ(st.mtime, set_time ? 4711 : keep_all_times ? 4002 : 4002);
|
||||
EXPECT_EQ(st.ctime, set_time ? 4711 : keep_all_times ? 4003 : 4002);
|
||||
|
||||
int inode = fs.open(*entry);
|
||||
EXPECT_GE(inode, 0);
|
||||
|
||||
std::vector<char> buf(st.st_size);
|
||||
ssize_t rv = fs.read(inode, &buf[0], st.st_size, 0);
|
||||
EXPECT_EQ(rv, st.st_size);
|
||||
EXPECT_EQ(std::string(buf.begin(), buf.end()), test::loremipsum(st.st_size));
|
||||
std::vector<char> buf(st.size);
|
||||
ssize_t rv = fs.read(inode, &buf[0], st.size, 0);
|
||||
EXPECT_EQ(rv, st.size);
|
||||
EXPECT_EQ(std::string(buf.begin(), buf.end()), test::loremipsum(st.size));
|
||||
|
||||
entry = fs.find("/somelink");
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, 16);
|
||||
EXPECT_EQ(st.st_uid, set_uid ? 0 : 1000);
|
||||
EXPECT_EQ(st.st_gid, set_gid ? 0 : 100);
|
||||
EXPECT_EQ(st.st_rdev, 0);
|
||||
EXPECT_EQ(st.st_atime, set_time ? 4711 : keep_all_times ? 2001 : 2002);
|
||||
EXPECT_EQ(st.st_mtime, set_time ? 4711 : keep_all_times ? 2002 : 2002);
|
||||
EXPECT_EQ(st.st_ctime, set_time ? 4711 : keep_all_times ? 2003 : 2002);
|
||||
EXPECT_EQ(st.size, 16);
|
||||
EXPECT_EQ(st.uid, set_uid ? 0 : 1000);
|
||||
EXPECT_EQ(st.gid, set_gid ? 0 : 100);
|
||||
EXPECT_EQ(st.rdev, 0);
|
||||
EXPECT_EQ(st.atime, set_time ? 4711 : keep_all_times ? 2001 : 2002);
|
||||
EXPECT_EQ(st.mtime, set_time ? 4711 : keep_all_times ? 2002 : 2002);
|
||||
EXPECT_EQ(st.ctime, set_time ? 4711 : keep_all_times ? 2003 : 2002);
|
||||
|
||||
std::string link;
|
||||
EXPECT_EQ(fs.readlink(*entry, &link), 0);
|
||||
@ -261,7 +267,7 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, 6);
|
||||
EXPECT_EQ(st.size, 6);
|
||||
|
||||
EXPECT_EQ(fs.readlink(*entry, &link), 0);
|
||||
EXPECT_EQ(link, "../foo");
|
||||
@ -271,14 +277,14 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
if (with_specials) {
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, 0);
|
||||
EXPECT_EQ(st.st_uid, set_uid ? 0 : 1000);
|
||||
EXPECT_EQ(st.st_gid, set_gid ? 0 : 100);
|
||||
EXPECT_TRUE(S_ISFIFO(st.st_mode));
|
||||
EXPECT_EQ(st.st_rdev, 0);
|
||||
EXPECT_EQ(st.st_atime, set_time ? 4711 : keep_all_times ? 8001 : 8002);
|
||||
EXPECT_EQ(st.st_mtime, set_time ? 4711 : keep_all_times ? 8002 : 8002);
|
||||
EXPECT_EQ(st.st_ctime, set_time ? 4711 : keep_all_times ? 8003 : 8002);
|
||||
EXPECT_EQ(st.size, 0);
|
||||
EXPECT_EQ(st.uid, set_uid ? 0 : 1000);
|
||||
EXPECT_EQ(st.gid, set_gid ? 0 : 100);
|
||||
EXPECT_EQ(st.type(), posix_file_type::fifo);
|
||||
EXPECT_EQ(st.rdev, 0);
|
||||
EXPECT_EQ(st.atime, set_time ? 4711 : keep_all_times ? 8001 : 8002);
|
||||
EXPECT_EQ(st.mtime, set_time ? 4711 : keep_all_times ? 8002 : 8002);
|
||||
EXPECT_EQ(st.ctime, set_time ? 4711 : keep_all_times ? 8003 : 8002);
|
||||
} else {
|
||||
EXPECT_FALSE(entry);
|
||||
}
|
||||
@ -288,11 +294,11 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
if (with_devices) {
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, 0);
|
||||
EXPECT_EQ(st.st_uid, 0);
|
||||
EXPECT_EQ(st.st_gid, 0);
|
||||
EXPECT_TRUE(S_ISCHR(st.st_mode));
|
||||
EXPECT_EQ(st.st_rdev, 259);
|
||||
EXPECT_EQ(st.size, 0);
|
||||
EXPECT_EQ(st.uid, 0);
|
||||
EXPECT_EQ(st.gid, 0);
|
||||
EXPECT_EQ(st.type(), posix_file_type::character);
|
||||
EXPECT_EQ(st.rdev, 259);
|
||||
} else {
|
||||
EXPECT_FALSE(entry);
|
||||
}
|
||||
@ -302,20 +308,20 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
if (with_devices) {
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, 0);
|
||||
EXPECT_EQ(st.st_uid, 0);
|
||||
EXPECT_EQ(st.st_gid, 0);
|
||||
EXPECT_TRUE(S_ISCHR(st.st_mode));
|
||||
EXPECT_EQ(st.st_rdev, 261);
|
||||
EXPECT_EQ(st.st_atime, set_time ? 4711
|
||||
: keep_all_times ? 4000010001
|
||||
: 4000020002);
|
||||
EXPECT_EQ(st.st_mtime, set_time ? 4711
|
||||
: keep_all_times ? 4000020002
|
||||
: 4000020002);
|
||||
EXPECT_EQ(st.st_ctime, set_time ? 4711
|
||||
: keep_all_times ? 4000030003
|
||||
: 4000020002);
|
||||
EXPECT_EQ(st.size, 0);
|
||||
EXPECT_EQ(st.uid, 0);
|
||||
EXPECT_EQ(st.gid, 0);
|
||||
EXPECT_EQ(st.type(), posix_file_type::character);
|
||||
EXPECT_EQ(st.rdev, 261);
|
||||
EXPECT_EQ(st.atime, set_time ? 4711
|
||||
: keep_all_times ? 4000010001
|
||||
: 4000020002);
|
||||
EXPECT_EQ(st.mtime, set_time ? 4711
|
||||
: keep_all_times ? 4000020002
|
||||
: 4000020002);
|
||||
EXPECT_EQ(st.ctime, set_time ? 4711
|
||||
: keep_all_times ? 4000030003
|
||||
: 4000020002);
|
||||
} else {
|
||||
EXPECT_FALSE(entry);
|
||||
}
|
||||
@ -368,14 +374,14 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
|
||||
EXPECT_EQ(entry->inode_num(), e2->inode_num());
|
||||
|
||||
struct ::stat st1, st2;
|
||||
file_stat st1, st2;
|
||||
ASSERT_EQ(0, fs.getattr(*entry, &st1));
|
||||
ASSERT_EQ(0, fs.getattr(*e2, &st2));
|
||||
|
||||
EXPECT_EQ(st1.st_ino, st2.st_ino);
|
||||
EXPECT_EQ(st1.ino, st2.ino);
|
||||
if (enable_nlink) {
|
||||
EXPECT_EQ(2, st1.st_nlink);
|
||||
EXPECT_EQ(2, st2.st_nlink);
|
||||
EXPECT_EQ(2, st1.nlink);
|
||||
EXPECT_EQ(2, st2.nlink);
|
||||
}
|
||||
|
||||
entry = fs.find("/");
|
||||
@ -388,27 +394,27 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_GT(entry->inode_num(), 0);
|
||||
ASSERT_EQ(0, fs.getattr(*entry, &st1));
|
||||
EXPECT_EQ(23456, st1.st_size);
|
||||
EXPECT_EQ(23456, st1.size);
|
||||
e2 = fs.find(0, "somedir");
|
||||
ASSERT_TRUE(e2);
|
||||
ASSERT_EQ(0, fs.getattr(*e2, &st2));
|
||||
entry = fs.find(st2.st_ino, "ipsum.py");
|
||||
entry = fs.find(st2.ino, "ipsum.py");
|
||||
ASSERT_TRUE(entry);
|
||||
ASSERT_EQ(0, fs.getattr(*entry, &st1));
|
||||
EXPECT_EQ(access_fail ? 0 : 10000, st1.st_size);
|
||||
EXPECT_EQ(access_fail ? 0 : 10000, st1.size);
|
||||
EXPECT_EQ(0, fs.access(*entry, R_OK, 1000, 100));
|
||||
entry = fs.find(0, "baz.pl");
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(set_uid ? EACCES : 0, fs.access(*entry, R_OK, 1337, 0));
|
||||
|
||||
for (auto mp : {&filesystem_v2::walk, &filesystem_v2::walk_data_order}) {
|
||||
std::map<std::string, struct ::stat> entries;
|
||||
std::map<std::string, file_stat> entries;
|
||||
std::vector<int> inodes;
|
||||
|
||||
(fs.*mp)([&](dir_entry_view e) {
|
||||
struct ::stat stbuf;
|
||||
file_stat stbuf;
|
||||
ASSERT_EQ(0, fs.getattr(e.inode(), &stbuf));
|
||||
inodes.push_back(stbuf.st_ino);
|
||||
inodes.push_back(stbuf.ino);
|
||||
auto path = e.path();
|
||||
if (!path.empty()) {
|
||||
path = "/" + path;
|
||||
@ -420,16 +426,15 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
input->size() + 2 * with_devices + with_specials - 3);
|
||||
|
||||
for (auto const& [p, st] : entries) {
|
||||
struct ::stat ref;
|
||||
input->lstat(p, &ref);
|
||||
EXPECT_EQ(ref.st_mode, st.st_mode) << p;
|
||||
EXPECT_EQ(set_uid ? 0 : ref.st_uid, st.st_uid) << p;
|
||||
EXPECT_EQ(set_gid ? 0 : ref.st_gid, st.st_gid) << p;
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
auto ref = input->symlink_info(p);
|
||||
EXPECT_EQ(ref.mode, st.mode) << p;
|
||||
EXPECT_EQ(set_uid ? 0 : ref.uid, st.uid) << p;
|
||||
EXPECT_EQ(set_gid ? 0 : ref.gid, st.gid) << p;
|
||||
if (!st.is_directory()) {
|
||||
if (input->access(p, R_OK) == 0) {
|
||||
EXPECT_EQ(ref.st_size, st.st_size) << p;
|
||||
EXPECT_EQ(ref.size, st.size) << p;
|
||||
} else {
|
||||
EXPECT_EQ(0, st.st_size) << p;
|
||||
EXPECT_EQ(0, st.size) << p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -571,19 +576,19 @@ TEST_P(packing_test, regression_empty_fs) {
|
||||
|
||||
filesystem_v2 fs(lgr, mm, opts);
|
||||
|
||||
struct ::statvfs vfsbuf;
|
||||
vfs_stat vfsbuf;
|
||||
fs.statvfs(&vfsbuf);
|
||||
|
||||
EXPECT_EQ(1, vfsbuf.f_files);
|
||||
EXPECT_EQ(0, vfsbuf.f_blocks);
|
||||
EXPECT_EQ(1, vfsbuf.files);
|
||||
EXPECT_EQ(0, vfsbuf.blocks);
|
||||
|
||||
size_t num = 0;
|
||||
|
||||
fs.walk([&](dir_entry_view e) {
|
||||
++num;
|
||||
struct ::stat stbuf;
|
||||
file_stat stbuf;
|
||||
ASSERT_EQ(0, fs.getattr(e.inode(), &stbuf));
|
||||
EXPECT_TRUE(S_ISDIR(stbuf.st_mode));
|
||||
EXPECT_TRUE(stbuf.is_directory());
|
||||
});
|
||||
|
||||
EXPECT_EQ(1, num);
|
||||
@ -649,11 +654,11 @@ TEST(block_manager, regression_block_boundary) {
|
||||
|
||||
filesystem_v2 fs(lgr, mm, opts);
|
||||
|
||||
struct ::statvfs vfsbuf;
|
||||
vfs_stat vfsbuf;
|
||||
fs.statvfs(&vfsbuf);
|
||||
|
||||
EXPECT_EQ(2, vfsbuf.f_files);
|
||||
EXPECT_EQ(size, vfsbuf.f_blocks);
|
||||
EXPECT_EQ(2, vfsbuf.files);
|
||||
EXPECT_EQ(size, vfsbuf.blocks);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(std::is_sorted(fs_sizes.begin(), fs_sizes.end()))
|
||||
@ -721,26 +726,26 @@ TEST_P(compression_regression, github45) {
|
||||
|
||||
filesystem_v2 fs(lgr, mm, opts);
|
||||
|
||||
struct ::statvfs vfsbuf;
|
||||
vfs_stat vfsbuf;
|
||||
fs.statvfs(&vfsbuf);
|
||||
|
||||
EXPECT_EQ(3, vfsbuf.f_files);
|
||||
EXPECT_EQ(2 * file_size, vfsbuf.f_blocks);
|
||||
EXPECT_EQ(3, vfsbuf.files);
|
||||
EXPECT_EQ(2 * file_size, vfsbuf.blocks);
|
||||
|
||||
auto check_file = [&](char const* name, std::string const& contents) {
|
||||
auto entry = fs.find(name);
|
||||
struct ::stat st;
|
||||
file_stat st;
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(fs.getattr(*entry, &st), 0);
|
||||
EXPECT_EQ(st.st_size, file_size);
|
||||
EXPECT_EQ(st.size, file_size);
|
||||
|
||||
int inode = fs.open(*entry);
|
||||
EXPECT_GE(inode, 0);
|
||||
|
||||
std::vector<char> buf(st.st_size);
|
||||
ssize_t rv = fs.read(inode, &buf[0], st.st_size, 0);
|
||||
EXPECT_EQ(rv, st.st_size);
|
||||
std::vector<char> buf(st.size);
|
||||
ssize_t rv = fs.read(inode, &buf[0], st.size, 0);
|
||||
EXPECT_EQ(rv, st.size);
|
||||
EXPECT_EQ(std::string(buf.begin(), buf.end()), contents);
|
||||
};
|
||||
|
||||
@ -824,30 +829,19 @@ TEST_P(filter, filesystem) {
|
||||
auto input = std::make_shared<test::os_access_mock>();
|
||||
|
||||
for (auto const& [stat, name] : dwarfs::test::test_dirtree()) {
|
||||
struct ::stat st;
|
||||
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
|
||||
st.st_ino = stat.st_ino;
|
||||
st.st_mode = stat.st_mode;
|
||||
st.st_nlink = stat.st_nlink;
|
||||
st.st_uid = stat.st_uid;
|
||||
st.st_gid = stat.st_gid;
|
||||
st.st_size = stat.st_size;
|
||||
st.st_atime = stat.atime;
|
||||
st.st_mtime = stat.mtime;
|
||||
st.st_ctime = stat.ctime;
|
||||
st.st_rdev = stat.st_rdev;
|
||||
|
||||
auto path = name.substr(name.size() == 5 ? 5 : 6);
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
input->add(path, st,
|
||||
[size = st.st_size] { return test::loremipsum(size); });
|
||||
} else if (S_ISLNK(st.st_mode)) {
|
||||
input->add(path, st, test::loremipsum(st.st_size));
|
||||
} else {
|
||||
input->add(path, st);
|
||||
switch (stat.type()) {
|
||||
case posix_file_type::regular:
|
||||
input->add(path, stat,
|
||||
[size = stat.size] { return test::loremipsum(size); });
|
||||
break;
|
||||
case posix_file_type::symlink:
|
||||
input->add(path, stat, test::loremipsum(stat.size));
|
||||
break;
|
||||
default:
|
||||
input->add(path, stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,13 +31,12 @@
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include <folly/FileUtil.h>
|
||||
#include <folly/String.h>
|
||||
#include <folly/json.h>
|
||||
|
||||
#include "dwarfs/block_compressor.h"
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/filesystem_extractor.h"
|
||||
#include "dwarfs/filesystem_v2.h"
|
||||
#include "dwarfs/filesystem_writer.h"
|
||||
@ -45,6 +44,7 @@
|
||||
#include "dwarfs/mmap.h"
|
||||
#include "dwarfs/options.h"
|
||||
#include "dwarfs/progress.h"
|
||||
#include "dwarfs/vfs_stat.h"
|
||||
#include "dwarfs/worker_group.h"
|
||||
|
||||
#include "mmap_mock.h"
|
||||
@ -797,11 +797,12 @@ std::vector<std::string> headers_v2{
|
||||
"DWARFS\x02" + format_sh + "DWARFS\x02",
|
||||
};
|
||||
|
||||
struct ::stat make_stat(::mode_t mode, ::off_t size) {
|
||||
struct ::stat st;
|
||||
file_stat make_stat(posix_file_type::value type, file_stat::perms_type perms,
|
||||
file_stat::off_type size) {
|
||||
file_stat st;
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
st.st_mode = mode;
|
||||
st.st_size = size;
|
||||
st.mode = type | perms;
|
||||
st.size = size;
|
||||
return st;
|
||||
}
|
||||
|
||||
@ -810,15 +811,15 @@ void check_compat(logger& lgr, filesystem_v2 const& fs,
|
||||
bool has_devices = not(version == "0.2.0" or version == "0.2.3");
|
||||
bool has_ac_time = version == "0.2.0" or version == "0.2.3";
|
||||
|
||||
struct ::statvfs vfsbuf;
|
||||
vfs_stat vfsbuf;
|
||||
fs.statvfs(&vfsbuf);
|
||||
|
||||
EXPECT_EQ(1024, vfsbuf.f_bsize);
|
||||
EXPECT_EQ(1, vfsbuf.f_frsize);
|
||||
EXPECT_EQ(10614, vfsbuf.f_blocks);
|
||||
EXPECT_EQ(33 + 3 * has_devices, vfsbuf.f_files);
|
||||
EXPECT_EQ(ST_RDONLY, vfsbuf.f_flag);
|
||||
EXPECT_GT(vfsbuf.f_namemax, 0);
|
||||
EXPECT_EQ(1024, vfsbuf.bsize);
|
||||
EXPECT_EQ(1, vfsbuf.frsize);
|
||||
EXPECT_EQ(10614, vfsbuf.blocks);
|
||||
EXPECT_EQ(33 + 3 * has_devices, vfsbuf.files);
|
||||
EXPECT_TRUE(vfsbuf.readonly);
|
||||
EXPECT_GT(vfsbuf.namemax, 0);
|
||||
|
||||
auto json = fs.serialize_metadata_as_json(true);
|
||||
EXPECT_GT(json.size(), 1000) << json;
|
||||
@ -828,18 +829,18 @@ void check_compat(logger& lgr, filesystem_v2 const& fs,
|
||||
EXPECT_GT(dumpss.str().size(), 1000) << dumpss.str();
|
||||
|
||||
auto entry = fs.find("/format.sh");
|
||||
struct ::stat st;
|
||||
file_stat st;
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(0, fs.getattr(*entry, &st));
|
||||
EXPECT_EQ(94, st.st_size);
|
||||
EXPECT_EQ(S_IFREG | 0755, st.st_mode);
|
||||
EXPECT_EQ(1000, st.st_uid);
|
||||
EXPECT_EQ(100, st.st_gid);
|
||||
EXPECT_EQ(1606256045, st.st_mtime);
|
||||
EXPECT_EQ(94, st.size);
|
||||
EXPECT_EQ(S_IFREG | 0755, st.mode);
|
||||
EXPECT_EQ(1000, st.uid);
|
||||
EXPECT_EQ(100, st.gid);
|
||||
EXPECT_EQ(1606256045, st.mtime);
|
||||
if (has_ac_time) {
|
||||
EXPECT_EQ(1616013831, st.st_atime);
|
||||
EXPECT_EQ(1616013816, st.st_ctime);
|
||||
EXPECT_EQ(1616013831, st.atime);
|
||||
EXPECT_EQ(1616013816, st.ctime);
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, fs.access(*entry, R_OK, 1000, 0));
|
||||
@ -847,9 +848,9 @@ void check_compat(logger& lgr, filesystem_v2 const& fs,
|
||||
auto inode = fs.open(*entry);
|
||||
EXPECT_GE(inode, 0);
|
||||
|
||||
std::vector<char> buf(st.st_size);
|
||||
auto rv = fs.read(inode, &buf[0], st.st_size, 0);
|
||||
EXPECT_EQ(rv, st.st_size);
|
||||
std::vector<char> buf(st.size);
|
||||
auto rv = fs.read(inode, &buf[0], st.size, 0);
|
||||
EXPECT_EQ(rv, st.size);
|
||||
EXPECT_EQ(format_sh, std::string(buf.begin(), buf.end()));
|
||||
|
||||
entry = fs.find("/foo/bad");
|
||||
@ -883,69 +884,71 @@ void check_compat(logger& lgr, filesystem_v2 const& fs,
|
||||
|
||||
EXPECT_EQ(expected, names);
|
||||
|
||||
std::map<std::string, struct ::stat> ref_entries{
|
||||
{"", make_stat(S_IFDIR | 0755, 8)},
|
||||
{"bench.sh", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"dev", make_stat(S_IFDIR | 0755, 2)},
|
||||
{"dev/null", make_stat(S_IFCHR | 0666, 0)},
|
||||
{"dev/zero", make_stat(S_IFCHR | 0666, 0)},
|
||||
{"empty", make_stat(S_IFDIR | 0755, 1)},
|
||||
{"empty/alsoempty", make_stat(S_IFDIR | 0755, 0)},
|
||||
{"foo", make_stat(S_IFDIR | 0755, 5)},
|
||||
{"foo/1", make_stat(S_IFDIR | 0755, 2)},
|
||||
{"foo/1/2", make_stat(S_IFDIR | 0755, 2)},
|
||||
{"foo/1/2/3", make_stat(S_IFDIR | 0755, 3)},
|
||||
{"foo/1/2/3/4", make_stat(S_IFDIR | 0755, 2)},
|
||||
{"foo/1/2/3/4/5", make_stat(S_IFDIR | 0755, 2)},
|
||||
{"foo/1/2/3/4/5/6", make_stat(S_IFDIR | 0755, 1)},
|
||||
{"foo/1/2/3/4/5/6/7", make_stat(S_IFDIR | 0755, 1)},
|
||||
{"foo/1/2/3/4/5/6/7/8", make_stat(S_IFDIR | 0755, 1)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9", make_stat(S_IFDIR | 0755, 13)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/a", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/b", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/blubb", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/c", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/d", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/e", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/f", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/g", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/h", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/i", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/j", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/k", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/l", make_stat(S_IFREG | 0644, 2)},
|
||||
{"foo/1/2/3/4/5/z", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"foo/1/2/3/4/y", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"foo/1/2/3/copy.sh", make_stat(S_IFREG | 0755, 94)},
|
||||
{"foo/1/2/3/x", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"foo/1/2/xxx.sh", make_stat(S_IFREG | 0755, 94)},
|
||||
{"foo/1/fmt.sh", make_stat(S_IFREG | 0755, 94)},
|
||||
{"foo/bad", make_stat(S_IFLNK | 0777, 6)},
|
||||
{"foo/bar", make_stat(S_IFREG | 0644, 0)},
|
||||
{"foo/bla.sh", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"foo/pipe", make_stat(S_IFIFO | 0644, 0)},
|
||||
{"foobar", make_stat(S_IFLNK | 0777, 7)},
|
||||
{"format.sh", make_stat(S_IFREG | 0755, 94)},
|
||||
{"perl-exec.sh", make_stat(S_IFREG | 0644, 87)},
|
||||
{"test.py", make_stat(S_IFREG | 0644, 1012)},
|
||||
std::map<std::string, file_stat> ref_entries{
|
||||
{"", make_stat(posix_file_type::directory, 0755, 8)},
|
||||
{"bench.sh", make_stat(posix_file_type::regular, 0644, 1517)},
|
||||
{"dev", make_stat(posix_file_type::directory, 0755, 2)},
|
||||
{"dev/null", make_stat(posix_file_type::character, 0666, 0)},
|
||||
{"dev/zero", make_stat(posix_file_type::character, 0666, 0)},
|
||||
{"empty", make_stat(posix_file_type::directory, 0755, 1)},
|
||||
{"empty/alsoempty", make_stat(posix_file_type::directory, 0755, 0)},
|
||||
{"foo", make_stat(posix_file_type::directory, 0755, 5)},
|
||||
{"foo/1", make_stat(posix_file_type::directory, 0755, 2)},
|
||||
{"foo/1/2", make_stat(posix_file_type::directory, 0755, 2)},
|
||||
{"foo/1/2/3", make_stat(posix_file_type::directory, 0755, 3)},
|
||||
{"foo/1/2/3/4", make_stat(posix_file_type::directory, 0755, 2)},
|
||||
{"foo/1/2/3/4/5", make_stat(posix_file_type::directory, 0755, 2)},
|
||||
{"foo/1/2/3/4/5/6", make_stat(posix_file_type::directory, 0755, 1)},
|
||||
{"foo/1/2/3/4/5/6/7", make_stat(posix_file_type::directory, 0755, 1)},
|
||||
{"foo/1/2/3/4/5/6/7/8", make_stat(posix_file_type::directory, 0755, 1)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9",
|
||||
make_stat(posix_file_type::directory, 0755, 13)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/a", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/b", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/blubb",
|
||||
make_stat(posix_file_type::regular, 0644, 1517)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/c", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/d", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/e", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/f", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/g", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/h", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/i", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/j", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/k", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/6/7/8/9/l", make_stat(posix_file_type::regular, 0644, 2)},
|
||||
{"foo/1/2/3/4/5/z", make_stat(posix_file_type::regular, 0644, 1517)},
|
||||
{"foo/1/2/3/4/y", make_stat(posix_file_type::regular, 0644, 1517)},
|
||||
{"foo/1/2/3/copy.sh", make_stat(posix_file_type::regular, 0755, 94)},
|
||||
{"foo/1/2/3/x", make_stat(posix_file_type::regular, 0644, 1517)},
|
||||
{"foo/1/2/xxx.sh", make_stat(posix_file_type::regular, 0755, 94)},
|
||||
{"foo/1/fmt.sh", make_stat(posix_file_type::regular, 0755, 94)},
|
||||
{"foo/bad", make_stat(posix_file_type::symlink, 0777, 6)},
|
||||
{"foo/bar", make_stat(posix_file_type::regular, 0644, 0)},
|
||||
{"foo/bla.sh", make_stat(posix_file_type::regular, 0644, 1517)},
|
||||
{"foo/pipe", make_stat(posix_file_type::fifo, 0644, 0)},
|
||||
{"foobar", make_stat(posix_file_type::symlink, 0777, 7)},
|
||||
{"format.sh", make_stat(posix_file_type::regular, 0755, 94)},
|
||||
{"perl-exec.sh", make_stat(posix_file_type::regular, 0644, 87)},
|
||||
{"test.py", make_stat(posix_file_type::regular, 0644, 1012)},
|
||||
};
|
||||
|
||||
if (!has_devices) {
|
||||
for (auto special : {"dev/null", "dev/zero", "foo/pipe"}) {
|
||||
ref_entries.erase(special);
|
||||
}
|
||||
ref_entries["dev"].st_size -= 2;
|
||||
ref_entries["foo"].st_size -= 1;
|
||||
ref_entries["dev"].size -= 2;
|
||||
ref_entries["foo"].size -= 1;
|
||||
}
|
||||
|
||||
for (auto mp : {&filesystem_v2::walk, &filesystem_v2::walk_data_order}) {
|
||||
std::map<std::string, struct ::stat> entries;
|
||||
std::map<std::string, file_stat> entries;
|
||||
std::vector<int> inodes;
|
||||
|
||||
(fs.*mp)([&](dir_entry_view e) {
|
||||
struct ::stat stbuf;
|
||||
file_stat stbuf;
|
||||
ASSERT_EQ(0, fs.getattr(e.inode(), &stbuf));
|
||||
inodes.push_back(stbuf.st_ino);
|
||||
inodes.push_back(stbuf.ino);
|
||||
EXPECT_TRUE(entries.emplace(e.path(), stbuf).second);
|
||||
});
|
||||
|
||||
@ -955,15 +958,15 @@ void check_compat(logger& lgr, filesystem_v2 const& fs,
|
||||
auto it = ref_entries.find(p);
|
||||
EXPECT_TRUE(it != ref_entries.end()) << p;
|
||||
if (it != ref_entries.end()) {
|
||||
EXPECT_EQ(it->second.st_mode, st.st_mode) << p;
|
||||
if (S_ISCHR(st.st_mode)) {
|
||||
EXPECT_EQ(0, st.st_uid) << p;
|
||||
EXPECT_EQ(0, st.st_gid) << p;
|
||||
EXPECT_EQ(it->second.mode, st.mode) << p;
|
||||
if (st.type() == posix_file_type::character) {
|
||||
EXPECT_EQ(0, st.uid) << p;
|
||||
EXPECT_EQ(0, st.gid) << p;
|
||||
} else {
|
||||
EXPECT_EQ(1000, st.st_uid) << p;
|
||||
EXPECT_EQ(100, st.st_gid) << p;
|
||||
EXPECT_EQ(1000, st.uid) << p;
|
||||
EXPECT_EQ(100, st.gid) << p;
|
||||
}
|
||||
EXPECT_EQ(it->second.st_size, st.st_size) << p;
|
||||
EXPECT_EQ(it->second.size, st.size) << p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1005,12 +1008,12 @@ void check_compat(logger& lgr, filesystem_v2 const& fs,
|
||||
if (ri != ref_entries.end()) {
|
||||
auto const& st = ri->second;
|
||||
|
||||
EXPECT_EQ(kv["mode"], fmt::format("{0:o}", st.st_mode & 0777));
|
||||
EXPECT_EQ(kv["mode"], fmt::format("{0:o}", st.mode & 0777));
|
||||
EXPECT_EQ(std::stoi(kv["uid"]), kv["type"] == "char" ? 0 : 1000);
|
||||
EXPECT_EQ(std::stoi(kv["gid"]), kv["type"] == "char" ? 0 : 100);
|
||||
|
||||
if (kv["type"] == "file") {
|
||||
EXPECT_EQ(std::stoi(kv["size"]), st.st_size);
|
||||
EXPECT_EQ(std::stoi(kv["size"]), st.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,6 +188,7 @@ bool wait_until_file_ready(std::filesystem::path const& path,
|
||||
}
|
||||
|
||||
bool check_readonly(std::filesystem::path const& p, bool readonly = false) {
|
||||
// TODO: std::filesystem
|
||||
struct ::stat buf;
|
||||
if (::stat(p.c_str(), &buf) != 0) {
|
||||
throw std::runtime_error("could not stat " + p.string());
|
||||
@ -211,6 +212,7 @@ bool check_readonly(std::filesystem::path const& p, bool readonly = false) {
|
||||
}
|
||||
|
||||
::nlink_t num_hardlinks(std::filesystem::path const& p) {
|
||||
// TODO: std::filesystem
|
||||
struct ::stat buf;
|
||||
if (::stat(p.c_str(), &buf) != 0) {
|
||||
throw std::runtime_error("could not stat " + p.string());
|
||||
|
@ -1,10 +1,12 @@
|
||||
#include <array>
|
||||
|
||||
#include "test_helpers.h"
|
||||
|
||||
namespace dwarfs::test {
|
||||
|
||||
std::span<std::pair<simplestat, std::string_view> const> test_dirtree() {
|
||||
// clang-format off
|
||||
static std::vector<std::pair<simplestat, std::string_view>> const dt{
|
||||
static std::array<std::pair<simplestat, std::string_view>, 337> const dt{{
|
||||
{{46193947, 040755, 1, 0, 0, 10, 27, 1667125429, 1666544836, 1666894334}, "stage"},
|
||||
{{46193949, 040755, 1, 0, 0, 10, 27, 1667125429, 1666544836, 1666894334}, "stage/run"},
|
||||
{{46193951, 040755, 1, 0, 0, 10, 27, 1667125429, 1666544836, 1666894334}, "stage/boot"},
|
||||
@ -342,7 +344,7 @@ std::span<std::pair<simplestat, std::string_view> const> test_dirtree() {
|
||||
{{46255143, 060640, 1, 0, 6, 0, 27, 1666894345, 1666544853, 1666894345}, "stage/dev/hda20"},
|
||||
{{46255250, 060640, 1, 0, 6, 0, 27, 1666894345, 1666544853, 1666894345}, "stage/dev/sdd"},
|
||||
{{46255234, 020600, 1, 0, 0, 0, 27, 1666894345, 1666544853, 1666894345}, "stage/dev/input/event23"},
|
||||
};
|
||||
}};
|
||||
// clang-format on
|
||||
return dt;
|
||||
}
|
||||
|
@ -25,9 +25,6 @@
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <folly/String.h>
|
||||
|
||||
#include "dwarfs/overloaded.h"
|
||||
@ -38,9 +35,31 @@
|
||||
namespace dwarfs {
|
||||
namespace test {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
file_stat make_file_stat(simplestat const& ss) {
|
||||
file_stat rv;
|
||||
::memset(&rv, 0, sizeof(rv));
|
||||
rv.ino = ss.ino;
|
||||
rv.nlink = ss.nlink;
|
||||
rv.mode = ss.mode;
|
||||
rv.uid = ss.uid;
|
||||
rv.gid = ss.gid;
|
||||
rv.rdev = ss.rdev;
|
||||
rv.size = ss.size;
|
||||
rv.atime = ss.atime;
|
||||
rv.mtime = ss.mtime;
|
||||
rv.ctime = ss.ctime;
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct os_access_mock::mock_dirent {
|
||||
std::string name;
|
||||
struct ::stat stat;
|
||||
simplestat status;
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
v;
|
||||
@ -50,7 +69,7 @@ struct os_access_mock::mock_dirent {
|
||||
mock_dirent* find(std::string const& name);
|
||||
|
||||
void
|
||||
add(std::string const& name, struct ::stat const& st,
|
||||
add(std::string const& name, simplestat const& st,
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
var);
|
||||
@ -64,7 +83,7 @@ struct os_access_mock::mock_directory {
|
||||
mock_dirent* find(std::string const& name);
|
||||
|
||||
void
|
||||
add(std::string const& name, struct ::stat const& st,
|
||||
add(std::string const& name, simplestat const& st,
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
var);
|
||||
@ -84,7 +103,7 @@ auto os_access_mock::mock_dirent::find(std::string const& name)
|
||||
}
|
||||
|
||||
void os_access_mock::mock_dirent::add(
|
||||
std::string const& name, struct ::stat const& st,
|
||||
std::string const& name, simplestat const& st,
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
var) {
|
||||
@ -108,13 +127,13 @@ auto os_access_mock::mock_directory::find(std::string const& name)
|
||||
}
|
||||
|
||||
void os_access_mock::mock_directory::add(
|
||||
std::string const& name, struct ::stat const& st,
|
||||
std::string const& name, simplestat const& st,
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
var) {
|
||||
assert(!find(name));
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (st.type() == posix_file_type::directory) {
|
||||
assert(std::holds_alternative<std::unique_ptr<mock_directory>>(var));
|
||||
} else {
|
||||
assert(!std::holds_alternative<std::unique_ptr<mock_directory>>(var));
|
||||
@ -122,7 +141,7 @@ void os_access_mock::mock_directory::add(
|
||||
|
||||
auto& de = ent.emplace_back();
|
||||
de.name = name;
|
||||
de.stat = st;
|
||||
de.status = st;
|
||||
de.v = std::move(var);
|
||||
}
|
||||
|
||||
@ -132,7 +151,7 @@ class dir_reader_mock : public dir_reader {
|
||||
: files_(files)
|
||||
, index_(0) {}
|
||||
|
||||
bool read(std::string& name) const override {
|
||||
bool read(std::string& name) override {
|
||||
if (index_ < files_.size()) {
|
||||
name = files_[index_++];
|
||||
return true;
|
||||
@ -143,7 +162,7 @@ class dir_reader_mock : public dir_reader {
|
||||
|
||||
private:
|
||||
std::vector<std::string> files_;
|
||||
mutable size_t index_;
|
||||
size_t index_;
|
||||
};
|
||||
|
||||
os_access_mock::os_access_mock() = default;
|
||||
@ -151,28 +170,49 @@ os_access_mock::~os_access_mock() = default;
|
||||
|
||||
std::shared_ptr<os_access_mock> os_access_mock::create_test_instance() {
|
||||
static const std::vector<std::pair<std::string, simplestat>> statmap{
|
||||
{"", {1, S_IFDIR | 0777, 1, 1000, 100, 0, 0, 1, 2, 3}},
|
||||
{"test.pl", {3, S_IFREG | 0644, 2, 1000, 100, 0, 0, 1001, 1002, 1003}},
|
||||
{"somelink", {4, S_IFLNK | 0777, 1, 1000, 100, 16, 0, 2001, 2002, 2003}},
|
||||
{"somedir", {5, S_IFDIR | 0777, 1, 1000, 100, 0, 0, 3001, 3002, 3003}},
|
||||
{"foo.pl", {6, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, 4003}},
|
||||
{"bar.pl", {6, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, 4003}},
|
||||
{"baz.pl", {16, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 8001, 8002, 8003}},
|
||||
{"", {1, posix_file_type::directory | 0777, 1, 1000, 100, 0, 0, 1, 2, 3}},
|
||||
{"test.pl",
|
||||
{3, posix_file_type::regular | 0644, 2, 1000, 100, 0, 0, 1001, 1002,
|
||||
1003}},
|
||||
{"somelink",
|
||||
{4, posix_file_type::symlink | 0777, 1, 1000, 100, 16, 0, 2001, 2002,
|
||||
2003}},
|
||||
{"somedir",
|
||||
{5, posix_file_type::directory | 0777, 1, 1000, 100, 0, 0, 3001, 3002,
|
||||
3003}},
|
||||
{"foo.pl",
|
||||
{6, posix_file_type::regular | 0600, 2, 1337, 0, 23456, 0, 4001, 4002,
|
||||
4003}},
|
||||
{"bar.pl",
|
||||
{6, posix_file_type::regular | 0600, 2, 1337, 0, 23456, 0, 4001, 4002,
|
||||
4003}},
|
||||
{"baz.pl",
|
||||
{16, posix_file_type::regular | 0600, 2, 1337, 0, 23456, 0, 8001, 8002,
|
||||
8003}},
|
||||
{"ipsum.txt",
|
||||
{7, S_IFREG | 0644, 1, 1000, 100, 2000000, 0, 5001, 5002, 5003}},
|
||||
{7, posix_file_type::regular | 0644, 1, 1000, 100, 2000000, 0, 5001,
|
||||
5002, 5003}},
|
||||
{"somedir/ipsum.py",
|
||||
{9, S_IFREG | 0644, 1, 1000, 100, 10000, 0, 6001, 6002, 6003}},
|
||||
{9, posix_file_type::regular | 0644, 1, 1000, 100, 10000, 0, 6001, 6002,
|
||||
6003}},
|
||||
{"somedir/bad",
|
||||
{10, S_IFLNK | 0777, 1, 1000, 100, 6, 0, 7001, 7002, 7003}},
|
||||
{10, posix_file_type::symlink | 0777, 1, 1000, 100, 6, 0, 7001, 7002,
|
||||
7003}},
|
||||
{"somedir/pipe",
|
||||
{12, S_IFIFO | 0644, 1, 1000, 100, 0, 0, 8001, 8002, 8003}},
|
||||
{"somedir/null", {13, S_IFCHR | 0666, 1, 0, 0, 0, 259, 9001, 9002, 9003}},
|
||||
{12, posix_file_type::fifo | 0644, 1, 1000, 100, 0, 0, 8001, 8002,
|
||||
8003}},
|
||||
{"somedir/null",
|
||||
{13, posix_file_type::character | 0666, 1, 0, 0, 0, 259, 9001, 9002,
|
||||
9003}},
|
||||
{"somedir/zero",
|
||||
{14, S_IFCHR | 0666, 1, 0, 0, 0, 261, 4000010001, 4000020002,
|
||||
4000030003}},
|
||||
{14, posix_file_type::character | 0666, 1, 0, 0, 0, 261, 4000010001,
|
||||
4000020002, 4000030003}},
|
||||
{"somedir/empty",
|
||||
{212, S_IFREG | 0644, 1, 1000, 100, 0, 0, 8101, 8102, 8103}},
|
||||
{"empty", {210, S_IFREG | 0644, 3, 1337, 0, 0, 0, 8201, 8202, 8203}},
|
||||
{212, posix_file_type::regular | 0644, 1, 1000, 100, 0, 0, 8101, 8102,
|
||||
8103}},
|
||||
{"empty",
|
||||
{210, posix_file_type::regular | 0644, 3, 1337, 0, 0, 0, 8201, 8202,
|
||||
8203}},
|
||||
};
|
||||
|
||||
static std::map<std::string, std::string> linkmap{
|
||||
@ -183,91 +223,78 @@ std::shared_ptr<os_access_mock> os_access_mock::create_test_instance() {
|
||||
auto m = std::make_shared<os_access_mock>();
|
||||
|
||||
for (auto const& kv : statmap) {
|
||||
const auto& sst = kv.second;
|
||||
struct ::stat st;
|
||||
const auto& stat = kv.second;
|
||||
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
|
||||
st.st_ino = sst.st_ino;
|
||||
st.st_mode = sst.st_mode;
|
||||
st.st_nlink = sst.st_nlink;
|
||||
st.st_uid = sst.st_uid;
|
||||
st.st_gid = sst.st_gid;
|
||||
st.st_size = sst.st_size;
|
||||
st.st_atime = sst.atime;
|
||||
st.st_mtime = sst.mtime;
|
||||
st.st_ctime = sst.ctime;
|
||||
st.st_rdev = sst.st_rdev;
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
m->add(kv.first, st, [size = st.st_size] { return loremipsum(size); });
|
||||
} else if (S_ISLNK(st.st_mode)) {
|
||||
m->add(kv.first, st, linkmap.at(kv.first));
|
||||
} else {
|
||||
m->add(kv.first, st);
|
||||
switch (stat.type()) {
|
||||
case posix_file_type::regular:
|
||||
m->add(kv.first, stat, [size = stat.size] { return loremipsum(size); });
|
||||
break;
|
||||
case posix_file_type::symlink:
|
||||
m->add(kv.first, stat, linkmap.at(kv.first));
|
||||
break;
|
||||
default:
|
||||
m->add(kv.first, stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void os_access_mock::add(std::filesystem::path const& path,
|
||||
struct ::stat const& st) {
|
||||
void os_access_mock::add(fs::path const& path, simplestat const& st) {
|
||||
add_internal(path, st, std::monostate{});
|
||||
}
|
||||
|
||||
void os_access_mock::add(std::filesystem::path const& path,
|
||||
struct ::stat const& st, std::string const& contents) {
|
||||
void os_access_mock::add(fs::path const& path, simplestat const& st,
|
||||
std::string const& contents) {
|
||||
add_internal(path, st, contents);
|
||||
}
|
||||
|
||||
void os_access_mock::add(std::filesystem::path const& path,
|
||||
struct ::stat const& st,
|
||||
void os_access_mock::add(fs::path const& path, simplestat const& st,
|
||||
std::function<std::string()> generator) {
|
||||
add_internal(path, st, generator);
|
||||
}
|
||||
|
||||
void os_access_mock::add_dir(std::filesystem::path const& path) {
|
||||
struct ::stat st;
|
||||
void os_access_mock::add_dir(fs::path const& path) {
|
||||
simplestat st;
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
st.st_ino = ino_++;
|
||||
st.st_mode = S_IFDIR | 0755;
|
||||
st.st_uid = 1000;
|
||||
st.st_gid = 100;
|
||||
st.ino = ino_++;
|
||||
st.mode = posix_file_type::directory | 0755;
|
||||
st.uid = 1000;
|
||||
st.gid = 100;
|
||||
add(path, st);
|
||||
}
|
||||
|
||||
void os_access_mock::add_file(std::filesystem::path const& path, size_t size) {
|
||||
struct ::stat st;
|
||||
void os_access_mock::add_file(fs::path const& path, size_t size) {
|
||||
simplestat st;
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
st.st_ino = ino_++;
|
||||
st.st_mode = S_IFREG | 0644;
|
||||
st.st_uid = 1000;
|
||||
st.st_gid = 100;
|
||||
st.st_size = size;
|
||||
st.ino = ino_++;
|
||||
st.mode = posix_file_type::regular | 0644;
|
||||
st.uid = 1000;
|
||||
st.gid = 100;
|
||||
st.size = size;
|
||||
add(path, st, [size] { return loremipsum(size); });
|
||||
}
|
||||
|
||||
void os_access_mock::add_file(std::filesystem::path const& path,
|
||||
void os_access_mock::add_file(fs::path const& path,
|
||||
std::string const& contents) {
|
||||
struct ::stat st;
|
||||
simplestat st;
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
st.st_ino = ino_++;
|
||||
st.st_mode = S_IFREG | 0644;
|
||||
st.st_uid = 1000;
|
||||
st.st_gid = 100;
|
||||
st.st_size = contents.size();
|
||||
st.ino = ino_++;
|
||||
st.mode = posix_file_type::regular | 0644;
|
||||
st.uid = 1000;
|
||||
st.gid = 100;
|
||||
st.size = contents.size();
|
||||
add(path, st, contents);
|
||||
}
|
||||
|
||||
void os_access_mock::set_access_fail(std::filesystem::path const& path) {
|
||||
void os_access_mock::set_access_fail(fs::path const& path) {
|
||||
access_fail_set_.emplace(path.string());
|
||||
}
|
||||
|
||||
size_t os_access_mock::size() const { return root_ ? root_->size() : 0; }
|
||||
|
||||
std::vector<std::string>
|
||||
os_access_mock::splitpath(std::filesystem::path const& path) {
|
||||
std::vector<std::string> os_access_mock::splitpath(fs::path const& path) {
|
||||
std::vector<std::string> parts;
|
||||
folly::split('/', path.native(), parts);
|
||||
while (!parts.empty() && parts.front().empty()) {
|
||||
@ -276,8 +303,7 @@ os_access_mock::splitpath(std::filesystem::path const& path) {
|
||||
return parts;
|
||||
}
|
||||
|
||||
auto os_access_mock::find(std::filesystem::path const& path) const
|
||||
-> mock_dirent* {
|
||||
auto os_access_mock::find(fs::path const& path) const -> mock_dirent* {
|
||||
return find(splitpath(path));
|
||||
}
|
||||
|
||||
@ -286,7 +312,7 @@ auto os_access_mock::find(std::vector<std::string> parts) const
|
||||
assert(root_);
|
||||
auto* de = root_.get();
|
||||
while (!parts.empty()) {
|
||||
if (!S_ISDIR(de->stat.st_mode)) {
|
||||
if (de->status.type() != posix_file_type::directory) {
|
||||
return nullptr;
|
||||
}
|
||||
de = de->find(parts.front());
|
||||
@ -299,22 +325,23 @@ auto os_access_mock::find(std::vector<std::string> parts) const
|
||||
}
|
||||
|
||||
void os_access_mock::add_internal(
|
||||
std::filesystem::path const& path, struct ::stat const& st,
|
||||
fs::path const& path, simplestat const& st,
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
var) {
|
||||
auto parts = splitpath(path);
|
||||
|
||||
if (S_ISDIR(st.st_mode) && std::holds_alternative<std::monostate>(var)) {
|
||||
if (st.type() == posix_file_type::directory &&
|
||||
std::holds_alternative<std::monostate>(var)) {
|
||||
var = std::make_unique<mock_directory>();
|
||||
}
|
||||
|
||||
if (parts.empty()) {
|
||||
assert(!root_);
|
||||
assert(S_ISDIR(st.st_mode));
|
||||
assert(st.type() == posix_file_type::directory);
|
||||
assert(std::holds_alternative<std::unique_ptr<mock_directory>>(var));
|
||||
root_ = std::make_unique<mock_dirent>();
|
||||
root_->stat = st;
|
||||
root_->status = st;
|
||||
root_->v = std::move(var);
|
||||
} else {
|
||||
auto name = parts.back();
|
||||
@ -326,8 +353,9 @@ void os_access_mock::add_internal(
|
||||
}
|
||||
|
||||
std::shared_ptr<dir_reader>
|
||||
os_access_mock::opendir(const std::string& path) const {
|
||||
if (auto de = find(path); de && S_ISDIR(de->stat.st_mode)) {
|
||||
os_access_mock::opendir(std::string const& path) const {
|
||||
if (auto de = find(path);
|
||||
de && de->status.type() == posix_file_type::directory) {
|
||||
std::vector<std::string> files{".", ".."};
|
||||
for (auto const& e :
|
||||
std::get<std::unique_ptr<mock_directory>>(de->v)->ent) {
|
||||
@ -339,15 +367,17 @@ os_access_mock::opendir(const std::string& path) const {
|
||||
throw std::runtime_error("oops");
|
||||
}
|
||||
|
||||
void os_access_mock::lstat(const std::string& path, struct ::stat* st) const {
|
||||
file_stat os_access_mock::symlink_info(std::string const& path) const {
|
||||
if (auto de = find(path)) {
|
||||
std::memcpy(st, &de->stat, sizeof(*st));
|
||||
return make_file_stat(de->status);
|
||||
}
|
||||
|
||||
throw std::runtime_error("oops");
|
||||
}
|
||||
|
||||
std::string
|
||||
os_access_mock::readlink(const std::string& path, size_t size) const {
|
||||
if (auto de = find(path); de && S_ISLNK(de->stat.st_mode)) {
|
||||
std::string os_access_mock::read_symlink(std::string const& path) const {
|
||||
if (auto de = find(path);
|
||||
de && de->status.type() == posix_file_type::symlink) {
|
||||
return std::get<std::string>(de->v);
|
||||
}
|
||||
|
||||
@ -355,8 +385,9 @@ os_access_mock::readlink(const std::string& path, size_t size) const {
|
||||
}
|
||||
|
||||
std::shared_ptr<mmif>
|
||||
os_access_mock::map_file(const std::string& path, size_t size) const {
|
||||
if (auto de = find(path); de && S_ISREG(de->stat.st_mode)) {
|
||||
os_access_mock::map_file(std::string const& path, size_t size) const {
|
||||
if (auto de = find(path);
|
||||
de && de->status.type() == posix_file_type::regular) {
|
||||
return std::make_shared<mmap_mock>(std::visit(
|
||||
overloaded{
|
||||
[this](std::string const& str) { return str; },
|
||||
@ -371,11 +402,11 @@ os_access_mock::map_file(const std::string& path, size_t size) const {
|
||||
throw std::runtime_error("oops");
|
||||
}
|
||||
|
||||
int os_access_mock::access(const std::string& path, int) const {
|
||||
int os_access_mock::access(std::string const& path, int) const {
|
||||
return access_fail_set_.count(path) ? -1 : 0;
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> find_binary(std::string_view name) {
|
||||
std::optional<fs::path> find_binary(std::string_view name) {
|
||||
auto path_str = std::getenv("PATH");
|
||||
if (!path_str) {
|
||||
return std::nullopt;
|
||||
@ -385,8 +416,8 @@ std::optional<std::filesystem::path> find_binary(std::string_view name) {
|
||||
folly::split(':', path_str, path);
|
||||
|
||||
for (auto dir : path) {
|
||||
auto cand = std::filesystem::path(dir) / name;
|
||||
if (std::filesystem::exists(cand) and ::access(cand.c_str(), X_OK) == 0) {
|
||||
auto cand = fs::path(dir) / name;
|
||||
if (fs::exists(cand) and ::access(cand.c_str(), X_OK) == 0) {
|
||||
return cand;
|
||||
}
|
||||
}
|
||||
|
@ -33,13 +33,29 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "dwarfs/file_stat.h"
|
||||
#include "dwarfs/os_access.h"
|
||||
#include "dwarfs/script.h"
|
||||
|
||||
struct stat;
|
||||
|
||||
namespace dwarfs::test {
|
||||
|
||||
struct simplestat {
|
||||
file_stat::ino_type ino;
|
||||
file_stat::mode_type mode;
|
||||
file_stat::nlink_type nlink;
|
||||
file_stat::uid_type uid;
|
||||
file_stat::gid_type gid;
|
||||
file_stat::off_type size;
|
||||
file_stat::dev_type rdev;
|
||||
file_stat::time_type atime;
|
||||
file_stat::time_type mtime;
|
||||
file_stat::time_type ctime;
|
||||
|
||||
posix_file_type::value type() const {
|
||||
return static_cast<posix_file_type::value>(mode & posix_file_type::mask);
|
||||
}
|
||||
};
|
||||
|
||||
class os_access_mock : public os_access {
|
||||
public:
|
||||
os_access_mock();
|
||||
@ -49,10 +65,10 @@ class os_access_mock : public os_access {
|
||||
|
||||
size_t size() const;
|
||||
|
||||
void add(std::filesystem::path const& path, struct ::stat const& st);
|
||||
void add(std::filesystem::path const& path, struct ::stat const& st,
|
||||
void add(std::filesystem::path const& path, simplestat const& st);
|
||||
void add(std::filesystem::path const& path, simplestat const& st,
|
||||
std::string const& contents);
|
||||
void add(std::filesystem::path const& path, struct ::stat const& st,
|
||||
void add(std::filesystem::path const& path, simplestat const& st,
|
||||
std::function<std::string()> generator);
|
||||
|
||||
void add_dir(std::filesystem::path const& path);
|
||||
@ -61,16 +77,15 @@ class os_access_mock : public os_access {
|
||||
|
||||
void set_access_fail(std::filesystem::path const& path);
|
||||
|
||||
std::shared_ptr<dir_reader> opendir(const std::string& path) const override;
|
||||
std::shared_ptr<dir_reader> opendir(std::string const& path) const override;
|
||||
|
||||
void lstat(const std::string& path, struct ::stat* st) const override;
|
||||
|
||||
std::string readlink(const std::string& path, size_t size) const override;
|
||||
file_stat symlink_info(std::string const& path) const override;
|
||||
std::string read_symlink(std::string const& path) const override;
|
||||
|
||||
std::shared_ptr<mmif>
|
||||
map_file(const std::string& path, size_t size) const override;
|
||||
map_file(std::string const& path, size_t size) const override;
|
||||
|
||||
int access(const std::string&, int) const override;
|
||||
int access(std::string const&, int) const override;
|
||||
|
||||
private:
|
||||
struct mock_directory;
|
||||
@ -80,7 +95,7 @@ class os_access_mock : public os_access {
|
||||
struct mock_dirent* find(std::filesystem::path const& path) const;
|
||||
struct mock_dirent* find(std::vector<std::string> parts) const;
|
||||
void add_internal(
|
||||
std::filesystem::path const& path, struct ::stat const& st,
|
||||
std::filesystem::path const& path, simplestat const& st,
|
||||
std::variant<std::monostate, std::string, std::function<std::string()>,
|
||||
std::unique_ptr<mock_directory>>
|
||||
var);
|
||||
@ -110,19 +125,6 @@ class script_mock : public script {
|
||||
}
|
||||
};
|
||||
|
||||
struct simplestat {
|
||||
::ino_t st_ino;
|
||||
::mode_t st_mode;
|
||||
::nlink_t st_nlink;
|
||||
::uid_t st_uid;
|
||||
::gid_t st_gid;
|
||||
::off_t st_size;
|
||||
::dev_t st_rdev;
|
||||
uint64_t atime;
|
||||
uint64_t mtime;
|
||||
uint64_t ctime;
|
||||
};
|
||||
|
||||
extern std::map<std::string, simplestat> statmap;
|
||||
|
||||
std::optional<std::filesystem::path> find_binary(std::string_view name);
|
||||
|
Loading…
x
Reference in New Issue
Block a user