On Sun, Oct 21, 2012 at 16:01:23 +0200, Dominik George wrote:
> When opening local URLs and the file is a symbolic link, iceweasel resolves
> them to their target before opening. This breaks relative links in the 
> document
> and also deviates from the behaviour when using symbolic links on a webserver

This bug makes it effectively impossible to browse any documentation in
a git-annex repository.  I'm using an LD_PRELOAD hack (attached) to work
around it:
  ./gen-with-links-as-files \
    | gcc -shared -fPIC -x c -pipe -ldl -o with-links-as-files.so -
  LD_PRELOAD=`pwd`/with-links-as-files.so /usr/bin/iceweasel

(Only the __lxstat* parts are really needed in this case.)

-- Michael
#!/bin/sh

cat_lxstat () {
        cat <<__EOF__

        int
        __lxstat$1(int ver, const char *path, struct stat$1 *buf)
        {
                static int (*real_lxstat$1)(int, const char*, struct stat$1*);
                static int (*real_xstat$1)(int, const char*, struct stat$1*);
                int ret = -1;

                if (!real_lxstat$1) {
                        real_lxstat$1 = dlsym(RTLD_NEXT, "__lxstat$1");
                        real_xstat$1 = dlsym(RTLD_NEXT, "__xstat$1");
                        if (!real_xstat$1 || !real_lxstat$1) {
                                errno = ELIBACC;
                                goto out;
                        }
                }

                ret = real_lxstat$1(ver, path, buf);
                if (ret != 0) goto out;

                if (S_ISLNK(buf->st_mode)) {
                        struct stat$1 tmp;

                        ret = real_xstat$1(ver, path, &tmp);
                        if (ret != 0) {
                                // We can still use the lstat$1 result.
                                ret = 0;
                                goto out;
                        }

                        if (S_ISDIR(tmp.st_mode)) {
                                *buf = tmp;
                        } else if (S_ISREG(tmp.st_mode)
                                        && ((tmp.st_mode & 0111) == 0)) {
                                *buf = tmp;
                        }
                }
        out:
                return ret;
        }
__EOF__
}

cat_readdir () {
        cat <<__EOF__

        static void
        fix_dirent$1(DIR *dir, struct dirent$1 *ent)
        {
                if (ent->d_type == DT_LNK || ent->d_type == DT_UNKNOWN) {
                        int fd = openat(dirfd(dir), &ent->d_name[0], O_RDONLY);
                        if (fd >= 0) {
                                struct stat64 stbuf;
                                if (fstat64(fd, &stbuf) == 0) {
                                        if (S_ISREG(stbuf.st_mode)) {
                                                ent->d_type = DT_REG;
                                        } else if (S_ISDIR(stbuf.st_mode)) {
                                                ent->d_type = DT_DIR;
                                        } else if (S_ISFIFO(stbuf.st_mode)) {
                                                ent->d_type = DT_FIFO;
                                        } else if (S_ISCHR(stbuf.st_mode)) {
                                                ent->d_type = DT_CHR;
                                        } else if (S_ISBLK(stbuf.st_mode)) {
                                                ent->d_type = DT_BLK;
                                        } else if (S_ISSOCK(stbuf.st_mode)) {
                                                ent->d_type = DT_SOCK;
                                        }
                                }
                                do {} while (close(fd) != 0 && errno == EINTR);
                        }
                }
        }

        struct dirent$1*
        readdir$1(DIR *dir)
        {
                static struct dirent$1* (*real_readdir$1)(DIR*);
                struct dirent$1 *ret = NULL;

                if (!real_readdir$1) {
                        real_readdir$1 = dlsym(RTLD_NEXT, "readdir$1");
                        if (!real_readdir$1) {
                                errno = ELIBACC;
                                goto out;
                        }
                }

                ret = real_readdir$1(dir);
                if (ret) {
                        fix_dirent$1(dir, ret);
                }
        out:
                return ret;
        }

        int
        readdir$1_r(DIR *dir, struct dirent$1 *entry, struct dirent$1 **result)
        {
                static int (*real_readdir${1}_r)(DIR*,
                                struct dirent$1*, struct dirent$1*);
                int ret = -1;

                if (!real_readdir${1}_r) {
                        real_readdir${1}_r = dlsym(RTLD_NEXT, "readdir${1}_r");
                        if (!real_readdir${1}_r) {
                                errno = ELIBACC;
                                goto out;
                        }
                }

                ret = real_readdir${1}_r(dir, entry, *result);
                if (ret == 0 && *result) {
                        fix_dirent$1(dir, *result);
                }
        out:
                return ret;
        }
__EOF__
}

cat_preload () {
        cat <<__EOF__
        #define _GNU_SOURCE
        #include <dlfcn.h>
        #include <dirent.h>
        #include <errno.h>
        #include <fcntl.h>
        #include <stdio.h>
        #include <string.h>
        #include <sys/socket.h>
        #include <sys/stat.h>
        #include <sys/types.h>
        #include <unistd.h>

        $(cat_lxstat '')
        $(cat_lxstat '64')
        $(cat_readdir '')
        $(cat_readdir '64')
__EOF__
}

cat_preload

Attachment: signature.asc
Description: Digital signature

Reply via email to