Retrieving the path from a file descriptor or FILE *? - c

Is there a way to retrieve path information from a file descriptor or FILE pointer?
I'm aware that this data may not always be relevant/useful for case where the file descriptor is a socket, pipe, etc.

I don't believe there's any portable way, but e.g. on Linux you can call readlink on "/proc/self/fd/fileno" and the kernel will give you a path if it can, or one of various kinds of funny-looking strings if not.

If you're lucky enough to be using Mac OS X you can use the following code:
#define _DARWIN_C_SOURCE
#include <sys/fcntl.h>
.
.
.
char pathbuf[PATH_MAX];
if (fcntl(fd, F_GETPATH, pathbuf) >= 0) {
// pathbuf now contains *a* path to the open file descriptor
}
Note that the path you get back is not necessarily the one used to open the file... If there are hard links, especially, the path you get back will be a valid path to the file.

There can be one or many names for a file, so no std way.
I am not sure if there can be something OS specific.

Related

Basics questions regarding File and I/O System Calls in C (on Linux/UNIX)

I'm working on improving my C programming knowledge, but I am having trouble understanding the man pages for the following Unix system calls:
open
create
close
unlink
read
write
lseek
The man pages for each of these are, for lack of a better term, completely confusing and unintelligible. For example, here is the man page for open:
"Given a pathname for a file, open() returns a file descriptor, a small, nonnegative integer for use in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.). The file descriptor returned by a successful call will be the lowest-numbered file descriptor not currently open for the process.
By default, the new file descriptor is set to remain open across an execve(2) (i.e., the FD_CLOEXEC file descriptor flag described in fcntl(2) is initially disabled; the O_CLOEXEC flag, described below, can be used to change this default). The file offset is set to the beginning of the file (see lseek(2)).
A call to open() creates a new open file description, an entry in the system-wide table of open files. This entry records the file offset and the file status flags (modifiable via the fcntl(2) F_SETFL operation). A file descriptor is a reference to one of these entries; this reference is unaffected if pathname is subsequently removed or modified to refer to a different file. The new open file description is initially not shared with any other process, but sharing may arise via fork(2)."
I have no idea what this all means. From my understanding, if open returns a negative integer, an error occurred, and if it returns a positive integer, then that integer can be used in further system calls (???). That is, unfortunately, basically the extent of my knowledge and what I can attempt to parse from the man page. I need some help.
What does it mean that it "returns the lowest-numbered file descriptor not currently open for the process"? What process is it referring to? Why is it the lowest-numbered file descriptor, and why does this matter/how would I use this? I hate to sound like an idiot but I honestly have no clue what it's talking about.
Let's take an example. Let's say I wanted to create a new file in a directory, and open up a file from another directory, and copy the file I opened into the file I created, while checking for errors along the way. This is my attempt:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int XYZ = creat("XYZ.doc", 0 );
if (XYZ < 0)
printf("file creating error");
int file = open("/usr/.../xx.xx", 0);
if(file < 0)
printf("file opening error");
}
How would I copy the file that I opened into the file that I created? That should be easy. But what if I wanted to copy the file that I opened in reverse to the file that I created? (Maybe that example will illuminate how to use the file offset stuff mentioned in the man page, which I don't currently understand...)
I would like to edit this post to write a layman's terms description next to each of these system calls, thus creating a good online resource for people to study from. Also, if anyone has any good references for these system calls in C, that would be much appreciated as well.
Error checking left out for simplicity sake:
char data[1024]; /* size of this chosen more or less on a whim */
ssize_t n;
while ((n = read(file, data, sizeof(data))) > 0) {
write(XYZ, data, n);
}
close(file);
close(XYZ);

How to move file from one folder to different folder in linux

How to move a particular file from one folder to another folder?
What I have tried,
#include <stdio.h>
int main() {
FILE *tFile;
if (tFile != NULL)
tFile = NULL;
if ((tFile = fopen("TempFile.txt", "rw")) == NULL) {
return -1;
}
mv("TempFile.txt", "../MST");
printf("Done Succesfully\n");
return 0;
}
Error :
test.c:17:2: warning: no newline at end of file
/tmp/ccKLWYNa.o(.text+0x5e): In function `main':
: undefined reference to `mv'
collect2: ld returned 1 exit status
Please guide me how can I do this.
You really should read Advanced Linux Programming and syscalls(2)
To move (from C) a file from one place to another in the same file system just use the rename(2) syscall.
At the very least, for your particular example, you'll need to code:
char* srcpath = "TempFile.txt"; // assume it is a variable path
char destpath[1024];
snprintf (destpath, sizeof(destpath), "../MST/%s", srcpath);
if (rename (srcpath, destpath)) {
// something went wrong
if (errno == EXDEV) {
// copy data and meta data
} else { perror("rename"); exit(EXIT_FAILURE); };
}
else { // the rename succeeded
}
If you really want to mv TempFile.txt ../MST/TempFile.txt specifically for TempFile.txt only you could just call rename("TempFile.txt", "../MST/TempFile.txt") and handle the error cases like I suggest. If you are sure that ../MST/lie in the same file system than . then EXDEV should not happen and you don't need to handle it particularly (but you do need to handle errors).
If you want to move a file between two different file systems, you have to copy the data (and perhaps some of the meta-data) yourself (and then remove e.g. with unlink(2)) the original source file). You could detect that situation by various means: you could just try the rename and if errno (see errno(3)) is EXDEV you need to copy the file. Or you could use stat(2) to query the source file(and the destination directory) meta-data -e.g. its size and its file system.
Of course, you need to understand what are files on Linux (or Posix), in particular what is an inode.... See inode(7) and credentials(7)
You could have used system with /bin/mv (but be careful about strange characters -like spaces or semicolons- in the file paths, you need to escape them to avoid code injection), apparently you don't want to.
You should play with strace(1) (or perhaps also ltrace) on mv in various situations to understand what it is doing. Also, study the source code of GNU coreutils which provides /bin/mv notably in mv.c ...
Some extra C or C++ libraries may provide you with functions to move files (in the same filesystem they should do a rename, in different file systems they copy the source file data and perhaps some meta-data and unlink the source, so cannot be atomic), e.g. in C g_file_move (from Gio with Glib from Gnome), or in C++ copy_file -followed by remove in Boost, etc etc....
PS. For temporary files see tmpfile(3), mkstemp(3), etc...

