On Wed, Aug 22, 2012 at 5:31 AM, Roland Mainz <roland.ma...@nrubsig.org> wrote:
> Attached (as "gisburn_ast_openat_emulation20120820.c.txt") is a first
> try for an |openat()|*co. emulation code.
[snip]

Attached (as "gisburn_fsat_emu_002_ast_map_fsat.h.txt" and
"gisburn_fsat_emu_002_ast_fsat_emu.c.txt") is a slightly newer, more
complete version...

----

Bye,
Roland

-- 
  __ .  . __
 (o.\ \/ /.o) roland.ma...@nrubsig.org
  \__\/\/__/  MPEG specialist, C&&JAVA&&Sun&&Unix programmer
  /O /==\ O\  TEL +49 641 3992797
 (;O/ \/ \O;)
#ifndef AST_MAP_FSAT_H
#define AST_MAP_FSAT_H 1
/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*             Copyright (c) 2012 AT&T Intellectual Property            *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*          http://www.eclipse.org/org/documents/epl-v10.html           *
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*               Roland Mainz <roland.ma...@nrubsig.org>                *
*                                                                      *
***********************************************************************/

/*
 * This header defines provide an easy way for legacy applications
 * to use the new POSIX |*at()|-APIs.
 *
 * This is mainly intended for operating systems which already map
 * the old calls to the new |*at()| calls, therefore the macros
 * below bypass a lot of wrapper code in libc.
 * The other usage is _testing_ the code in libc and the |*at())
 * emulation code in libast
 */

/*
 * How should we map these with CPP ? ;-(
 *
 * open(path, flag, ...)        openat(AT_FDCWD, (path), (flag), ...)
 * creat(path, ...)             openat(AT_FDCWD, (path), 
O_WRONLY|O_CREAT|O_TRUNC, ...)
 */

#define unlink(path)                    unlinkat(AT_FDCWD, (path), 0)
#define rmdir(path)                     unlinkat(AT_FDCWD, (path), AT_REMOVEDIR)
#define chown(path, uid, gid)           fchownat(AT_FDCWD, (path), (uid), 
(gid), 0)
#define lchown(path, uid, gid)          fchownat(AT_FDCWD, (path), (uid), 
(gid), AT_SYMLINK_NOFOLLOW)
#define fchown(fd, uid, gid)            fchownat((fd), NULL, (uid), (gid), 0)
#define stat(path, sb)                  fstatat(AT_FDCWD, (path), (sb), 0)
#define lstat(path, sb)                 fstatat(AT_FDCWD, (path), (sb), 
AT_SYMLINK_NOFOLLOW)
#define fstat(fd, sb)                   fstatat((fd), NULL, (sb), 0)
#define rename(oldname, newname)        renameat(AT_FDCWD, (oldname), AT_FDCWD, 
(newname))
#define access(path, amode)             faccessat(AT_FDCWD, (path), (amode), 0)
#define eaccess(path, amode)            faccessat(AT_FDCWD, (path), (amode), 
AT_EACCESS)
#define mkdir(path, amode)              mkdirat(AT_FDCWD, (path), (amode))
#define mkfifo(path, amode)             mkfifoat(AT_FDCWD, (path), (amode))
#define mknod(path, amode, adev)        mknodat(AT_FDCWD, (path), (amode), 
(adev))

#endif /* !AST_MAP_FSAT_H */
/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*             Copyright (c) 2012 AT&T Intellectual Property            *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*          http://www.eclipse.org/org/documents/epl-v10.html           *
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*               Roland Mainz <roland.ma...@nrubsig.org>                *
*                                                                      *
***********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <assert.h>

#ifdef AST_FSAT_TEST_RECURIVELY
#include "ast_map_fsat.h"
#endif

/*
 * Mappings |*()| call vs. |*at()| call:
 *
 * open(path, flag, ...)        openat(AT_FDCWD, path, flag, ...)
 * creat(path, ...)             openat(AT_FDCWD, path, 
O_WRONLY|O_CREAT|O_TRUNC, ...)
 * unlink(path)                 unlinkat(AT_FDCWD, path, 0)
 * rmdir(path)                  unlinkat(AT_FDCWD, path, AT_REMOVEDIR)
 * chown(path, uid, gid)        fchownat(AT_FDCWD, path, uid, gid, 0)
 * lchown(path, uid, gid)       fchownat(AT_FDCWD, path, uid, gid, 
AT_SYMLINK_NOFOLLOW)
 * fchown(fd, uid, gid)         fchownat(fd, NULL, uid, gid, 0)
 * stat(path, &sb)              fstatat(AT_FDCWD, path, &sb, 0)
 * lstat(path, &sb)             fstatat(AT_FDCWD, path, &sb, 
AT_SYMLINK_NOFOLLOW)
 * fstat(fd, &sb)               fstatat(fd, NULL, &sb, 0)
 * rename(oldname, newname)     renameat(AT_FDCWD, oldname, AT_FDCWD, newname)
 * access(path, amode)          faccessat(AT_FDCWD, path, amode, 0)
 * eaccess(path, amode)         faccessat(AT_FDCWD, path, amode, AT_EACCESS)
 * mkdir(path, amode)           mkdirat(AT_FDCWD, path, amode)
 * mkfifo(path, amode)          mkfifoat(AT_FDCWD, path, amode)
 * mknod(path, amode, adev)     mknodat(AT_FDCWD, path, amode, adev)
 *
 * Warning: if we cache the cwd fd globally we must observe |close()|
 * and |dup2()| usage carefully!
 */

/*
 * Note that /dev/fd/ on Solaris can NOT be used reliably to do
 * something like /dev/fd/$fd/src/hello/world profile
 *
 * Use of /dev/fd/$fd/src/hello/world on Linux seems to be 100%
 * safe, assuming /dev/fd/ is mounted - it may be missing from
 * chroot'ed environments.
 */
#define ASTOPENATEMU_USE_DEVFD 1

/* note: |AT_CWD| must always be a negative number < -256 */
#define AST_AT_FDCWD            (AT_FDCWD)

/* |open()|-flags */
#define AST_O_DIRECTORY         (O_DIRECTORY)
#define AST_O_SEARCH            (O_SEARCH)

/* extra |*at()| flags */
#define AST_AT_REMOVEDIR        (AT_REMOVEDIR)
#define AST_AT_SYMLINK_NOFOLLOW (AT_SYMLINK_NOFOLLOW)
#define AST_AT_EACCESS          (AT_EACCESS)


#define ASTOPENEMU_GLOBALDIR_LOCK
#define ASTOPENEMU_GLOBALDIR_UNLOCK

struct global_cwd
{
        const char      *name;
        int             fd;

        /* storage for |global_cwd.name|. We made this extra large */
        char            namebuff[(2*PATH_MAX)+128];

#ifdef ASTOPENATEMU_USE_DEVFD
        bool            has_devfd;      /* Remember whether we have /dev/fd/ */
        const char      *devfd_fs;      /* either "/dev/fd" or "/proc/self/fd" 
*/
#endif
};

static struct global_cwd global_cwd =
{
        NULL,
        -1,
        { '\0', },
#ifdef ASTOPENATEMU_USE_DEVFD
        true,
        "/dev/fd"       /* either "/dev/fd" or "/proc/self/fd" */
#endif
};

/*
 * Warning: This accesses |global_cwd| WITHOUT locking (for
 * performace and therefore without being reliable in threaded
 * applications. Hurray! ;-/ ()
 */
#define ASSERT_GLOBAL_CWD_VALID assert((global_cwd.fd >= 0) || (global_cwd.name 
!= NULL))

/* Repeat syscall in expr each time it gets hit with EINTR */
#define EINTR_REPEAT(expr) while((expr) && (errno == EINTR)) errno=0;


