How to turn a regular file into a symlink on Linux
I'm writing an (un)archiving tool and the way it is designed it first creates a regular file from the archive before it examines the special attributes and may decide that this item is a symlink, in fact.
Note: Before more people misunderstand me for wanting to make a symlink of a file. No, I write th开发者_StackOverflow中文版e symlink data, i.e. its path, into the file, and then I want to tell the file system that this is a symlink
I've been developing this on OS X, where it's possible to turn a regular file into a symlink by simply setting its Type and Creator codes accordingly.
Now I like to get this code working on Linux as well. So I like to find a similar way there.
I am aware that the normal way to create a symlink is to call the symlink() function, but I wonder if there is also a way to change a regular file into a symlink, just like it's possible in OSX's BSD system, so that I do not have to refactor my working code too much?
There is lstat(), which returns the file type in st_mode's upmost bits. Now I wonder if there's also an analogous setter function for this mode field.
I don't believe there is a way in Linux to do this as you describe. IIRC, the filesystem stores symlink information in the inode table and not in a regular file so there's no direct way of turning a file into a link.
If the symlink's path is stored inside the file, why not read out the path, delete the file, and create a symlink in its place?
Demonstrating what I wrote as a comment to bmarguiles's answer,
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *buffer = 0, *name = 0;
int i;
for (i = 1; i < argc; i++) {
struct stat st;
int fd = open(argv[i], O_RDONLY);
fstat(fd, &st);
buffer = realloc(buffer, st.st_size + 1);
read(fd, buffer, st.st_size);
close(fd);
buffer[st.st_size] = '\0';
name = realloc(name, strlen(argv[i]) + 2);
sprintf(name, "%s~", argv[i]);
symlink(buffer, name);
rename(name, argv[i]);
}
free(buffer);
free(name);
return 0;
}
$ vi f2s.c ... $ cc -o f2s f2s.c $ echo -n / > test $ ./f2s test $ ls -l test lrwxrwxrwx 1 me me 1 Feb 24 23:17 test -> / $ echo -n / > test2 $ strace ./f2s test2 open("test2", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=1, ...}) = 0 read(3, "/", 1) = 1 close(3) = 0 symlink("/", "test2~") = 0 rename("test2~", "test2") = 0
This is just a demonstration; it really needs more error-handling and maybe a better temporary filename.
No, you can't turn one into the other. You have to unlink to kill the file and then symlink to create a symlink as a replacement.
精彩评论