Using File Descriptors with readlink()

I have a situation where I need to get a file name so that I can call the readlink() function. All I have is an integer that was originally stored as a file descriptor via an open() command. Problem is, I don't have access to the function where the open() command executed (if I did, then I wouldn't be posting this). The return value from open() was stored in a struct that I do have access to.
char buf[PATH_MAX];
char tempFD[2]; //file descriptor number of the temporary file created
tempFD[0] = fi->fh + '0';
tempFD[1] = '\0';
char parentFD[2]; //file descriptor number of the original file
parentFD[0] = (fi->fh - 1) + '0';
parentFD[1] = '\0';
if (readlink(tempFD, buf, sizeof(buf)) < 0) {
log_msg("\treadlink() error\n");
perror("readlink() error");
} else
log_msg("readlink() returned '%s' for '%s'\n", buf, tempFD);
This is part of the FUSE file system. The struct is called fi, and the file descriptor is stored in fh, which is of type uint64_t. Because of the way this program executes, I know that the two linked files have file descriptor numbers that are always 1 apart. At least that's my working assumption, which I am trying to verify with this code.
This compiles, but when I run it, my log file shows a readlink error every time. My file descriptors have the correct integer values stored in them, but it's not working.
Does anyone know how I can get the file name from these integer values? Thanks!
If it's acceptable that your code becomes non portable and is tied to being run on a somewhat modern version of Linux, then you can use /proc/<pid>/fd/<fd>. However, I would recommend against adding '0' to the fd as a means to get the string representing the number, because it uses the assumption that fd < 10.
However it would be best if you were able to just pick up the filename instead of relying on /proc. At the very least, you can replace calls to the library's function with a wrapper function using a linker flag. Example of usage is gcc program.c -Wl,-wrap,theFunctionToBeOverriden -o program, all calls to the library function will be linked against __wrap_theFunctionToBeOverriden; the original function is accessible under the name __real_theFunctionToBeOverriden. See this answer https://stackoverflow.com/a/617606/111160 for details.
But, back to the answer not involving linkage rerouting: you can do it something like
char fd_path[100];
snprintf("/proc/%d/fd/%d", sizeof(fd_path), getpid(), fi->fh);
You should now use this /proc/... path (it is a softlink) rather than using the path it links to.
You can call readlink to find the actual path in the filesystem. However, doing so introduces a security vulnerability and I suggest against using the path readlink returns.
When the file the descriptor points at is deleted,unlinked, then you can still access it through the /proc/... path. However, when you readlink on it, you get the original pathname (appended with a ' (deleted)' text).
If your file was /tmp/a.txt and it gets deleted, readlink on the /proc/... path returns /tmp/a.txt (deleted). If this path exists, you will be able to access it!, while you wanted to access a different file (/tmp/a.txt). An attacker may be able to provide hostile contents in the /tmp/a.txt (deleted) file.
On the other hand, if you just access the file through the /proc/... path, you will access the correct (unlinked but still alive) file, even if the path claims to be a link to something else.

