12

I have a program that reads from a file and writes to a file. I'd like to prevent the user from specifying the same file for both (for obvious reasons). Lets say the first path is in char* path1 and the second path is in char* path2. can I fopen() both paths, call fileno() on each and get the same number?

To explain more clearly:

char* path1 = "/asdf"
char* path2 = "/asdf"

FILE* f1 = fopen(path1, "r");
FILE* f2 = fopen(path2, "w");

int fd1 = fileno(f1);
int fd2 = fileno(f2);

if(fd1 == fd2) {
  printf("These are the same file, you really shouldn't do this\n");
}

EDIT:

I do not want to compare filenames because one could easily defeat that with paths like /asdf/./asdf or by using symlinks. Ultimately, I do not want to write my output into the file that I'm reading from (could cause serious issues).

Huckle
  • 1,810
  • 3
  • 26
  • 40
  • 1
    No. On any reasonably implemented POSIX system, a new call to open will return a new file descriptor. Really, why not just compare the file names themselves for equality? –  Sep 19 '12 at 20:43
  • Generally, no. What's the "same file"? Two different file names (paths) can refer to the same chunk of data on disk with hard links.The same path and file can refer to two different chunks of data as well. How? Create file A, open file A, delete file A -- without closing it. Now create same file name A, open it again -- it's a different file opened with the same name. Two open descriptors opened with the same name, referring to different files. – Clinton Pierce Sep 19 '12 at 20:46
  • Why can't you just compare the file paths he/she gives and error if they're the same? – im so confused Sep 19 '12 at 20:48
  • Just tried it, the two file descriptors are different even when referencing the same file on disk. – Huckle Sep 19 '12 at 20:53
  • FYI, you can compare file paths too, by using [`realpath`](http://pubs.opengroup.org/onlinepubs/009695399/functions/realpath.html) to get the canonical, absolute path with all symlinks resolved. If you do use `realpath`, heed @clintp's warning: the file paths might resolve to the same thing but point to different files. Don't use this in security-conscious code. – nneonneo Sep 19 '12 at 20:58
  • 2
    @nneonneo: Using `realpath` for this purpose is subject to race conditions and other flaws. `fstat` on the open file descriptors is the only valid way to perform the test OP needs. – R.. GitHub STOP HELPING ICE Sep 20 '12 at 01:09

2 Answers2

28

Yes - compare the file device ID and inode. Per the <sys/stat.h> specification:

The st_ino and st_dev fields taken together uniquely identify the file within the system.

Use

int same_file(int fd1, int fd2) {
    struct stat stat1, stat2;
    if(fstat(fd1, &stat1) < 0) return -1;
    if(fstat(fd2, &stat2) < 0) return -1;
    return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
}
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • Although it's only a few extra lines, it does require an extra header. Any faster way you can think of? – Huckle Sep 19 '12 at 20:55
  • 4
    Is `#include ` that big of a deal? – nneonneo Sep 19 '12 at 20:56
  • 10
    Faster? do you want it to be fast, or do you want it to be correct? – wildplasser Sep 19 '12 at 21:04
  • Faster in terms of typing time rather than execution time. Should have clarified. – Huckle Sep 24 '12 at 14:28
  • 11
    Typing time is never a problem; development time (thinking of problem solutions, consulting references, etc.) and debugging time (fixing bugs in inadequate solutions, fixing typing errors) is. Don't ever try to reduce typing time and risk increasing dev or debug time; it will not be worth the effort. – nneonneo Sep 24 '12 at 16:19
-1

You can also use kcmp syscall to compare even file descriptors across processes (but also inside a process).

Mitar
  • 6,756
  • 5
  • 54
  • 86
  • 1
    How? `KCMP_FILE` compares file *descriptions*, and two separate file *descriptions* that the `kcmp()` call will say are not equal can refer to the same *file*. [File *descriptions*](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_258) are not the same as [file *descriptors*](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_166). `KCMP_FILES` appears to compare the entire set of file descriptors for the two processes, not if two descriptors refer to the same file. – Andrew Henle May 25 '23 at 15:51