6

How can I get the absolute path of a symbolic link? If I do it in the following way:

char buf[100];
realpath(symlink, buf);

I won't get the absolute path of the symlink, but instead I would get the absolute path this symlink links to. Now my question is: What If I want to get the absolute path of the symlink itself? Is there any function in Linux C that allows me to do so?

Note: What I'd like to achieve is the absolute path of the symbolic link itself, not the path it's pointing to! For example, if the relative path of a symbolic link is Task2/sym_lnk, I want its absolute path, which can be /home/user/kp/Task2/sym_lnk.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Kindermann
  • 403
  • 1
  • 7
  • 17

2 Answers2

3

You can use realpath() function with the parent folder of the symlink, then you concatenate the symlink name.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>

// Find the last occurrence of c in str, otherwise returns NULL
char* find_last_of( char *str, char c )
{
    for( char *i = str + strlen(str) ; i >= str ; i-- )
        if( *i == c )
            return i;
    return NULL;
}

// Does the job
char* getAbsPath( char *path  )
{
    char *name; // Stores the symlink name
    char *tmp; // Aux for store the last /
    char *absPath = malloc( PATH_MAX ); // Stores the absolute path

    tmp = find_last_of( path, '/' );

    // If path is only the symlink name (there's no /), then the
    // parent folder is the current work directory
    if( tmp == NULL ){ 
        name = strdup( path );
        getcwd( absPath, PATH_MAX ); // Is already absolute path
    }
    else{
        // Extract the name and erase it from the original
        // path.
        name = strdup( tmp + 1 );
        *tmp = '\0';
        // Get the real path of the parent folder.
        realpath( path, absPath );
    }
    // Concatenate the realpath of the parent and  "/name"
    strcat( absPath, "/" );
    strcat( absPath, name );
    free( name );
    return absPath;
}

// Test the function
int main( int argc, char **argv )
{
    char *absPath;

    if( argc != 2 ){
        fprintf( stderr, "Use:\n\n %s <symlink>\n", *argv );
        return -1;
    }
    // Verify if path exists
    if( access( argv[1], F_OK ) ){
        perror( argv[1] );
        return -1;
    }

    absPath = getAbsPath( argv[1] );
    printf( "Absolute Path: %s\n", absPath );
    free( absPath );
    return 0;
}

If you use the above code with directories, it needs an special case for "." and "..", but works with "./" and "../"

reroman
  • 31
  • 2
  • The standard function equivalent to `find_last_of()` is `strrchr()`. Note that if the character sought is not in the string, the code in `find_last_of()` decrements `i` (an unusual name for a `char *` variable) to before the start of the array. That is not guaranteed to work. – Jonathan Leffler Jun 16 '22 at 13:52
1

You can use the system call readlink():

int readlink(const char* fdpath, char* filepath, size_t pathsize);

fdpath is the path of the symbolic link, something like /proc/pid/fd/link_number.

filepath is the path of the file it's pointing to.

Note that readlink() does not null terminate the output string, but does reports its length in the return value.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Haris
  • 12,120
  • 6
  • 43
  • 70
  • how can i determine the last argm '256'? How can I know the length of filepath beforehand? – Kindermann Nov 03 '15 at 09:54
  • @Kindermann, in my project what i did was take a big enough string to fit any path. Unless its some ridiculously long [path, i think you will do good with 256 or 512 – Haris Nov 03 '15 at 10:07
  • @Kindermann: "how can i determine the last argm '256'?" see `PATH_MAX` constant from `limits.h` – Dummy00001 Nov 03 '15 at 13:54
  • @Haris, can you have a look at my Note above. I wanted to get the abs path of the symbolic link itself, but filepath is the path of the file its pointing to...are they the same? – Kindermann Nov 03 '15 at 13:54
  • @Kindermann: Alternatively, use a dynamically allocated buffer, and call `readlink` in a loop; as long as `readlink` keeps returning the same value as `bufsiz`, you `realloc` the buffer (increasing by multiples or by some fixed number of bytes at a time) and try again. When the return doesn't match the `bufsiz`, manually insert the `NUL` at the return value's index (because `readlink` doesn't do it for you) and you're done. To test, start with a small `bufsiz` so you can verify you expand correctly. – ShadowRanger Nov 03 '15 at 14:00