Skip to content

WIP: Improve performance #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ include_directories(${Boost_INCLUDE_DIRS})

add_executable(tmfs
src/main.cc
src/readdir.cc
src/read.cc
src/dir.cc
src/file.cc
src/readlink.cc
src/getattr.cc
src/get_real_path.cc
Expand Down
71 changes: 71 additions & 0 deletions src/dir.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "tmfs.hh"

typedef struct dirhandle {
DIR * dir;
fs::path real_path;
} dirhandle;

int tmfs_opendir(const char * path, struct fuse_file_info * fi)
{
// get the real path
fs::path real_path = get_real_path(path);

// checks if it's really a directory
if (!fs::is_directory(real_path))
return -ENOTDIR;

dirhandle * dh = new dirhandle;
dh->dir = opendir(real_path.c_str());
if (dh->dir == 0)
return -errno;

dh->real_path = real_path;
fi->fh = (intptr_t)dh;

return 0;
}

int tmfs_releasedir(const char * path, struct fuse_file_info * fi)
{
dirhandle * dh = (dirhandle *)fi->fh;
int res = closedir(dh->dir);
delete dh;
return res;
}

int tmfs_readdir(const char * path, void * buf, fuse_fill_dir_t filler_cb, off_t offset,
struct fuse_file_info * fi)
{
struct stat stbuf;

dirhandle * dh = (dirhandle *)fi->fh;

// XXX: Midnight Commander seems to be doing something weird, and calls readdir twice
// on the same directory. As we report everything in one go this means the second call
// produces an empty output.
// See also https://github.com/littlefs-project/littlefs-fuse/issues/43
// This is a workaround for that, and it's obviously quite expensive for big directories ...
rewinddir(((dirhandle *)fi->fh)->dir);

// report ./ and ../
stbuf.st_mode = S_IFDIR | 0755;
stbuf.st_nlink = 2;
filler_cb(buf, ".", &stbuf, 0);
filler_cb(buf, "..", &stbuf, 0);

struct dirent * entry;
while ((entry = readdir(dh->dir)))
{
// Skip '.' and '..', we already reported those.
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;

// stat the file pointed by entry
if (getattr_at(dh->real_path, entry->d_name, &stbuf))
continue;
stbuf.st_mode |= 0755;
// report the entry
filler_cb(buf, entry->d_name, &stbuf, 0);
}
return 0;
}
22 changes: 16 additions & 6 deletions src/read.cc → src/file.cc
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
#include "tmfs.hh"

int tmfs_read(const char * path, char * buf, size_t nbytes, off_t offset,
struct fuse_file_info * fi)
int tmfs_open(const char * path, struct fuse_file_info * fi)
{
// get the real path
std::string real_path = get_real_path(path);

// open the file
int fd = open(real_path.c_str(), O_RDONLY);
int fd = open(real_path.c_str(), fi->flags);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading through this, original code was better I think and more explicit:

Suggested change
int fd = open(real_path.c_str(), fi->flags);
int fd = open(real_path.c_str(), O_RDONLY);

if (fd < 0)
return -errno;
fi->fh = fd;
return 0;
}

// read the data and close
ssize_t bytes = pread(fd, buf, nbytes, offset);
close(fd);
int tmfs_release(const char * path, struct fuse_file_info * fi)
{
close(fi->fh);
return 0;
}

int tmfs_read(const char * path, char * buf, size_t nbytes, off_t offset,
struct fuse_file_info * fi)
{
// read the data
ssize_t bytes = pread(fi->fh, buf, nbytes, offset);
if (bytes < 0)
return -errno;
return bytes;
Expand Down
52 changes: 36 additions & 16 deletions src/get_real_path.cc
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
#include "tmfs.hh"

static std::string _get_real_path(const std::string & str)
static fs::path _get_real_path(const fs::path & known_real_path, const std::string & relative_path)
{
// use the relative path so that the real_path doesn't get replaced
const auto clean_path = fs::path(str).relative_path();
const auto clean_path = fs::path(relative_path).relative_path();
auto it = clean_path.begin();

fs::path real_path(tmfs::instance().hfs_root());
real_path /= "Backups.backupdb"; // ${hfs_root}/Backups.backupdb/
fs::path real_path;
if (known_real_path.empty()) {
// Start from the root of the HFS
real_path = fs::path(tmfs::instance().hfs_root());
real_path /= "Backups.backupdb"; // ${hfs_root}/Backups.backupdb/

// ok let's copy the 3 first part of the virtual path
// (${comp_name}, ${date}, ${disk_name})
auto it = clean_path.begin();
for (int i = 0; i < 3 && it != clean_path.end(); ++i, ++it)
real_path /= *it;
// ok let's copy the 3 first part of the virtual path
// (${comp_name}, ${date}, ${disk_name})
for (int i = 0; i < 3 && it != clean_path.end(); ++i, ++it)
real_path /= *it;
} else {
// Start from the known real path, which should already contain the reference to the correct
// Backups.backupdb sub-directory in the HFS root.
real_path = known_real_path;
}

fs::path private_data_dir = tmfs::instance().hfs_root() / ".HFS+ Private Directory Data\r";
std::string dir_name_prefix = "dir_";

// let's resolv all the parts of the path
struct stat stbuf;
Expand All @@ -21,29 +32,38 @@ static std::string _get_real_path(const std::string & str)
real_path /= *it;
// Does the file exists ?
if (lstat(real_path.string().c_str(), &stbuf))
return real_path.string();
return real_path;

// Is the file a dir_id ?
if (S_ISREG(stbuf.st_mode) && stbuf.st_size == 0 && stbuf.st_nlink > 0)
{
// build the real path
fs::path dir_path = tmfs::instance().hfs_root();
dir_path /= ".HFS+ Private Directory Data\r/dir_" + std::to_string(stbuf.st_nlink);
std::string dir_name = dir_name_prefix + std::to_string(stbuf.st_nlink);
fs::path dir_path = private_data_dir / dir_name;

// check if it's really a ${dir_id}
if (stat(dir_path.c_str(), &stbuf))
continue; // it's not
real_path = dir_path; // it is
}
}
return real_path.string();
return real_path;
}

std::string get_real_path(const std::string & str)
fs::path get_real_path(const std::string & str)
{
auto result = _get_real_path(str);
auto result = _get_real_path("", str);
#ifndef NDEBUG
std::cout << "get_real_path(\"" << str << "\") -> " << result << std::endl;
std::cout << "get_real_path(\"" << str << "\") -> " << result << std::endl;
#endif
return result;
}

fs::path get_real_path_at(const fs::path & known_real_path, const std::string & relative_path)
{
auto result = _get_real_path(known_real_path, relative_path);
#ifndef NDEBUG
std::cout << "get_real_path_at(" << known_real_path << ", \"" << relative_path << "\") -> " << result << std::endl;
#endif
return result;
}
11 changes: 8 additions & 3 deletions src/getattr.cc
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
#include "tmfs.hh"

int tmfs_getattr(const char *path, struct stat *stbuf)
{
return getattr_at("", path, stbuf);
}

int getattr_at(const fs::path & known_real_path, const std::string & relative_path, struct stat *stbuf)
{
// get the real path
std::string real_path = get_real_path(path);
fs::path real_path = get_real_path_at(known_real_path, relative_path);

// and now just stat the real path
memset(stbuf, 0, sizeof(struct stat));
if (lstat(real_path.c_str(), stbuf))
if (lstat(real_path.string().c_str(), stbuf))
return -errno;
return 0;
}
}
12 changes: 8 additions & 4 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ int main(int argc, char ** argv)
/* vtable setup */
struct fuse_operations ops;
memset(&ops, 0, sizeof (ops));
ops.read = tmfs_read;
ops.getattr = tmfs_getattr;
ops.readdir = tmfs_readdir;
ops.readlink = tmfs_readlink;
ops.open = tmfs_open;
ops.release = tmfs_release;
ops.read = tmfs_read;
ops.getattr = tmfs_getattr;
ops.opendir = tmfs_opendir;
ops.readdir = tmfs_readdir;
ops.releasedir = tmfs_releasedir;
ops.readlink = tmfs_readlink;

