Repository: abique/tmfs
Branch: master
Commit: 41f5648927a8
Files: 12
Total size: 10.4 KB
Directory structure:
gitextract_ricezco8/
├── AUTHORS
├── CMakeLists.txt
├── ChangeLog
├── LICENSE
├── README.markdown
└── src/
├── get_real_path.cc
├── getattr.cc
├── main.cc
├── read.cc
├── readdir.cc
├── readlink.cc
└── tmfs.hh
================================================
FILE CONTENTS
================================================
================================================
FILE: AUTHORS
================================================
Alexandre Bique <bique.alexandre@gmail.com>
Tor Arne Vestbø <torarnv@gmail.com>
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.16)
project(TMFS CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(PkgConfig)
pkg_check_modules(FUSE REQUIRED fuse)
add_definitions(${FUSE_CFLAGS} -Wall)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(tmfs
src/main.cc
src/readdir.cc
src/read.cc
src/readlink.cc
src/getattr.cc
src/get_real_path.cc
)
link_directories(${FUSE_LIBRARY_DIRS})
target_link_libraries(tmfs ${FUSE_LIBRARIES})
install(TARGETS tmfs RUNTIME DESTINATION bin
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
================================================
FILE: ChangeLog
================================================
2013-03-12 Alexandre Bique <bique.alexandre@gmail.com>
* release: 4
* dependancies: need boost >= 1.42
* improves readme and help message
* handle hard-links inside of .HFS+ Private Directory Data
2012-07-23 Alexandre Bique <bique.alexandre@gmail.com>
* release: 3
2012-03-21 Alexandre Bique <bique.alexandre@gmail.com>
* license: change to MIT
2011-07-08 Alexandre Bique <bique.alexandre@gmail.com>
* release: 2
* dependancies: switched from C++0x to standard C++
* release: 1
================================================
FILE: LICENSE
================================================
Copyright (C) 2011-2012 Alexandre Bique
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.markdown
================================================
TMFS
====
```
__/\\\\\\\\\\\\\\\___/\\\\____________/\\\\___/\\\\\\\\\\\\\\\___/\\\\\\\\\\\___
_\///////\\\/////___\/\\\\\\________/\\\\\\__\/\\\///////////__/\\\/////////\\\_
_______\/\\\________\/\\\//\\\____/\\\//\\\__\/\\\____________\//\\\______\///__
_______\/\\\________\/\\\\///\\\/\\\/_\/\\\__\/\\\\\\\\\\\_____\////\\\_________
_______\/\\\________\/\\\__\///\\\/___\/\\\__\/\\\///////_________\////\\\______
_______\/\\\________\/\\\____\///_____\/\\\__\/\\\___________________\////\\\___
_______\/\\\________\/\\\_____________\/\\\__\/\\\____________/\\\______\//\\\__
_______\/\\\________\/\\\_____________\/\\\__\/\\\___________\///\\\\\\\\\\\/___
_______\///_________\///______________\///___\///______________\///////////_____
```
Time Machine File System is a read-only virtual filesystem which helps you to read your Apple's time machine backup.
This filesystem does not targets performances, it has been written for a friend who has lost his macbook and wants to recover its data on Linux.
It's actually not perfect, feel free to report bugs or suggestions at [https://github.com/abique/tmfs/issues][https://github.com/abique/tmfs/issues].
Enjoy!
How to use it?
--------------
First you have to mount your HFS partition, by doing something like:
`mount /dev/sdXX /mnt/hfs-root`
Then as root run:
`tmfs /mnt/hfs-root /mnt/tm-root -ouid=$(id -u),gid=$(id -g),allow_other`
Then as a normal user, go to the directory /mnt/tm-root/ and enjoy your data! :-)
Sparsebundle Disk Images
------------------------
I haven't used it myself and I can't comment too much on it, but in short you should be able to mount a sparse bundle using https://github.com/torarnv/sparsebundlefs and then mount tmfs on top of it. Please have a look at their project page there should be further explanation.
Dependancies
------------
- c++ 17
- cmake >= 3.16
- fuse
How to build and install it?
----------------------------
Manually, run these commands:
```
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
make
DESTDIR=install-test make install
```
Then if the installation looks ok to you in install-test/ do make install as root with `sudo make install`.
Internals
---------
Time Machine structure:
Snapshot root: `${hfs_root}/Backups.backupdb/${comp_name}/${date}/${disk_name}/`
Hardlink count equals to dir_id: `${hfs_root}/Backups.backupdb/${comp_name}/${date}/${disk_name}/.../Folder`
Real folder with data: `${hfs_root}/.HFS+ Private Directory Data/dir_${dir_id}/`
Our representation:
`/${comp_name}/${date}/${disk_name}/${Real root}`
================================================
FILE: src/get_real_path.cc
================================================
#include "tmfs.hh"
static std::string _get_real_path(const std::string & str)
{
// use the relative path so that the real_path doesn't get replaced
const auto clean_path = fs::path(str).relative_path();
fs::path real_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;
// let's resolv all the parts of the path
struct stat stbuf;
for (; it != clean_path.end(); ++it)
{
real_path /= *it;
// Does the file exists ?
if (lstat(real_path.string().c_str(), &stbuf))
return real_path.string();
// 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);
// 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();
}
std::string get_real_path(const std::string & str)
{
auto result = _get_real_path(str);
#ifndef NDEBUG
std::cout << "get_real_path(\"" << str << "\") -> " << result << std::endl;
#endif
return result;
}
================================================
FILE: src/getattr.cc
================================================
#include "tmfs.hh"
int tmfs_getattr(const char *path, struct stat *stbuf)
{
// get the real path
std::string real_path = get_real_path(path);
// and now just stat the real path
memset(stbuf, 0, sizeof(struct stat));
if (lstat(real_path.c_str(), stbuf))
return -errno;
return 0;
}
================================================
FILE: src/main.cc
================================================
#include "tmfs.hh"
int main(int argc, char ** argv)
{
if (argc < 3)
{
fprintf(stderr, "Usage: %s: <HFS+ mount point> <Time Machine mount point>"
" [FUSE options]\n", argv[0]);
return 2;
}
/* global structure setup */
tmfs::instance().hfs_root_ = fs::absolute(fs::path(argv[1]));
--argc;
for (int i = 1; i < argc; ++i)
argv[i] = argv[i + 1];
/* check that hfs_root is a directory */
struct stat st;
if (lstat(tmfs::instance().hfs_root_.c_str(), &st)) {
fprintf(stderr, "%s: %m\n", tmfs::instance().hfs_root_.c_str());
return 1;
}
if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "%s: is not a directory\n", tmfs::instance().hfs_root_.c_str());
return 1;
}
/* 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;
/* lets go */
fuse_main(argc, argv, &ops, NULL);
return 0;
}
================================================
FILE: src/read.cc
================================================
#include "tmfs.hh"
int tmfs_read(const char * path, char * buf, size_t nbytes, off_t offset,
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);
if (fd < 0)
return -errno;
// read the data and close
ssize_t bytes = pread(fd, buf, nbytes, offset);
close(fd);
if (bytes < 0)
return -errno;
return bytes;
}
================================================
FILE: src/readdir.cc
================================================
#include "tmfs.hh"
int tmfs_readdir(const char * path, void * buf, fuse_fill_dir_t filler_cb, off_t offset,
struct fuse_file_info * fi)
{
// get the real path
std::string real_path = get_real_path(path);
// checks if it's really a directory
if (!fs::is_directory(real_path))
return -ENOTDIR;
struct stat stbuf;
// report ./ and ../
stbuf.st_mode = S_IFDIR | 0755;
stbuf.st_nlink = 2;
filler_cb(buf, ".", &stbuf, 0);
filler_cb(buf, "..", &stbuf, 0);
// now iterate over the real directory
DIR * dir = opendir(real_path.c_str());
if (!dir)
return 0;
struct dirent * entry;
while ((entry = readdir(dir)))
{
// stat the file pointed by entry
auto file_path = fs::path(path) / entry->d_name;
if (tmfs_getattr(file_path.string().c_str(), &stbuf))
continue;
stbuf.st_mode |= 0755;
// report the entry
filler_cb(buf, entry->d_name, &stbuf, 0);
}
closedir(dir);
return 0;
}
================================================
FILE: src/readlink.cc
================================================
#include "tmfs.hh"
int tmfs_readlink(const char * path, char * buf, size_t size)
{
if (size < 1)
return 0;
// get the real path
std::string real_path = get_real_path(path);
// and now just readlink the real path
ssize_t result = readlink(real_path.c_str(), buf, size - 1);
if (result < 0)
return -errno;
buf[result] = 0;
return 0;
}
================================================
FILE: src/tmfs.hh
================================================
#pragma once
#define FUSE_USE_VERSION 26
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <fuse.h>
#include <map>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
/** this is the global structure of tmfs */
struct tmfs {
inline const fs::path & hfs_root() const noexcept { return hfs_root_; }
static inline tmfs & instance() { static tmfs i; return i; }
fs::path hfs_root_; // the hfs root
};
/** 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);
/** fuse functions
* @{ */
int tmfs_getattr(const char * path, struct stat *stbuf);
int tmfs_readdir(const char * path, void * buf, fuse_fill_dir_t filler_callback,
off_t offset, 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);
/** @} */
gitextract_ricezco8/
├── AUTHORS
├── CMakeLists.txt
├── ChangeLog
├── LICENSE
├── README.markdown
└── src/
├── get_real_path.cc
├── getattr.cc
├── main.cc
├── read.cc
├── readdir.cc
├── readlink.cc
└── tmfs.hh
SYMBOL INDEX (12 symbols across 7 files)
FILE: src/get_real_path.cc
function _get_real_path (line 3) | static std::string _get_real_path(const std::string & str)
function get_real_path (line 42) | std::string get_real_path(const std::string & str)
FILE: src/getattr.cc
function tmfs_getattr (line 3) | int tmfs_getattr(const char *path, struct stat *stbuf)
FILE: src/main.cc
function main (line 3) | int main(int argc, char ** argv)
FILE: src/read.cc
function tmfs_read (line 3) | int tmfs_read(const char * path, char * buf, size_t nbytes, off_t offset,
FILE: src/readdir.cc
function tmfs_readdir (line 3) | int tmfs_readdir(const char * path, void * buf, fuse_fill_dir_t filler_c...
FILE: src/readlink.cc
function tmfs_readlink (line 3) | int tmfs_readlink(const char * path, char * buf, size_t size)
FILE: src/tmfs.hh
type tmfs (line 29) | struct tmfs {
method tmfs (line 31) | static inline tmfs & instance() { static tmfs i; return i; }
type stat (line 41) | struct stat
type fuse_file_info (line 43) | struct fuse_file_info
type fuse_file_info (line 45) | struct fuse_file_info
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (12K chars).
[
{
"path": "AUTHORS",
"chars": 80,
"preview": "Alexandre Bique <bique.alexandre@gmail.com>\nTor Arne Vestbø <torarnv@gmail.com>\n"
},
{
"path": "CMakeLists.txt",
"chars": 616,
"preview": "cmake_minimum_required(VERSION 3.16)\n\nproject(TMFS CXX)\n\nset(CMAKE_CXX_STANDARD 17)\n\nfind_package(PkgConfig)\npkg_check_m"
},
{
"path": "ChangeLog",
"chars": 503,
"preview": "2013-03-12 Alexandre Bique <bique.alexandre@gmail.com>\n\n\t* release: 4\n\t* dependancies: need boost >= 1.42\n\t* improves "
},
{
"path": "LICENSE",
"chars": 1064,
"preview": "Copyright (C) 2011-2012 Alexandre Bique\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
},
{
"path": "README.markdown",
"chars": 2614,
"preview": "TMFS\n====\n\n```\n__/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\___/\\\\\\\\____________/\\\\\\\\___/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\___/\\\\\\\\\\\\\\\\\\\\\\___\n _\\///////\\\\\\/////___\\/\\"
},
{
"path": "src/get_real_path.cc",
"chars": 1480,
"preview": "#include \"tmfs.hh\"\n\nstatic std::string _get_real_path(const std::string & str)\n{\n // use the relative path so that the "
},
{
"path": "src/getattr.cc",
"chars": 298,
"preview": "#include \"tmfs.hh\"\n\nint tmfs_getattr(const char *path, struct stat *stbuf)\n{\n // get the real path\n std::string real_p"
},
{
"path": "src/main.cc",
"chars": 992,
"preview": "#include \"tmfs.hh\"\n\nint main(int argc, char ** argv)\n{\n if (argc < 3)\n {\n fprintf(stderr, \"Usage: %s: <HFS+ mount p"
},
{
"path": "src/read.cc",
"chars": 454,
"preview": "#include \"tmfs.hh\"\n\nint tmfs_read(const char * path, char * buf, size_t nbytes, off_t offset,\n struct fuse_"
},
{
"path": "src/readdir.cc",
"chars": 965,
"preview": "#include \"tmfs.hh\"\n\nint tmfs_readdir(const char * path, void * buf, fuse_fill_dir_t filler_cb, off_t offset,\n "
},
{
"path": "src/readlink.cc",
"chars": 360,
"preview": "#include \"tmfs.hh\"\n\nint tmfs_readlink(const char * path, char * buf, size_t size)\n{\n if (size < 1)\n return 0;\n\n // "
},
{
"path": "src/tmfs.hh",
"chars": 1207,
"preview": "#pragma once\n\n#define FUSE_USE_VERSION 26\n\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <"
}
]
About this extraction
This page contains the full source code of the abique/tmfs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (10.4 KB), approximately 3.2k tokens, and a symbol index with 12 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.