/* must be called inside 
|ASTOPENEMU_GLOBALDIR_LOCK|/ASTOPENEMU_GLOBALDIR_UNLOCK pair!! */
static
bool save_cwd_unlocked(void)
{
        if (global_cwd.fd<0)
        {
                EINTR_REPEAT((global_cwd.fd=open(".", O_RDONLY)) < 0);
                if (global_cwd.fd < 0)
                {
                        global_cwd.name=getcwd(global_cwd.namebuff, 
sizeof(global_cwd.namebuff));
                }
        }
        return (((global_cwd.fd>=0) || (global_cwd.name!=NULL))?true:false);
}

/* must be called inside 
|ASTOPENEMU_GLOBALDIR_LOCK|/ASTOPENEMU_GLOBALDIR_UNLOCK pair!! */
static
void restore_cwd_unlocked(void)
{
        ASSERT_GLOBAL_CWD_VALID;

        if (global_cwd.fd >= 0)
        {
                EINTR_REPEAT(fchdir(global_cwd.fd) < 0);
        }
        else if (global_cwd.name)
        {
                EINTR_REPEAT(chdir(global_cwd.name) < 0);
        }
        else
        {
                /*
                 * this should never happen - |save_cwd_unlocked()|
                 * will only return |true| (and therefore fill
                 * either |global_cwd.fd| or |global_cwd.name|)
                 * if it can save the cwd location somehow.
                 */
                assert(0);
        }
}

/*
 * intercept for |close()|
 * we need this to make sure the cached |global_cwd| stays valid
 */
int ast_close(int fd)
{
        ASSERT_GLOBAL_CWD_VALID;

        ASTOPENEMU_GLOBALDIR_LOCK
        if (fd == global_cwd.fd)
        {
                global_cwd.fd=-1;
        }
        ASTOPENEMU_GLOBALDIR_UNLOCK
        return (close(fd));
}

/*
 * intercept for |fchdir()|
 * we need this to make sure the cached |global_cwd| does not
 * become "stale"
 */
int ast_fchdir(int fd)
{
        int retval;
        int saved_errno;

        ASSERT_GLOBAL_CWD_VALID;

        ASTOPENEMU_GLOBALDIR_LOCK;

        /*
         * We do not check for |(dirfd != global_cwd.fd)| (to do
         * optimisations) here since the caller might be interested
         * in the return code (for example if the directory was
         * removed (or the "x" permission was removed) after the
         * |fd| was obtained).
         */
        retval=fchdir(fd);
        saved_errno=errno;

        if (retval >= 0)
        {
                global_cwd.fd=fd;
                global_cwd.name=NULL;
        }

        ASTOPENEMU_GLOBALDIR_UNLOCK;

        errno=saved_errno;
        return (retval);
}

/*
 * intercept for |chdir()|
 * we need this to make sure the cached |global_cwd| does not
 * become "stale"
 */
int ast_chdir(const char *path)
{
        int retval;
        int saved_errno;
        ASSERT_GLOBAL_CWD_VALID;

        ASTOPENEMU_GLOBALDIR_LOCK;

        retval=chdir(path);
        saved_errno=errno;

        if (retval >= 0)
        {
                global_cwd.fd=-1;
                global_cwd.name=NULL;
        }

        ASTOPENEMU_GLOBALDIR_UNLOCK;

        errno=saved_errno;
        return (retval);
}

int ast_openat(int dirfd, const char *pathname, int flags, mode_t mode)
{
        /*
         * use |open()| for pathname relative to the cwd or
         * if it is an absolute path name
         */
        if ((dirfd == AST_AT_FDCWD) || (pathname[0] == '/'))
        {
                return open(pathname, flags, mode);
        }

#ifdef ASTOPENATEMU_USE_DEVFD
        if (global_cwd.has_devfd)
        {
                char pathbuf[PATH_MAX+256];

                if (flags & AST_O_DIRECTORY)
                {
                        sprintf(pathbuf, "%s/%d/%s/.", global_cwd.devfd_fs, 
dirfd, pathname);
                }
                else
                {
                        sprintf(pathbuf, "%s/%d/%s", global_cwd.devfd_fs, 
dirfd, pathname);
                }
                return open(pathbuf, flags, mode);
        }
        else
#endif
        {
                int saved_errno;
                int returned_dir;

                ASTOPENEMU_GLOBALDIR_LOCK

                if (!save_cwd_unlocked())
                {
                        saved_errno=errno;
                        ASTOPENEMU_GLOBALDIR_UNLOCK
                        errno=saved_errno;
                        return (-1);
                }

                if (dirfd != global_cwd.fd)
                {
                        if (fchdir(dirfd) < 0)
                        {
                                saved_errno=errno;
                                ASTOPENEMU_GLOBALDIR_UNLOCK
                                errno=saved_errno;
                                return (-1);
                        }
                }

                returned_dir=open(pathname, flags, mode);
                saved_errno=errno;

                restore_cwd_unlocked();

                ASTOPENEMU_GLOBALDIR_UNLOCK

                errno=saved_errno;

                return (returned_dir);
        }
}

int ast_unlinkat(int dirfd, const char *pathname, int flags)
{
        if ((dirfd == AST_AT_FDCWD) || (pathname[0] == '/'))
        {
                if (flags & AST_AT_REMOVEDIR)
                {
                        flags&=~AST_AT_REMOVEDIR;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                return (-1);
                        }

                        return rmdir(pathname);
                }
                if (flags == 0)
                {
                        return unlink(pathname);
                }
                
                /* this happes if there are flags we don't know about */
                errno=ENOSYS;
                return (-1);
        }

#ifdef ASTOPENATEMU_USE_DEVFD
        if (global_cwd.has_devfd)
        {
                char pathbuf[PATH_MAX+256];

                sprintf(pathbuf, "%s/%d/%s", global_cwd.devfd_fs, dirfd, 
pathname);

                if (flags & AST_AT_REMOVEDIR)
                {
                        flags&=~AST_AT_REMOVEDIR;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                return (-1);
                        }

                        return rmdir(pathbuf);
                }
                if (flags == 0)
                {
                        return unlink(pathbuf);
                }
                else
                {
                        /* this happes if there are flags we don't know about */
                        errno=ENOSYS;
                        return (-1);
                }
        }
        else
#endif
        {
                int saved_errno;
                int retval;

                ASTOPENEMU_GLOBALDIR_LOCK

                if (!save_cwd_unlocked())
                {
                        saved_errno=errno;
                        ASTOPENEMU_GLOBALDIR_UNLOCK
                        errno=saved_errno;
                        return (-1);
                }

                if (dirfd != global_cwd.fd)
                {
                        if (fchdir(dirfd) < 0)
                        {
                                saved_errno=errno;
                                ASTOPENEMU_GLOBALDIR_UNLOCK
                                errno=saved_errno;
                                return (-1);
                        }
                }

                if (flags & AST_AT_REMOVEDIR)
                {
                        flags&=~AST_AT_REMOVEDIR;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                retval=-1;
                        }

                        retval=rmdir(pathname);
                        saved_errno=errno;
                }
                if (flags == 0)
                {
                        retval=unlink(pathname);
                        saved_errno=errno;
                }
                else
                {
                        /* this happes if there are flags we don't know about */
                        saved_errno=ENOSYS;
                        retval=-1;
                }

                restore_cwd_unlocked();

                ASTOPENEMU_GLOBALDIR_UNLOCK

                errno=saved_errno;

                return (retval);
        }
}

int ast_fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int 
flags)
{
        if ((dirfd == AST_AT_FDCWD) || (pathname[0] == '/'))
        {
                if (flags & AST_AT_SYMLINK_NOFOLLOW)
                {
                        flags&=~AST_AT_SYMLINK_NOFOLLOW;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                return (-1);
                        }
                        return lchown(pathname, owner, group);

                }
                else if (flags == 0)
                {
                        return chown(pathname, owner, group);
                }

                /* unsupported flags */
                errno=ENOSYS;
                return (-1);
        }

        if (pathname == NULL)
        {
                /* abort if there are any flags */
                if (flags)
                {
                        errno=ENOSYS;
                        return (-1);
                }
                return fchown(dirfd, owner, group);
        }

#ifdef ASTOPENATEMU_USE_DEVFD
        if (global_cwd.has_devfd)
        {
                char pathbuf[PATH_MAX+256];

                sprintf(pathbuf, "%s/%d/%s", global_cwd.devfd_fs, dirfd, 
pathname);
                if (flags & AST_AT_SYMLINK_NOFOLLOW)
                {
                        flags&=~AST_AT_SYMLINK_NOFOLLOW;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                return (-1);
                        }
                        return lchown(pathbuf, owner, group);

                }
                else if (flags == 0)
                {
                        return chown(pathbuf, owner, group);
                }
                else
                {
                        /* unsupported flags */
                        errno=ENOSYS;
                        return (-1);
                }
        }
        else
#endif
        {
                int saved_errno;
                int retval;

                ASTOPENEMU_GLOBALDIR_LOCK

                if (!save_cwd_unlocked())
                {
                        saved_errno=errno;
                        ASTOPENEMU_GLOBALDIR_UNLOCK
                        errno=saved_errno;
                        return (-1);
                }

                if (dirfd != global_cwd.fd)
                {
                        if (fchdir(dirfd) < 0)
                        {
                                saved_errno=errno;
                                ASTOPENEMU_GLOBALDIR_UNLOCK
                                errno=saved_errno;
                                return (-1);
                        }
                }

                if (flags & AST_AT_SYMLINK_NOFOLLOW)
                {
                        flags&=~AST_AT_SYMLINK_NOFOLLOW;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                retval=-1;
                        }
                        else
                        {
                                retval=lchown(pathname, owner, group);
                                saved_errno=errno;
                        }
                }
                else if (flags == 0)
                {
                        retval=chown(pathname, owner, group);
                        saved_errno=errno;
                }
                else
                {
                        /* unsupported flags */
                        errno=ENOSYS;
                        retval=-1;
                }

                restore_cwd_unlocked();

                ASTOPENEMU_GLOBALDIR_UNLOCK

                errno=saved_errno;

                return (retval);
        }
}

int ast_fstatat(int dirfd, const char *pathname, struct stat *buf, int flags)
{
        if ((dirfd == AST_AT_FDCWD) || (pathname[0] == '/'))
        {
                if (flags & AST_AT_SYMLINK_NOFOLLOW)
                {
                        flags&=~AST_AT_SYMLINK_NOFOLLOW;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                return (-1);
                        }
                        return lstat(pathname, buf);

                }
                else if (flags == 0)
                {
                        return stat(pathname, buf);
                }

                /* unsupported flags */
                errno=ENOSYS;
                return (-1);
        }

        if (pathname == NULL)
        {
                /* abort if there are any flags */
                if (flags)
                {
                        errno=ENOSYS;
                        return (-1);
                }
                return fstat(dirfd, buf);
        }

#ifdef ASTOPENATEMU_USE_DEVFD
        if (global_cwd.has_devfd)
        {
                char pathbuf[PATH_MAX+256];

                sprintf(pathbuf, "%s/%d/%s", global_cwd.devfd_fs, dirfd, 
pathname);
                if (flags & AST_AT_SYMLINK_NOFOLLOW)
                {
                        flags&=~AST_AT_SYMLINK_NOFOLLOW;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                return (-1);
                        }
                        return lstat(pathbuf, buf);

                }
                else if (flags == 0)
                {
                        return stat(pathbuf, buf);
                }
                else
                {
                        /* unsupported flags */
                        errno=ENOSYS;
                        return (-1);
                }
        }
        else