Audio file format that can be written without seeking

I want to write audio data to stdout, preferably using libsndfile. When I output WAV to /dev/stdout I manage to write the header, but then I get an error
Error : could not open file : /dev/stdout
System error : Illegal seek.
I assume this is related to http://www.mega-nerd.com/libsndfile/FAQ.html#Q017, some file formats cannot be written without seeks. However, when I try to output SF_FORMAT_AU | SF_FORMAT_PCM_16 instead, I still get the same Illegal seek error.
Are there any audio file formats that can be written completely without seeking?
I'm using Linux.
EDIT: It might be obvious, but RAW format works (without seeking). Unfortunately I need a format that has meta information like sample rate.
You should finish reading that FAQ... the link you give us has all the answers.
However, there is at least one file format (AU) which is specifically designed to be written to a pipe.
So use AU instead of WAV.
Also make sure that you open the SNDFILE object with sf_open_fd, and not sf_open_virtual (or sf_open):
SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo,
void *user_data) ;
If you use sf_open_fd, then libsndfile will use fstat to determine whether the file descriptor is a pipe or a regular file. If you use sf_open_virtual or sf_open, it will assume that the file is seekable. This appears to be a flaw in libsndfile, but you should be using sf_open_fd anyway.
Footnote: Don't open /dev/stdout to get standard output; it is already open and there is no need to open it again. Use file descriptor STDOUT_FILENO.
Ended outputting an "infinite" wav header, and then writing raw PCM data for as long as the audio lasts. Not really valid, but most players seem to understand anyway.
The wav header is here, in case anyone wants it: https://gist.github.com/1428176
You could write to a temp file (perhaps in /tmp), let the libsnd seek to modify the .wav(RIFF) header of the temp file, and then, after libsnd has closed the file, stream the temp file out to stdout.

Duplicate file descriptor with its own file offset

How can one create a new file descriptor from an existing file descriptor such that the new descriptor does not share the same internal file structure/entry in the file table? Specifically attributes such as file offset (and preferably permissions, sharing and modes) should not be shared between the new and old file descriptors.
Under both Windows and Linux, dup() will duplicate the file descriptor, but both descriptors still point to the same file structure in the process' file table. Any seeking on either descriptor will adjust the position for the other descriptors as well.
Note
I've since received answers for both Windows and Linux and adjusted the question a little too often, which has made it difficult for people to answer. I'll adjust my votes and accept the cleanest answer which covers both Windows and Linux. Apologies to all, I'm still new to the SO paradigm. Thanks for the great answers!
So basically, what you really want is to be given a file descriptor, and basically open the same file over again, to get a separate position, sharing, mode, etc. And you want to do this on Windows (where the "file descriptor" is basically a foreign object, not something used directly by the OS or the run-time library at all.
Amazingly enough, there is a way to do that, at least with MS VC++. All but two steps of it use only the Win32 API so porting to other compilers/libraries should be fairly reasonable (I think most supply versions of those two functions). Those are for converting a Unix-style file descriptor to a native Win32 file handle, and converting a native Win32 file handle back to a Unix-style file descriptor.
Convert file-descriptor to native file handle with _get_osfhandle()
Get a name for the file with GetFileInformationByHandleEx(FILE_NAME_INFO)1
Use CreateFile to open a new handle to that file
Create a file descriptor for that handle with _open_osfhandle()
Et voilĂ , we have a new file descriptor referring to the same file, but with its own permissions, position, etc.
Toward the end of your question, you make it sound like you also want the "permissions", but that doesn't seem to make any real sense -- the permissions attach to the file itself, not to how the file is opened, so opening or reopening the file has no effect on the file's permissions. If you really want to know the, you can get it with GetFileInformationByHandle, but be aware that file permissions in Windows are quite a bit different from the (traditional) file permissions in Unix. Unix has owner/group/world permissions on all files, and most systems also have ACLs (though there's more variation in how they work). Windows either has no permissions at all (e.g., files on FAT or FAT32) or else uses ACLs (e.g., files on NTFS), but nothing that's really equivalent to the traditional owner/group/world permissions most people are accustomed to on Unix.
Perhaps you're using "permissions" to refer to whether the file was open for reading, writing, or both. Getting that is considerably uglier than any of the preceding. The problem is that most of it is in the library, not Win32, so there's probably no way to do it that will be even close to portable between compilers. With MS VC++ 9.0 SP1 (not guaranteed for any other compiler) you can do this:
#include <stdio.h>
int get_perms(int fd) {
int i;
FILE * base = __iob_func();
for (i=0; i<_IOB_ENTRIES; i++)
if (base[i]._file == fd)
return base[i]._flag; // we've found our file
return 0; // file wasn't found.
}
Since this involved some spelunking, I wrote a quick test to verify that it might actually work:
#ifdef TEST
#include <io.h>
void show_perms(int perms, char const *caption) {
printf("File opened for %s\n", caption);
printf("Read permission = %d\n", (perms & _IOREAD)!=0);
printf("Write permission = %d\n", (perms & _IOWRT)!=0);
}
int main(int argc, char **argv) {
FILE *file1, *file2;
int perms1, perms2;
file1=fopen(argv[1], "w");
perms1 = get_perms(_fileno(file1));
fclose(file1);
file2=fopen(argv[1], "r");
perms2 = get_perms(_fileno(file2));
fclose(file2);
show_perms(perms1, "writing");
show_perms(perms2, "reading");
return 0;
}
#endif
And the results seem to indicate success:
File opened for writing
Read permission = 0
Write permission = 1
File opened for reading
Read permission = 1
Write permission = 0
You can then test that returned flag against _IOREAD, _IOWRT, and _IORW, which are defined in stdio.h. Despite my previous warnings, I should probably point out that I suspect (though I certainly can't guarantee) that this part of the library is fairly stable, so the real chances of major changes are probably fairly minimal.
In the other direction, however, there's basically no chance at all that it'll work with any other library. It could (but certainly isn't guaranteed to) work with the other compilers that use the MS library, such as Intel, MinGW or Comeau using MS VC++ as its back-end. Of those, I'd say the most likely to work would be Comeau, and the least likely MinGW (but that's only a guess; there's a good chance it won't work with any of them).
Requires the redistributable Win32 FileID API Library
So, I recommend reading up on this a little more. The dup() and related functions serve to create a duplicate value in the file descriptor table pointing to the same entry in the open file table. This is intended to have the same offset. If you call open(), you will create a new entry the open file table.
It doesn't make any sense to create a duplicate of a file descriptor and that new file descriptor have a different offset in the open file table (this seems to contradict what the word "duplicate" means).
I'm not sure what your question is actually. I mean, it isn't the same thing as a duplicate. You could read:
/proc/self/fd/[descriptor]
and get the string that was used to open that file descriptor; bear in mind this may provide some pitfalls, some of which you actually noted in your observation of calling open() again.
Maybe you can explain a little more and I can try to update to help.
Why don't you just open the file a second time with open() or CreateFile() on windows? This gives you all freedom of different access rights and separate offset.
This of course has the drawback that you you can not open the file exclusively, but it solves your problem very simply.

Resources