19

I observe behavior such as this on my Mac:

  • Open a PDF with PDF Expert, make some changes to the file, move the file in Finder, save it in PDF Expert and it'll be correctly saved to the new place.
  • Open a shell in a directory like ~/foo, trash the directory with another app and the shell's pwd correctly outputs ~/.Trash/foo.

What's happening under the hood? These cases seem to indicate apps don't just hold an absolute path of the file like emacs (am I right with this?), or is it a totally differently mechanism?

3 Answers 3

21

macos has a special /.vol/ system mapped to the actual directory and files. The files and directories are accessible via /.vol/<device_id>/<inode_number>, regardless of where the files are on the file system.

It is a nice little system.

So, programs can for example get the inode number of /Users/jdoe/someFile.txt and then open it via /.vol/12345/6789 (in this case, device id is 12345 and inode number 6789). You then move /Users/jdoe/someFile.txt anywhere you want (on the same volume) and everything just works. You can even write a shell script that supports this magic.

ls -di <file> to get inode number.

$ ls -di /User/jdoe/someFile.txt
6789 /User/jdoe/someFile.txt

EDIT:

You use stat to get the id of the volume and inode number, according to linked answer as highlighted by IMSoP.

GetFileInfo /.vol/12345/6789 would return the current location of the file previously located in /Users/jdoe/someFile.txt.

See https://stackoverflow.com/questions/11951328/is-there-any-function-to-retrieve-the-path-associated-with-an-inode for more information.

7
  • 1
    According to linked answers, stat is a more useful command here than ls -di, since it tells you the volume/device ID as well as the file ID/inode number.
    – IMSoP
    Nov 8, 2017 at 9:49
  • 4
    In Debian I have no /.vol/ and this still happens (although I need pwd -P, only then the output of plain pwd is updated). I guess programs don't have to open files via any special path because in general they get (and keep) file descriptors that are mapped to inodes by the kernel anyway. I suspect on Mac /.vol/ is not essential as well. Nov 8, 2017 at 11:40
  • So if you move a file to a different disk, this scheme breaks. Nov 8, 2017 at 15:01
  • 1
    @JoelCoehoorn Yes, but technically you can't move a file to a different disk. You can copy it to the other disk, then delete it, and there are shortcuts to do this as "one step", but it is still a copy-and-delete, not a move, so, technically a different file.
    – ibrewster
    Nov 8, 2017 at 20:43
  • 1
    Many text editors read a given file, close it, work with its copy and save to the same path, so they recreate the file in its old location. But they might keep the file open all the time and write to it at the very end. My bash on Debian does that. I run exec 3<>foo, then moved foo within the same filesystem, echo whatever >&3, then checked foo in the new location -- and it's changed. Although bash cannot seek within the file, other programs in general can. My point is /.vol/ is not essential, programs can easily work like this without it. Or I don't get what the difference is. Nov 9, 2017 at 13:02
1

The answer below is false (see comments). Please ignore


In addition to the good answer thecarpy gave, it is likely that your programs are simply holding a file handle, which is independent of the files location in the directory tree (and on Unix systems even persists the file deletion, at least until you close it).

A file handle is basically the direct access to the file, independent of where or how often (in case of hardlinks) it exists in the directory structure.

3
  • Nope, you did not get it either, I think ... see my comment to @KamilMaciorowski. The filehandle does not change, when you then save the file, a new file is created at the original location .... not so on macos!
    – thecarpy
    Nov 9, 2017 at 11:59
  • 1
    You are correct, that is very unexpected and very unlike Unix. :(
    – Tom
    Nov 9, 2017 at 12:06
  • Agreed and upvoted!
    – thecarpy
    Nov 9, 2017 at 12:15
0

While I am unsure why macos uses this instead of standard C functionality, assuming what I read years ago in "Mac OS X Unleashed" is correct, it turns out I learned something new, again.

Please look at the following simple C program:

#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    struct timespec ts;
        ts.tv_sec = 10;
        ts.tv_nsec = 0;
    FILE * fp;

    fp = fopen("file.txt", "a");
    int f = fileno(fp);

    if (fp == NULL)
    {
        printf("Error opening file!\n");
        exit(1);
    }

    struct stat file_stat;
    int ret;
    ret = fstat (f, &file_stat);
    printf("inode number is %d\n", file_stat.st_ino);
    nanosleep(&ts, NULL);

    printf("Finished sleep, writing to file.\n");

/* print some text */
    const char *text = "Write this to the file";
    dprintf(f, "Some text: %s\n", text);

/* print integers and floats */
    int i = 1;
    float py = 3.1415927;
    dprintf(f, "Integer: %d, float: %f\n", i, py);

/* printing single characters */
    char c = 'A';
    dprintf(f, "A character: %c\n", c);

    close(f);
}

Compile the program, run it in the background and quickly mv file.txt file2.txt BEFORE the program prints "Finished sleep, writing to file." (you have 10 seconds)

Notice that file2.txt has the output of your program although it was moved before the text was printed to the file (via file descriptor).

$ gcc myfile.c
$ ./a.out &
[1] 21416
$ inode number is 83956
$ ./mv file.txt file2.txt
$ Finished sleep, writing to file.
[1]+  Done                    ./a.out
$ cat file2.txt
Some text: Write this to the file
Integer: 1, float: 3.141593
A character: A

DISCLAIMER: I have not pruned the "include" list, this was quickly hacked together to prove a point.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .