Search code examples
solarissolaris-10dtrace

dtrace: How to get symlink target from file


I am using dtrace to record all files being deleted.

Is it possible to find out what the symlink target is (if it's a symlink)? I want to output the symlink filename and the target filename for logging in case I need to restore the link later.

One solution I came up with is to use the dtrace "system" function to call my own program that will print out the symlink target: system("myprogram %s") where %s is the full name of the symlink I want to print out the target for.

This works ok (it's a very short C program and does the job) - but is there a way to get the info directly through dtrace, seeing as we are handling the file anyway we should be able to look up the symlink target?

I had a look at the fileinfo_t struct but it doesn't have the symlink target: http://docs.oracle.com/cd/E18752_01/html/817-6223/chp-io-args.html#chp-io-fileinfo

This is my script so far: This is in Solaris 10. (This is probing for fop_remove rather than syscall::unlink and the reason for that is to be able to get the symlink directory in case the user doesn't specify the directory in the call to rm).

    #!/usr/sbin/dtrace -s

    #pragma D option quiet

    fbt::fop_remove:entry
    {
           self->dir = stringof(args[0]->v_path);
           self->file = stringof(args[1]);
    }

    fbt::fop_remove:return
    /self->file != NULL/
    {
           printf("%s/%s\n", self->dir, self->file);
           self->file = 0;
           self->dir = 0;
    }

thanks!


Solution

  • On Solaris VFS level (aka the vnode_t structure), the symlink target is not attached to the node for the symlink itself. It's a filesystem implementation detail.

    For UFS filesystems, if the pathname of the link target is shorter than 48 Bytes (what UFS calls a 'fast symlink'), it's recorded inline with the inode_t struct, and you can print the value through DTrace via:

    vnode = args[0];
    inode = (inode_t*)vnode->v_data;
    
    printf("symlink tgt: %47s\n",
        vnode->v_type != VLNK ||
        vnode->v_op != ufs_vnodeops ||
        inode->i_flags & I_FASTSYMLINK == 0 ?
            "[unresolved]" :
            (char *)inode->i_db);
    

    For other filesystems / in the generic case, you'd have to use a fsinfo::readlink:return (or fop::fop_readlink:return) probe points to get the target - on access, that is, it won't (usually) be retrievable directly from the vnode_t.