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