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.
Related
I have 2 programs:
#include "file1"
int main(void)
{
return (1);
}
where file1 is just an empty file located in the same directory as the program.
Then I have:
#include "~/file2"
int main(void)
{
return (1);
}
where file2 is an empty file but this time is located in my home directory.
The first program compiles, the second program complains and says file not found
Can someone explain what is going on here?
A directive like:
#include "file.h"
searches for a file whose name is file.h. The string ~/file2 in your example is not actually the name of a file. The ~ is expanded by the shell to the path of your home directory; the actual file name is something like /home/username/file2.
The mapping of strings to files can be complex, and it can vary from system to system, but in general there's a fair amount of syntax that's recognized by the shell and converted to a file name, but that doesn't form a file name itself. Variable names like $HOME are similar; you couldn't use $HOME in a #include directive.
Actually
#include "~/file2"
could be valid -- if you have a directory whose name is literally ~ containing a file whose name is file2. That would be legal but confusing.
You could use
#include "/home/username/file2"
but that ties the source to your particular home directory and would make it difficult for anyone else to use ut.
Usually a file to be included should have a name ending in .h, and its location should be either relative to the directory containing the source file, or in one of several locations searched by the compiler.
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.
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...
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.
I created a static library in C using Visual Studio. This library contains a function which accesses a text file stored in that current directory. The library was built properly. But the problem is that when I call the function from outside other project it is not loading that text file( I linked the .lib file properly everything else is working except for loading of that file).
Any ideas how to load a text file from .lib file just by relative path??
Thanks in advance..
The following is the library test function definition
int test()
{
FILE *fp = fopen("hello.txt", "r");
if(!fp) printf("File Error");
return 0;
}
The test.lib file is built and created for this.
Just accessing the current folder hello.txt file but when this function is called from other Project. it is saying File Error.
Modify your code to look at the errno:
#include <errno.h>
#include <string.h>
...
if(!fp) printf("File error: %s\n", strerror(errno));
And then look up the meaning of the errno on your operating system to see what's going on.
I'm pretty sure the fact that you're calling this function from a library is a red herring.
What's most likely happening is that your hello.txt file is not in the working directory of the executing process. Go ahead and #include <windows.h> in your project, and use the GetCurrentDirectory function to see what the working directory is when you run your program. Most likely, it's not the same path as your text file.
To remedy this, you can do one of two things: you can change the startup settings of the program (whether that's from Visual Studio or a Windows shortcut) to specify the working directory (called "Start in:" for a Windows shortcut) to be the path to the text file you want to open, or you can figure out what working directory your program has been using and move your text file there instead.
Edit: Also, if you want the application to use its own directory (where the executable file actually resides) you can use the GetModuleFileName function to get the full path of the executable. Of course, you'll have to trim the filename of the program off the end of the string it produces, but that should be a piece of cake.
Check your file path and print an errno, I think you have a static file path