Naming an anonymous file pointer - c

Given a file pointer fp which points to an open file, is there a portable way to give it a name? The function rename cannot be used in this case since I don't have a current name referring to the file.

On linux, you can use linkat
int linkat(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath, int flags);
by specifying the AT_EMPTY_PATH flag. For example, something like that:
linkat(fileno(fp), NULL, AT_FDCWD, "/path/to/new/name", AT_EMPTY_PATH);
Note that this does not rename the original file, it merely creates a new hard link to it (i.e. a new name). Also this approach is not portable, as the AT_EMPTY_PATH is a linux extension.

Related

How to create and write to a file on sdcard with linux?

I want to create a file on /dev/mmcblk0, if it doesn't exist already, and then write to it. My question is how to check if there is already such file on the sdcard and then access it, does it show up like /dev/mmcblk0/somefile?
/dev/mmcblk0 points to a drive, so you will need to mount the drive first before you can see what files are available on it or create new files.
You should use the command mount(8) to mount the device first. That will cause the device's filesystem to be attached to your system's file-system, and therefore, makes you able to access files on it just like you normally do. For example:
mount /dev/mmcblk0 /home/yooo123/sdcard
If all goes well, you can read and write files to it using fopen, fwrite, etc.
FILE *fp = fopen("/home/yooo123/sdcard/file.txt", "w");
...
fprintf(fp, "Hello, SD Card!\n");
However, if you want to do all of that from a C program, look up the mount(2) system call.
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);

stat alternative for long file paths

I'm writing a program that iterates through a directory tree depth first (similar to the GNU find program) by recursively constructing paths to each file in the tree and stores the relative paths of encountered files. It also collects some statistics about these files. For this purpose I'm using the stat function.
I've notices that this fails for very deep directory hierarchies, i.e. long file paths, in accordance with stat's documentation.
Now my question is: what alternative approach could I use here that is guaranteed to work for paths of any length? (I don't need working code, just a rough outline would be sufficient).
As you are traversing, open each directory you traverse.
You can then get information about a file in that directory using fstatat. The fstatat function takes an additional parameter, dirfd. If you pass a handle to an open directory in that parameter, the path is interpreted as relative to that directory.
int fstatat(int dirfd, const char *pathname, struct stat *buf,
int flags);
The basic usage is:
int dirfd = open("directory path", O_RDONLY);
struct stat st;
int r = fstatat(dirfd, "relative file path", &st, 0);
You can, of course, also use openat instead of open, as you recurse. And the special value AT_FDCWD can be passed as dirfd to refer to the current working directory.
Caveats
It is easy to get into symlink loops and recurse forever. It is not uncommon to find symlink loops in practice. On my system, /usr/bin/X11 is a symlink to /usr/bin.
Alternatives
There are easier ways to traverse file hierarchies. Use ftw or fts instead, if you can.

Does the c rename() function delete files?

I am practicing programming in the C programming language and was experimenting with the rename() function. I am using the following code:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
if(rename ("data", "database") )
{
fprintf(stderr, "Can't rename file\n");
exit(EXIT_FAILURE);
}
return 0;
}
This code changes the name of a file named, "data" to a file named, "database". I was wondering what would happen if you attempted to run this code, but already had a file named "database" in the same directory.
This is the contents of the directory that I have before running the rename() function:
And this is the contents of the directory that I have after running the rename() function:
It appears that the rename() function did correctly rename my file, but it also deleted the file that was already in this directory that had the same name. I was wondering if this is how the rename() function was designed to work or if this is something that my operating system (Windows 10 - cygwin64 - gcc compiler) is doing. Also, when using this function, should I first check to make sure that there are no files that already have the same name to prevent them from being deleted? Thank you for the help and insight.
You have to consult the documentation of your C library. According to the standard (N1570 7.21.4.2, emphasis mine):
The rename function causes the file whose name is the string pointed to by old to be
henceforth known by the name given by the string pointed to by new. The file named
old is no longer accessible by that name. If a file named by the string pointed to by new
exists prior to the call to the rename function, the behavior is implementation-defined.
In the case of gcc rename:
If oldname is not a directory, then any existing file named newname is removed during the renaming operation. However, if newname is the name of a directory, rename fails in this case.
In case of VS, however:
The new name must not be the name of an existing file or directory.
From cppreference.com:
If new_filename exists, the behavior is implementation-defined.
E.g. on Unix the behavior seems to be this (from man rename):
int
rename(const char *old, const char *new);
[…]
If new exists, it is first removed.
From rename doc :
If oldname is not a directory, then any existing file named newname is
removed during the renaming operation. However, if newname is the name
of a directory, rename fails in this case.

POSIX ways for generation of a temporary file with a nice file name

I want to generate a temporary file with a "nice" name like
my-app-Mar27-120357-Qf3K0a.html
while following the best practices for security.
POSIX offers me mkstemp(3) which takes a filename template (typically something like /tmp/my-app-XXXXXX) but it has two problems:
I need to choose the output directory myself. When I see glibc tempnam(3) (which is deprecated for a security reason) considers many factors, I wish to let the library function choose it.
There's no extension in the file name
The second item can be addressed by mkstemps(3) which takes a number of characters to keep as a user-defined extension. In my case, I can pass my-app-Mar27-120357-XXXXXX.html and 5
but it has its own problems:
I still need to choose the output directory
It isn't perfectly portable. NetBSD seems to lack it.
So I'm considering to use the deprecated tempnam(3) to generate a filename with the output directory path, overwrite the filename part with X and feed it to mkstemp(3), and then rename the file to my preferred format. So the problem lies in the last step, renaming without overwrite; is it possible in POSIX?
Or could there be any better alternatives?
Let mkstemp make the file it wants to make, in the POSIX-compliant way that it wants to. Use symlink to make a symbolic link from a source file and path of your choice to a destination that matches whatever comes from using mkstemp. Remove the symbolic link when you're done.
Another approach is to simply munge the template and add your path. We describe such a function in the BEDOPS toolkit here, used by the sort-bed application to allow the end user to specify where temporary intermediate files are stored: https://github.com/bedops/bedops/blob/6da835468565dfc30a3fcb65807e91fcf133ea2b/applications/bed/sort-bed/src/SortDetails.cpp#L115
FILE *
createTmpFile(char const* path, char** fileName)
{
FILE* fp;
int fd;
char* tmpl;
if (path == NULL)
{
fileName = NULL;
return tmpfile();
}
tmpl = static_cast<char*>( malloc(1 + strlen(path) + L_tmpnam) );
strcpy(tmpl, path);
strcpy(tmpl+strlen(path), "/sb.XXXXXX");
fd = mkstemp(tmpl);
if(fd == -1)
{
fprintf(stderr, "unable to create temp file!\n");
return NULL;
}
fp = fdopen(fd, "wb+");
*fileName = static_cast<char*>( malloc(strlen(tmpl) + 1) );
strcpy(*fileName, tmpl);
free(tmpl);
return fp;
}
This uses the L_tmpnam macro, part of the stdio library, to set the number of characters that the variable tmpl (the filename, ultimately) can store.
This compiles and works under Linux and OS X (BSD) hosts and also uses POSIX routines.
It is more complex than my other solution but it might work better for your use case.

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.

Resources