-1

What would be a good method to determine which directory a file is in? I would use realpath(), but that returns the absolute path of the file or directory to which a symlink is pointing.

For instance, if the argument is the basename of a file, and lstat() returns 0, I can confirm that the file exists. But for the purposes of the program I'm working on, I need to determine which directory that file is in.

The project is on GH, so I don't mind posting code here if it helps answer the question. Thanks!

UPDATE: Here are some specifics: The code is near L64. If the file, dir, or symlink is in .local/share/Trash.test/files/, I need to find the corresponding trashinfo file in .local/share/Trash.test/info/.trashinfo. Normally I truncate the return value realpath() at files/, then append info/, then append the basename and .trashinfo ext. and after that, it does what I need. But when I try to get the realpath of the symlink, the absolute path to it is returned, but it's the path to what the symlink points to (e.g. /home/andy/temp/.local/share/Trash.test/files/dnsmasq -> /usr/share/doc/dnsmasq

Andy Alt
  • 297
  • 2
  • 12
  • do want only the directory name only not absolute path? – lazyCoder Sep 16 '16 at 05:43
  • Starting with what? The *file handle* or the *file name* ? – selbie Sep 16 '16 at 05:44
  • It would be helpful if you could provide an example of what you want. For example if you have a file with absolute path `/home/foo/bar/hello.txt`, what part of that full path do you want? – Some programmer dude Sep 16 '16 at 05:45
  • 3
    so now the six-letter word github is being abbreviated into the two-letter GH? Why don't we abbreviate that into nothing? It would be "The project is on, so I don't mind..." – Mike Nakis Sep 16 '16 at 05:45
  • @BunkerBoy, the absolute path to the directory in which the file, symlink, or directory is located. – Andy Alt Sep 16 '16 at 05:49
  • The absolute path returned by `realpath()` doesn't contain any symlinks. To the extent there is a directory that holds the file name, then that path contains the name of the directory (all except the last component of the name is the path of the directory holding that file name). The file may also be found in other directories because of hard links (but they'll be on the same file system — error EXDEV prevents cross-device links). If you need a path relative to the current directory, that can be calculated; it might be a bit tricky, though. You'd need to specify accurately what you want. – Jonathan Leffler Sep 16 '16 at 05:56
  • A file can be in multiple directories at the same time, so there's no standard way to figure out the directory it lives in because it can not be done unambiguously. There's also a chance that the directory the file is in is inaccessible to you either because of permissions or even chroot. Whoever gave you the file needs to also tell you where it came from. – Art Sep 16 '16 at 06:01
  • The code is near [L64](https://github.com/andy5995/rmw/blob/f7a3ab115fee1f9dedb4398794c6060c1bd8a904/src/functions/restore_rmw.c#L64). If the file, dir, or symlink is in .local/share/Trash.test/files/, I need to find the corresponding trashinfo file in .local/share/Trash.test/info/.trashinfo. Normally I truncate the first string at /files, then append info/, then append the .trashinfo filename, and it does what I need. But when I try to get the realpath of the symlink, the absolute path to it is returned, but it's the path to what the symlink points to. – Andy Alt Sep 16 '16 at 06:02
  • Oh, are you looking for [`readlink()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html) then? Beware: it does not null terminate its output, which strikes me as an abhorrent piece of misdesign, but that's the way life goes sometimes. And you should add an MCVE ([MCVE]) to the question, along with material to set up the motivating example. – Jonathan Leffler Sep 16 '16 at 06:11
  • Note that line 64 in a file with complex data structures and a myriad other functions called and defined is not an MCVE. – Jonathan Leffler Sep 16 '16 at 06:27
  • If `realpath("/home/andy/temp/.local/share/Trash.test/files/dnsmasq", output)` is giving you the wrong information because `dnsmasq` is a symlink, then presumably, you need to use `realpath("/home/andy/temp/.local/share/Trash.test/files/", output)` to get the real path of the directory holding the `dnsmasq` directory entry, and you can simply append `/dnsmasq` to generate the file name. Or am I over-simplifying somehow? The POSIX [`dirname()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dirname.html) function may be of assistance. – Jonathan Leffler Sep 16 '16 at 06:34
  • @JonathanLeffler point noted about what constitutes an MCVE, thanks. Your suggestion about dirname() was interesting, and I just tried it. It doesn't work when just the basename is given as the file to restore. It does however, work with the example I provided. I'll have to give more thought to this after I've taken a break from it. Thank you very much, everyone. – Andy Alt Sep 16 '16 at 06:48

3 Answers3

0

No, realpath() does not return the absolute path of the file or directory to which a symlink is pointing. realpath() returns the absolute path of an existing file given a relative path, and as an additional benefit, if a symlink is involved in the relative path, then the symlink will be resolved.

A plain file name in the current directory is also a relative path. So, realpath() is the function you need.

Also: why not try it before posting a question?

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • I agree that `realpath()` is probably the required function, but your description of what it does has me puzzled. The POSIX specification for [`realpath()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html) says: _The `realpath()` function shall derive, from the pathname pointed to by file_name, an absolute pathname that resolves to the same directory entry, whose resolution does not involve '`.`', '`..`', or symbolic links._ The input name can be a relative or absolute path. – Jonathan Leffler Sep 16 '16 at 06:02
  • I do not understand what puzzles you. What I wrote is exactly in line with this definition. – Mike Nakis Sep 16 '16 at 06:04
  • Paraphrasing mildly and emphasis added, you said: "`realpath()` _does not_ return the absolute path of the file or directory to which a symlink is pointing" while POSIX says "`realpath()` _does_ return an absolute path of the file or directory to which a symlink is pointing". Those seem to be contradictory positions. What am I misunderstanding about your comment? – Jonathan Leffler Sep 16 '16 at 06:07
  • I did try it. Many times. I try realpath() on `/home/andy/temp/.local/share/Trash.test/files/dnsmasq` and get `/usr/share/doc returned`. – Andy Alt Sep 16 '16 at 06:08
  • Well, then please post precise, clear examples of a) exactly what you tried, b) exactly what you got, and c) exactly what you expected instead. That's how stackoverflow works. – Mike Nakis Sep 16 '16 at 06:11
  • @MikeNakis, I'm happy to post examples, but I usually wait until people ask for more specific information, so I'm not posting far more than is necessary. Is assuming someone didn't try something before asking them how stackoverflow works? – Andy Alt Sep 16 '16 at 06:16
  • @andy5995: Welcome to Stack Overflow. Please read the [About] and [Ask] pages soon. When you ask a question, you should provide enough information that it's clear you've done research on the issue, and you should ideally provide an MCVE ([MCVE]) which shows your problem, and discuss what you've tried, show your best effort, and ask about where you need help. Your information about file in `.local/share/…` should be in the question, along with details about the information you need to find, Ideally, we should be able to set up a simulation of your environment from the question. Note 'minimal'! – Jonathan Leffler Sep 16 '16 at 06:23
  • Thank you @JonathanLeffler that's helpful. :) I edited my original question and added more specifics. – Andy Alt Sep 16 '16 at 06:27
  • Andy, the phrase "realpath() returns an absolute path of the file or directory to which a symlink is pointing" constitutes a misleading deviation from the definition, because if read word-for-word it would lead one to believe that `realpath()` only accepts a symlink as a parameter, and all it does is to resolve the symlink. – Mike Nakis Sep 16 '16 at 11:10
  • But that's not all that `realpath()` does, and it does not even represent a common usage scenario of `realpath()`. As my answer says, the primary purpose and the most common usage of `realpath()` is to convert relative paths to absolute, which is precisely what you want according to your question. The fact that it also resolves symlinks is incidental, and irrelevant to the discussion at hand. If you focus on that, and think that this is what `realpath()` is all about, then you miss the point of `realpath()`, and you fail to see that it is suitable to solve your issue. – Mike Nakis Sep 16 '16 at 11:11
  • @MikeNakis Thank you for the detailed explanation; it helped to fill in some gaps. But I understood that it's not suitable to fit my needs, thats why I was looking for a different method. And I figured one out. I'll post the answer. – Andy Alt Sep 16 '16 at 18:58
0

there is no unambiguous answer to the question of "which directory hold this file". in unix-like filesystems, there can be any number of file names in any number of directories that all refer to the exact same file (hard links).

even realpath only says that it will give you one fully qualified pathname that refers to the file.

J Earls
  • 1,792
  • 8
  • 12
  • You could argue cogently that the `realpath()` functions gives an unambiguous answer to "which directory contains this file name", while recognizing that there may be other path names (relative and absolute) which also point to the same file name, and that still other path names may point to the same file contents by another name. – Jonathan Leffler Sep 16 '16 at 06:05
  • @JonathanLeffler it's not unambiguous if the answer realpath gives you depends on which name you give it. – J Earls Sep 16 '16 at 14:00
0

This is how I solved what I was trying to do.

This does some boundary checking. Not relevant to the problem (I see I need to add another check or two though).

  buf_check (argv[restore_request], PATH_MAX);

argv[restore_request] is the file to be restored. This can be an absolute or relative path, or it can just be a basename, depending on the current working directory.

  strcpy (file.relative_path, argv[restore_request]);

  file.base_name = basename (argv[restore_request]);

This places a '\0' in the string: str[strlen(str) - strlen (file.base_name)], in effect, chopping off the basename.

  truncate_str (file.relative_path, strlen (file.base_name));

I believe the remaining lines are self-explanatory

  strcpy (file.info, file.relative_path);
  strcat (file.info, "../info/");
  strcat (file.info, file.base_name);
  strcat (file.info, DOT_TRASHINFO);

Now the path to the info file can be found, which was the desired result. I apologize that my question was not clear and didn't provide enough details to properly illustrate my goal. I will make it a point to write better questions in the future. Thanks to all who took the time to give me feedback.

Andy Alt
  • 297
  • 2
  • 12