How can I find the path in rust-fuse? - rust

I am trying to write a FUSE interface for a REST API in Rust. I am using the rust-fuse library. I need the dir path in the readdir callback function when implementing the Filesystem trait, but the function only takes an inode!
How can I find the path to the file? Is it somehow embedded in the Request?
I could create an inode <-> path map, but that makes things too complicated. The Python and Haskell FUSE libraries both pass the path as a parameter to the callback functions rather than an inode.
fn readdir(&mut self,
req: &Request,
ino: u64,
_fh: u64,
offset: u64,
mut reply: ReplyDirectory) {
// ...
}

It appears the library doesn't provide this yet:
From the README (emphasis mine):
To Do
There's still a lot of stuff to be done. Feel free to contribute.
Interrupting a filesystem operation isn't handled yet. An additional
more high level API would be nice. It should provide pathnames instead
inode numbers and automatically handle concurrency and interruption
(like the FUSE C library's high level API).
It appears you will need to assign a unique inode when you open / list the directory/file, keep track of a mapping of inodes to paths, and use that later on.
Depending on your API structure, you may also be able to encode some amount of information into the inode directly. For example, maybe you have < 32 endpoints, so you can encode each endpoint as a 5-bit number and decode that later. Then only a subset of inodes need to have arbitrary values.

Related

How do I seek for holes and data in a sparse file in golang [duplicate]

I want to copy files from one place to another and the problem is I deal with a lot of sparse files.
Is there any (easy) way of copying sparse files without becoming huge at the destination?
My basic code:
out, err := os.Create(bricks[0] + "/" + fileName)
in, err := os.Open(event.Name)
io.Copy(out, in)
Some background theory
Note that io.Copy() pipes raw bytes – which is sort of understandable once you consider that it pipes data from an io.Reader to an io.Writer which provide Read([]byte) and Write([]byte), correspondingly.
As such, io.Copy() is able to deal with absolutely any source providing
bytes and absolutely any sink consuming them.
On the other hand, the location of the holes in a file is a "side-channel" information which "classic" syscalls such as read(2) hide from their users.
io.Copy() is not able to convey such side-channel information in any way.
IOW, initially, file sparseness was an idea to just have efficient storage of the data behind the user's back.
So, no, there's no way io.Copy() could deal with sparse files in itself.
What to do about it
You'd need to go one level deeper and implement all this using the syscall package and some manual tinkering.
To work with holes, you should use the SEEK_HOLE and SEEK_DATA special values for the lseek(2) syscall which are, while formally non-standard, are supported by all major platforms.
Unfortunately, the support for those "whence" positions is not present
neither in the stock syscall package (as of Go 1.8.1)
nor in the golang.org/x/sys tree.
But fear not, there are two easy steps:
First, the stock syscall.Seek() is actually mapped to lseek(2)
on the relevant platforms.
Next, you'd need to figure out the correct values for SEEK_HOLE and
SEEK_DATA for the platforms you need to support.
Note that they are free to be different between different platforms!
Say, on my Linux system I can do simple
$ grep -E 'SEEK_(HOLE|DATA)' </usr/include/unistd.h
# define SEEK_DATA 3 /* Seek to next data. */
# define SEEK_HOLE 4 /* Seek to next hole. */
…to figure out the values for these symbols.
Now, say, you create a Linux-specific file in your package
containing something like
// +build linux
const (
SEEK_DATA = 3
SEEK_HOLE = 4
)
and then use these values with the syscall.Seek().
The file descriptor to pass to syscall.Seek() and friends
can be obtained from an opened file using the Fd() method
of os.File values.
The pattern to use when reading is to detect regions containing data, and read the data from them – see this for one example.
Note that this deals with reading sparse files; but if you'd want to actually transfer them as sparse – that is, with keeping this property of them, – the situation is more complicated: it appears to be even less portable, so some research and experimentation is due.
On Linux, it appears you could try to use fallocate(2) with
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE to try to punch a hole at the
end of the file you're writing to; if that legitimately fails
(with syscall.EOPNOTSUPP), you just shovel as many zeroed blocks to the destination file as covered by the hole you're reading – in the hope
the OS will do the right thing and will convert them to a hole by itself.
Note that some filesystems do not support holes at all – as a concept.
One example is the filesystems in the FAT family.
What I'm leading you to is that inability of creating a sparse file might
actually be a property of the target filesystem in your case.
You might find Go issue #13548 "archive/tar: add support for writing tar containing sparse files" to be of interest.
One more note: you might also consider checking whether the destination directory to copy a source file resides in the same filesystem as the source file, and if this holds true, use the syscall.Rename() (on POSIX systems)
or os.Rename() to just move the file across different directories w/o
actually copying its data.
You don't need to resort to syscalls.
package main
import "os"
func main() {
f, _ := os.Create("/tmp/sparse.dat")
f.Write([]byte("start"))
f.Seek(1024*1024*10, 0)
f.Write([]byte("end"))
}
Then you'll see:
$ ls -l /tmp/sparse.dat
-rw-rw-r-- 1 soren soren 10485763 Jun 25 14:29 /tmp/sparse.dat
$ du /tmp/sparse.dat
8 /tmp/sparse.dat
It's true you can't use io.Copy as is. Instead you need to implement an alternative to io.Copy which reads a chunk from the src, checks if it's all '\0'. If it is, just dst.Seek(len(chunk), os.SEEK_CUR) to skip past that part in dst. That particular implementation is left as an exercise to the reader :)

How to monitor which files consumes iops?

I need to understand which files consumes iops of my hard disc. Just using "strace" will not solve my problem. I want to know, which files are really written to disc, not to page cache. I tried to use "systemtap", but I cannot understand how to find out which files (filenames or inodes) consumes my iops. Is there any tools, which will solve my problem?
Yeah, you can definitely use SystemTap for tracing that. When upper-layer (usually, a VFS subsystem) wants to issue I/O operation, it will call submit_bio and generic_make_request functions. Note that these doesn't necessary mean a single physical I/O operation. For example, writes from adjacent sectors can be merged by I/O scheduler.
The trick is how to determine file path name in generic_make_request. It is quite simple for reads, as this function will be called in the same context as read() call. Writes are usually asynchronous, so write() will simply update page cache entry and mark it as dirty, while submit_bio gets called by one of the writeback kernel threads which doesn't have info of original calling process:
Writes can be deduced by looking at page reference in bio structure -- it has mapping of struct address_space. struct file which corresponds to an open file also contains f_mapping which points to the same address_space instance and it also points to dentry containing name of the file (this can be done by using task_dentry_path)
So we would need two probes: one to capture attempts to read/write a file and save path and address_space into associative array and second to capture generic_make_request calls (this is performed by probe ioblock.request).
Here is an example script which counts IOPS:
// maps struct address_space to path name
global paths;
// IOPS per file
global iops;
// Capture attempts to read and write by VFS
probe kernel.function("vfs_read"),
kernel.function("vfs_write") {
mapping = $file->f_mapping;
// Assemble full path name for running task (task_current())
// from open file "$file" of type "struct file"
path = task_dentry_path(task_current(), $file->f_path->dentry,
$file->f_path->mnt);
paths[mapping] = path;
}
// Attach to generic_make_request()
probe ioblock.request {
for (i = 0; i < $bio->bi_vcnt ; i++) {
// Each BIO request may have more than one page
// to write
page = $bio->bi_io_vec[i]->bv_page;
mapping = #cast(page, "struct page")->mapping;
iops[paths[mapping], rw] <<< 1;
}
}
// Once per second drain iops statistics
probe timer.s(1) {
println(ctime());
foreach([path+, rw] in iops) {
printf("%3d %s %s\n", #count(iops[path, rw]),
bio_rw_str(rw), path);
}
delete iops
}
This example script is works for XFS, but needs to be updated to support AIO and volume managers (including btrfs). Plus I'm not sure how it will handle metadata reads and writes, but it is a good start ;)
If you want to know more on SystemTap you can check out my book: http://myaut.github.io/dtrace-stap-book/kernel/async.html
Maybe iotop gives you a hint about which process are doing I/O, in consequence you have an idea about the related files.
iotop --only
the --only option is used to see only processes or threads actually doing I/O, instead of showing all processes or threads

Acquire fuse_file_info in FUSE's truncate()

Is it possible to acquire the struct fuse_file_info* fi in the function truncate()? Why is it not there in the first place?
int truncate(const char* path, off_t size)
I'm storing my file descriptor in the file handler, fh, of the fuse_file_info structure. The function open() appears to be called beforehand so that structure is created for the file. The description of fh is: "File handle. May be filled in by filesystem in open(). Available in all other file operations".
(As a last resort I'm thinking of having a structure to store this information, saved into a hash map, and then use the file handler to store the key. This would allow me to search the structure, using the path, in order to find the respective file descriptor.)
Note: I'm actually using jnr-fuse but since it mimics libfuse I'm not asking specifically for it; what works for one should (sort of) work for the other.
Why is it not there in the first place?
Because of implementation of truncate in the Linux kernel. You can see signature here.

How can I read the routing table information from Kernel space?

I would like to be able to read the routing table from kernel space...
In user space, this information is readable in /proc/net/route, but I don't know how to read the same information from kernel space..
I don't want to modify, only read..
any ideas?
To fetch the routing table, you would need to send a message of type RTM_GETROUTE to the kernel using a socket of the AF_NETLINK family — this is the rtnetlink(7) interface.
For convenience, rather than sending messages over a socket, you can use the libnetlink(3) library, and call int rtnl_wilddump_request(struct rtnl_handle *rth, int family, RTM_GETROUTE).
For an even simpler cross-platform abstraction, you could use the libdnet library, which has a function int route_get(route_t *r, struct route_entry *entry).
You may find out where in kernel source code tree this file is created in '/proc' pseoudo filesystem by searching the "route" keyword or "create_proc_... smth" Take a look at how such files are created for your kernel in source.
I suspect it's located somewhere in net/ipv4/route.c

Implementing symlinks in a virtual file system

I'm working on a virtual file system which isn't disk based, kind of like /proc. Now I want to create a symlink within it to a target on a ext3 file system. I haven't found any standard documentation on ways to achieve this. What I've guessed so far is that I have to write a function to put in for symlink in struct inode_operations. But frankly I'm at a loss even with the function parameters.
If it matters, I started off with this tutorial on LWN: http://lwn.net/Articles/13325/
EDIT: I'm working with libfs, not FUSE at the moment
Presumably you're using fuse, if you're not, do :)
All you have to do is implement the getattr function to tell the kernel that the object is a symlink, then implement the readlink function and return the path that the link should link to; the kernel will do the rest.
I was able to accomplish it finally. Here is what I did (some details may differ depending on what the filesystem wants to achieve):
Create inode of the symlink with the S_IFLNK mode and add the target to the i_private field.
Implement follow_link because generic_readlink requires it to be present
static void *sample_follow_link (struct dentry *dentry, struct nameidata *nd)
{
nd->depth = 0;
nd_set_link(nd, (char *)dentry->d_inode->i_private);
return NULL;
}
static struct inode_operations sample_inode_ops = {
.readlink = generic_readlink,
.follow_link = sample_follow_link,
};
.....
//in the function for the dentry and inode creation
inode->i_op = sample_inode_ops
I would suggest to take a look at linux/fs/ext2/ source code. Files symlink.c, inode.c, namei.c and probably few others. You will get some idea as of what needs to be done. Contrary to expectation, filesystem code of the individual filesystems is actually very short and easy to read.
But maybe instead of creating new virtual filesystem, you might ask yourself another question, wouldn't fuse user level filesystem be enough in my case? They have slightly better documentation to creating virtual filesystems and a few more examples.

Resources