5

In a linux kernel module is there a way to get a file name/path from an unsigned int fd?

I'm aware of this answer: How can I get a filename from a file descriptor inside a kernel module? but if I understand the code right, I need a struct files_struct too.

EDIT:

Please stop voting as duplicated as it isn't. I'm asking for a way to get file's name/path in plain C from a kernel module, not using system tools. Said in another way: running readlink on /procself/fd/ is not a good answer.

EDIT 2:

Kernel's syscall read ssize_t read(int fd, void *buf, size_t count); takes 3 arguments, one of them being a fd. It's obvious that somehow read is able to read from a single file (instead of all files inside an inode). The question is how.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
alexandernst
  • 14,352
  • 22
  • 97
  • 197
  • A file might have multiple paths in the case of hard links - how are you going to know you got the right one? – Carl Norum Jul 06 '13 at 16:33
  • possible duplicate of [Getting Filename from file descriptor in C](http://stackoverflow.com/questions/1188757/getting-filename-from-file-descriptor-in-c) – Carl Norum Jul 06 '13 at 16:33
  • @CarlNorum Maybe this can be circumvented by returning an array of strings. –  Jul 06 '13 at 16:33
  • I need to get whatever path is currently being used to access that file. So, /path/to/file.txt and /another/path/to/myfile.md both will be fine. – alexandernst Jul 06 '13 at 16:34
  • Without using `/proc/self/fd` all you can get is the inode using `fstat`, but [that's it](http://stackoverflow.com/questions/4529737/is-it-possible-getting-and-setting-file-name-with-struct-stat-descriptor), unless you're going to traverse the whole file system. A path points to an inode, not the other way round. – mata Jul 06 '13 at 20:14
  • @mata there must be some way of doing it. Mac is doing it via fcntl F_GETPATH. Anyways, if there isn't, what is the closest I can get? – alexandernst Jul 06 '13 at 21:49
  • @mata also please see my second edit. – alexandernst Jul 07 '13 at 00:41

1 Answers1

3

The code in the answer to the question that you reference is what you need to do. And yes, a struct files_struct from a task is needed, because a file descriptor is only meaningful in the context of a files_struct (usually, there is one of these per process). File descriptors aren't globally unique, just an index within an individual open file table.

If your code is running in process-context (eg. invoked through a syscall) then you can use current->files for the current task's files_struct. This is what read() does.

caf
  • 233,326
  • 40
  • 323
  • 462
  • I'm running that from a syscall, so, yes, I can get current->files. That's what I have after a few hours of reading, but I get garbage instead of what I really want. Where's the error? http://pastebin.com/RNrGRVsC – alexandernst Jul 07 '13 at 13:50
  • @alexandernst: You aren't doing correct locking / rcu dereferences, you're ignoring the `qstr->len` and you're only accessing the final element of the path. Use the helper functions as shown in http://stackoverflow.com/a/8250940/134633. – caf Jul 07 '13 at 14:27
  • Sorry for taking me so long to reply. Your answer from that other question worked indeed, I just wasn't doing the right way. That's how my function looks like: http://pastebin.com/707W1JDR There is one small question that I have. I don't have to kfree what path_from_fd returns, right? – alexandernst Jul 08 '13 at 22:58
  • 1
    @alexandernst: You shouldn't call `free_page((unsigned long)tmp);` until you are finished with the `pathname`. If you want to return a string that you can `kfree()` later, then you can use `kstrdup(pathname, GFP_KERNEL)` before calling `free_page()`, then return the result. – caf Jul 09 '13 at 01:31
  • I'm sorry but I have one more question. I implemented your solution and firstly I tought everything was fine, but then I realised that no matter what process calls this function, I only dev /dev/pts/XXX or pipe:[XXXXXXX], no other paths at all. Is this the expected behaviour? – alexandernst Jul 10 '13 at 00:08
  • 1
    @alexandernst: That is certainly likely when you're looking at file descriptor 0, 1 or 2 (stdin, stdout and stderr respectively), since those are almost always going to be connected to a pseudo-terminal (`/dev/pts/???`) or a pipe, rather than a disk file. What system call are you calling this function from? – caf Jul 10 '13 at 00:41
  • I just saw your other reply (the kstrdup one). Isn't that pathname = kstrdup(tmp, GFP_KERNEL);? About the syscall I'm using, it's __NR_read. You can see the actual code here: https://github.com/alexandernst/procmon/blob/master/procmon_kmodule/hookfns.c – alexandernst Jul 10 '13 at 07:23
  • @alexandernst: No, `tmp` is a pointer to the page that contains the path, but it doesn't necessarily start at the start of that page. `pathname` is a pointer within the allocated page, to the start of the path, so it's `pathname` that you want to pass to `kstrdup()` and `tmp` that you pass to `free_page()`. It is not unexpected that the vast majority of `read()` calls on a live system would be to pseudo-terminals or pipes. – caf Jul 10 '13 at 07:35
  • Ok, so char * finalpath = kstrdup(pathname, GFP_KERNEL); free_page(....tmp...); return finalpath; And kfree(finalpath) in the function that called get_path_from_fd, right? What about __NR_open? Will that show "real" paths? – alexandernst Jul 10 '13 at 07:40
  • @alexandernst: Yes, that looks right. By the way, if your `hooked_sys_read()` is going to call `kmalloc()` then it should check for failure before dereferencing the returned value. In the `sys_open` syscall you'll have access to the path passed from userspace. Note that not every file descriptor is created by a call to `sys_open` though. – caf Jul 10 '13 at 10:02
  • Sorry for bumping this question, but I was wondering if there is a way to get the real path (inside the FS) to a file from pipe[YYYYY] ? – alexandernst Aug 11 '13 at 02:12
  • 1
    @alexandernst: If you're seeing `pipe:[XXXXXX]` then the file descriptor is an anonymous pipe created by the `pipe()` system call - it doesn't *have* a path name. – caf Aug 11 '13 at 04:07