/* lets go */
fuse_main(argc, argv, &ops, NULL);
Expand Down
40 changes: 0 additions & 40 deletions src/readdir.cc

This file was deleted.

10 changes: 9 additions & 1 deletion src/tmfs.hh
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,22 @@ struct tmfs {
};

/** transforms a virtual paths in the tmfs's root to the real path in hfs's root */
std::string get_real_path(const std::string & path);
fs::path get_real_path(const std::string & path);
fs::path get_real_path_at(const fs::path & known_real_path, const std::string & relative_path);

/** get the attributes of a file relative to the known real path */
int getattr_at(const fs::path & known_real_path, const std::string & relative_path, struct stat * stbuf);

/** fuse functions
* @{ */
int tmfs_getattr(const char * path, struct stat *stbuf);
int tmfs_opendir(const char * path, struct fuse_file_info * fi);
int tmfs_readdir(const char * path, void * buf, fuse_fill_dir_t filler_callback,
off_t offset, struct fuse_file_info * fi);
int tmfs_releasedir(const char * path, struct fuse_file_info * fi);
int tmfs_read(const char * path, char * buf, size_t nbytes, off_t offset,
struct fuse_file_info * fi);
int tmfs_readlink(const char * path, char * buf, size_t size);
int tmfs_open(const char * path, struct fuse_file_info * fi);
int tmfs_release(const char * path, struct fuse_file_info * fi);
/** @} */