#endif
        {
                int saved_errno;
                int retval;

                ASTOPENEMU_GLOBALDIR_LOCK

                if (!save_cwd_unlocked())
                {
                        saved_errno=errno;
                        ASTOPENEMU_GLOBALDIR_UNLOCK
                        errno=saved_errno;
                        return (-1);
                }

                if (dirfd != global_cwd.fd)
                {
                        if (fchdir(dirfd) < 0)
                        {
                                saved_errno=errno;
                                ASTOPENEMU_GLOBALDIR_UNLOCK
                                errno=saved_errno;
                                return (-1);
                        }
                }

                if (flags & AST_AT_SYMLINK_NOFOLLOW)
                {
                        flags&=~AST_AT_SYMLINK_NOFOLLOW;
                        /* abort if there are more flags we do not support */
                        if (flags)
                        {
                                errno=ENOSYS;
                                retval=-1;
                        }
                        else
                        {
                                retval=lstat(pathname, buf);
                                saved_errno=errno;
                        }
                }
                else if (flags == 0)
                {
                        retval=stat(pathname, buf);
                        saved_errno=errno;
                }
                else
                {
                        /* unsupported flags */
                        errno=ENOSYS;
                        retval=-1;
                }

                restore_cwd_unlocked();

                ASTOPENEMU_GLOBALDIR_UNLOCK

                errno=saved_errno;

                return (retval);
        }
}

int main(int ac, char *av[])
{
        int dirfd1, dirfd2, fd1, fd2;
        int res;
        struct stat statbuf;

        dirfd1=ast_openat(AST_AT_FDCWD, "/etc", O_RDONLY|AST_O_DIRECTORY, 0);
        dirfd2=ast_openat(AST_AT_FDCWD, "/etc", O_RDONLY|AST_O_DIRECTORY, 0);
        fd1=ast_openat(dirfd1, "profile",   O_RDONLY, 0);
        fd2=ast_openat(dirfd2, "ksh.kshrc", O_RDONLY, 0);

        memset(&statbuf, 0, sizeof(statbuf));
        res=ast_fstatat(dirfd1, ".", &statbuf, 0);
        printf("ast_fstatat(dirfd1, \".\", &statbuf, 0)=%d, st_size=%ld\n", 
res, (long)statbuf.st_size);
        memset(&statbuf, 0, sizeof(statbuf));
        res=(fstatat)(dirfd1, ".", &statbuf, 0);
        printf("    fstatat(dirfd1, \".\", &statbuf, 0)=%d, st_size=%ld\n", 
res, (long)statbuf.st_size);

        /* On SuSE Linux 12.1 /etc/ksh.kshrc is a softlink to /etc/bash.bashrc 
*/
        memset(&statbuf, 0, sizeof(statbuf));
        res=ast_fstatat(dirfd1, "ksh.kshrc", &statbuf, 0);
        printf("ast_fstatat(dirfd1, \"ksh.kshrc\", &statbuf, 0)=%d, 
st_size=%ld\n", res, (long)statbuf.st_size);
        memset(&statbuf, 0, sizeof(statbuf));
        res=(fstatat)(dirfd1, "ksh.kshrc", &statbuf, 0);
        printf("    fstatat(dirfd1, \"ksh.kshrc\", &statbuf, 0)=%d, 
st_size=%ld\n", res, (long)statbuf.st_size);
        /* ... again, now with |AT_SYMLINK_NOFOLLOW| ... */
        memset(&statbuf, 0, sizeof(statbuf));
        res=ast_fstatat(dirfd1, "ksh.kshrc", &statbuf, AST_AT_SYMLINK_NOFOLLOW);
        printf("ast_fstatat(dirfd1, \"ksh.kshrc\", &statbuf, 
AST_AT_SYMLINK_NOFOLLOW)=%d, st_size=%ld\n", res, (long)statbuf.st_size);
        memset(&statbuf, 0, sizeof(statbuf));
        res=(fstatat)(dirfd1, "ksh.kshrc", &statbuf, AT_SYMLINK_NOFOLLOW);
        printf("    fstatat(dirfd1, \"ksh.kshrc\", &statbuf,     
AT_SYMLINK_NOFOLLOW)=%d, st_size=%ld\n", res, (long)statbuf.st_size);

        printf("# dirfd1=%d, dirfd2=%d, fd1=%d, fd2=%d\n", dirfd1, dirfd2, fd1, 
fd2);

        return EXIT_SUCCESS;
}

_______________________________________________
ast-developers mailing list
ast-developers@research.att.com
https://mailman.research.att.com/mailman/listinfo/ast-developers

Reply via email to