/*
* Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: installwatch.c,v 0.7.0.8 2006/11/01 06:51:39 izto Exp $
*
* april-15-2001 - Modifications by Felipe Eduardo Sanchez Diaz Duran
*
* Added backup() and make_path() functions.
*
* november-25-2002 - Modifications by Olivier Fleurigeon
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#undef basename
#include
#include
#include
#include
#include
#include "localdecls.h"
#define DEBUG 1
#define LOGLEVEL (LOG_USER | LOG_INFO | LOG_PID)
#define BUFSIZE 1024
#define error(X) (X < 0 ? strerror(errno) : "success")
int __installwatch_refcount = 0;
int __installwatch_timecount = 0;
#define REFCOUNT __installwatch_refcount++
#define TIMECOUNT __installwatch_timecount++
static time_t (*true_time) (time_t *);
static int (*true_chdir)(const char *);
static int (*true_chmod)(const char *, mode_t);
static int (*true_chown)(const char *, uid_t, gid_t);
static int (*true_chroot)(const char *);
static int (*true_creat)(const char *, mode_t);
static int (*true_fchmod)(int, mode_t);
static int (*true_fchown)(int, uid_t, gid_t);
static FILE *(*true_fopen)(const char *,const char*);
static int (*true_ftruncate)(int, TRUNCATE_T);
static char *(*true_getcwd)(char*,size_t);
static int (*true_lchown)(const char *, uid_t, gid_t);
static int (*true_link)(const char *, const char *);
static int (*true_mkdir)(const char *, mode_t);
static int (*true_xmknod)(int ver,const char *, mode_t, dev_t *);
static int (*true_open)(const char *, int, ...);
static DIR *(*true_opendir)(const char *);
static struct dirent *(*true_readdir)(DIR *dir);
static int (*true_readlink)(const char*,char *,size_t);
static char *(*true_realpath)(const char *,char *);
static int (*true_rename)(const char *, const char *);
static int (*true_rmdir)(const char *);
static int (*true_xstat)(int,const char *,struct stat *);
static int (*true_lxstat)(int,const char *,struct stat *);
static int (*true_scandir)( const char *,struct dirent ***,
int (*)(const struct dirent *),
int (*)(const void *,const void *));
static int (*true_symlink)(const char *, const char *);
static int (*true_truncate)(const char *, TRUNCATE_T);
static int (*true_unlink)(const char *);
static int (*true_utime)(const char *,const struct utimbuf *);
static int (*true_utimes)(const char *,const struct timeval *);
static int (*true_access)(const char *, int);
#if(GLIBC_MINOR >= 1)
static int (*true_creat64)(const char *, __mode_t);
static FILE *(*true_fopen64)(const char *,const char *);
static int (*true_ftruncate64)(int, __off64_t);
static int (*true_open64)(const char *, int, ...);
static struct dirent64 *(*true_readdir64)(DIR *dir);
static int (*true_scandir64)( const char *,struct dirent64 ***,
int (*)(const struct dirent64 *),
int (*)(const void *,const void *));
static int (*true_xstat64)(int,const char *, struct stat64 *);
static int (*true_lxstat64)(int,const char *, struct stat64 *);
static int (*true_truncate64)(const char *, __off64_t);
#endif
#if defined __GNUC__ && __GNUC__>=2
#define inline inline
#else
#define inline
#endif
static inline int true_stat(const char *pathname,struct stat *info) {
return true_xstat(_STAT_VER,pathname,info);
}
static inline int true_mknod(const char *pathname,mode_t mode,dev_t dev) {
return true_xmknod(_MKNOD_VER,pathname,mode,&dev);
}
static inline int true_lstat(const char *pathname,struct stat *info) {
return true_lxstat(_STAT_VER,pathname,info);
}
/* A few defines to fix things a little */
#define INSTW_OK 0
/* If not set, no work with translation is allowed */
#define INSTW_INITIALIZED (1<<0)
/* If not set, a wrapped function only do its "real" job */
#define INSTW_OKWRAP (1<<1)
#define INSTW_OKBACKUP (1<<2)
#define INSTW_OKTRANSL (1<<3)
#define INSTW_TRANSLATED (1<<0)
/* Indicates that a translated file is identical to original */
#define INSTW_IDENTITY (1<<1)
/* The file currently exists in the root filesystem */
#define INSTW_ISINROOT (1<<6)
/* The file currently exists in the translated filesystem */
#define INSTW_ISINTRANSL (1<<7)
#define _BACKUP "/BACKUP"
#define _TRANSL "/TRANSL"
/* The root that contains all the needed metas-infos */
#define _META "/META"
/* We store under this subtree the translated status */
#define _MTRANSL _TRANSL
/* We construct under this subtree fake directory listings */
#define _MDIRLS "/DIRLS"
/* String cell used to chain excluded paths */
typedef struct string_t string_t;
struct string_t {
char *string;
string_t *next;
};
/* Used to keep all infos needed to cope with backup, translated fs... */
typedef struct instw_t {
/*
* global fields
*/
int gstatus;
int dbglvl;
pid_t pid;
char *root;
char *backup;
char *transl;
char *meta;
char *mtransl;
char *mdirls;
/* the list of all the paths excluded from translation */
string_t *exclude;
/*
* per instance fields
*/
int error;
int status;
/* the public path, hiding translation */
char path[PATH_MAX+1];
/* the public resolved path, hiding translation */
char reslvpath[PATH_MAX+1];
/* the real resolved path, exposing tranlsation */
char truepath[PATH_MAX+1];
/* the real translated path */
char translpath[PATH_MAX+1];
/* the list of all the equiv paths conducing to "reslvpath" */
string_t *equivpaths;
/* the real path used to flag translation status */
char mtranslpath[PATH_MAX+1];
/* the path given to a wrapped opendir */
char mdirlspath[PATH_MAX+1];
} instw_t;
static instw_t __instw;
static int canonicalize(const char *,char *);
static int make_path(const char *);
static int copy_path(const char *,const char *);
static inline int path_excluded(const char *);
static int unlink_recursive(const char *);
int expand_path(string_t **,const char *,const char *);
int parse_suffix(char *,char *,const char *);
/* a lazy way to avoid sizeof */
#define mallok(T,N) (T *)malloc((N)*sizeof(T))
/* single method used to minimize excessive returns */
#define finalize(code) {rcod=code;goto finalize;}
#if DEBUG
static int __instw_printdirent(struct dirent*);
#if(GLIBC_MINOR >= 1)
static int __instw_printdirent64(struct dirent64*);
#endif
#endif
#ifdef DEBUG
static int instw_print(instw_t *);
#endif
static int instw_init(void);
static int instw_fini(void);
static int instw_new(instw_t *);
static int instw_delete(instw_t *);
/* references a translated file in /mtransl */
static int instw_setmetatransl(instw_t *);
static int instw_setpath(instw_t *,const char *);
static int instw_getstatus(instw_t *,int *);
static int instw_apply(instw_t *);
static int instw_filldirls(instw_t *);
static int instw_makedirls(instw_t *);
static int backup(const char *);
static int vlambda_log(const char *logname,const char *format,va_list ap);
/*
static int lambda_log(const char *logname,const char *format,...)
#ifdef __GNUC__
__attribute__((format(printf,2,3)))
#endif
;
*/
static inline int log(const char *format,...)
#ifdef __GNUC__
/* Tell gcc that this function behaves like printf()
* for parameters 1 and 2 */
__attribute__((format(printf, 1, 2)))
#endif /* defined __GNUC__ */
;
static inline int debug(int dbglvl,const char *format,...)
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
;
#define unset_okwrap() (__instw.gstatus &= ~INSTW_OKWRAP)
#define reset_okwrap() (__instw.gstatus |= INSTW_OKWRAP)
/*
* *****************************************************************************
*/
static void *libc_handle=NULL;
static void initialize(void) {
if (libc_handle)
return;
#ifdef BROKEN_RTLD_NEXT
// printf ("RTLD_LAZY");
libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY);
#else
// printf ("RTLD_NEXT");
libc_handle = RTLD_NEXT;
#endif
true_time = dlsym(libc_handle, "time");
true_chdir = dlsym(libc_handle, "chdir");
true_chmod = dlsym(libc_handle, "chmod");
true_chown = dlsym(libc_handle, "chown");
true_chroot = dlsym(libc_handle, "chroot");
true_creat = dlsym(libc_handle, "creat");
true_fchmod = dlsym(libc_handle, "fchmod");
true_fchown = dlsym(libc_handle, "fchown");
true_fopen = dlsym(libc_handle, "fopen");
true_ftruncate = dlsym(libc_handle, "ftruncate");
true_getcwd = dlsym(libc_handle, "getcwd");
true_lchown = dlsym(libc_handle, "lchown");
true_link = dlsym(libc_handle, "link");
true_mkdir = dlsym(libc_handle, "mkdir");
true_xmknod = dlsym(libc_handle, "__xmknod");
true_open = dlsym(libc_handle, "open");
true_opendir = dlsym(libc_handle, "opendir");
true_readdir = dlsym(libc_handle, "readdir");
true_readlink = dlsym(libc_handle, "readlink");
true_realpath = dlsym(libc_handle, "realpath");
true_rename = dlsym(libc_handle, "rename");
true_rmdir = dlsym(libc_handle, "rmdir");
true_scandir = dlsym(libc_handle, "scandir");
true_xstat = dlsym(libc_handle, "__xstat");
true_lxstat = dlsym(libc_handle, "__lxstat");
true_symlink = dlsym(libc_handle, "symlink");
true_truncate = dlsym(libc_handle, "truncate");
true_unlink = dlsym(libc_handle, "unlink");
true_utime = dlsym(libc_handle, "utime");
true_utimes = dlsym(libc_handle, "utimes");
true_access = dlsym(libc_handle, "access");
#if(GLIBC_MINOR >= 1)
true_creat64 = dlsym(libc_handle, "creat64");
true_fopen64 = dlsym(libc_handle, "fopen64");
true_ftruncate64 = dlsym(libc_handle, "ftruncate64");
true_open64 = dlsym(libc_handle, "open64");
true_readdir64 = dlsym(libc_handle, "readdir64");
true_scandir64 = dlsym(libc_handle, "scandir64");
true_xstat64 = dlsym(libc_handle, "__xstat64");
true_lxstat64 = dlsym(libc_handle, "__lxstat64");
true_truncate64 = dlsym(libc_handle, "truncate64");
#endif
if(instw_init()) exit(-1);
}
void _init(void) {
initialize();
}
void _fini(void) {
instw_fini();
}
/*
* *****************************************************************************
*/
/*
* procedure = / rc:=vlambda_log(logname,format,ap) /
*
* task = / the va_list version of the lambda_log() procedure. /
*/
static int vlambda_log(const char *logname,const char *format,va_list ap) {
char buffer[BUFSIZE];
int count;
int logfd;
int rcod=0;
count=vsnprintf(buffer,BUFSIZE,format,ap);
if(count == -1) {
/* The buffer was not big enough */
strcpy(&(buffer[BUFSIZE - 5]), "...\n");
count=BUFSIZE-1;
}
if(logname!=NULL) {
logfd=true_open(logname,O_WRONLY|O_CREAT|O_APPEND,0666);
if(logfd>=0) {
if(write(logfd,buffer,count)!=count)
syslog( LOGLEVEL,
"Count not write `%s' in `%s': %s\n",
buffer,logname,strerror(errno));
if(close(logfd) < 0)
syslog( LOGLEVEL,
"Could not close `%s': %s\n",
logname,strerror(errno));
} else {
syslog( LOGLEVEL,
"Could not open `%s' to write `%s': %s\n",
logname,buffer,strerror(errno));
}
} else {
syslog(LOGLEVEL,buffer);
}
return rcod;
}
/*
* procedure = / rc:=lambda_log(logname,format,...) /
*
* task = / logs a message to the specified file, or via syslog
* if no file is specified. /
*
* returns = / 0 ok. message logged /
*
* note = /
* --This *general* log procedure was justified by the debug mode
* which used either stdout or stderr, thus interfering with the
* observed process.
* --From now, we output nothing to stdout/stderr.
* /
*
*/
/*
static int lambda_log(const char *logname,const char *format, ...) {
va_list ap;
int rcod=0;;
va_start(ap,format);
rcod=vlambda_log(logname,format,ap);
va_end(ap);
return rcod;
}
*/
static inline int log(const char *format,...) {
char *logname;
va_list ap;
int rcod;
logname=getenv("INSTW_LOGFILE");
va_start(ap,format);
rcod=vlambda_log(logname,format,ap);
va_end(ap);
return rcod;
}
static inline int debug(int dbglvl,const char *format,...) {
int rcod=0;
#ifdef DEBUG
char *logname;
va_list ap;
if( __instw.dbglvl==0 ||
dbglvl>__instw.dbglvl ||
dbglvl<0 ) return rcod;
logname=getenv("INSTW_DBGFILE");
va_start(ap,format);
rcod=vlambda_log(logname,format,ap);
va_end(ap);
#endif
return rcod;
}
/*
* procedure = / rc:=canonicalize(path,resolved_path) /
*
* note = /
* --We use realpath here, but this function calls __lxstat().
* We want to only use "real" calls in wrapping code, hence the
* barrier established by unset_okwrap()/reset_okwrap().
* --We try to canonicalize as much as possible, considering that
* /
*/
static int canonicalize(const char *path, char *resolved_path) {
unset_okwrap();
if(!realpath(path,resolved_path)) {
if((path[0] != '/')) {
/* The path could not be canonicalized, append it
* to the current working directory if it was not
* an absolute path */
true_getcwd(resolved_path, PATH_MAX-2);
strcat(resolved_path, "/");
strncat(resolved_path, path, MAXPATHLEN - 1);
} else {
strcpy(resolved_path,path);
}
}
reset_okwrap();
#if DEBUG
debug(4,"canonicalize(%s,%s)\n",path,resolved_path);
#endif
return 0;
}
static int make_path (const char *path) {
char checkdir[BUFSIZ];
struct stat inode;
int i = 0;
#if DEBUG
debug(2,"===== make_path: %s\n", path);
#endif
while ( path[i] != '\0' ) {
checkdir[i] = path[i];
if (checkdir[i] == '/') { /* Each time a '/' is found, check if the */
checkdir[i+1] = '\0'; /* path exists. If it doesn't, we create it. */
if (true_stat (checkdir, &inode) < 0)
true_mkdir (checkdir, S_IRWXU);
}
i++;
}
return 0;
}
/*
* procedure = / rc:=copy_path(truepath,translroot) /
*
* task = / do an exact translation of 'truepath' under 'translroot',
* the directory path to the new objet is not created /
*
* returns = / 0 ok. translation done
* -1 failed. cf errno /
*
* note = /
* --we suppose that 'translroot' has no trailing '/'
* --no overwrite is done is the target object already exists
* --the copy method depends on the source object type.
* --we don't fail if the source object doesn't exist.
* --we don't create the directory path because that would lead in the
* the translation case not to reference the newly created directories
* /
*/
static int copy_path(const char *truepath,const char *translroot) {
int rcod;
char buffer[BUFSIZ];
int bytes;
char translpath[PATH_MAX+1];
struct stat trueinfo;
struct stat translinfo;
int truefd;
int translfd;
struct utimbuf timbuf;
size_t truesz;
char linkpath[PATH_MAX+1];
size_t linksz;
#if DEBUG
debug(2,"copy_path(%s,%s)\n",truepath,translroot);
#endif
rcod=true_lstat(truepath,&trueinfo);
if(rcod<0 && errno!=ENOENT) return -1;
if(!rcod) {
if((truesz=strlen(truepath)+strlen(translpath))>PATH_MAX) {
errno=ENAMETOOLONG;
return -1;
}
strncpy(translpath,translroot,PATH_MAX);
strncat(translpath,truepath,PATH_MAX-truesz);
if(!true_lstat(translpath,&translinfo)) return 0;
/* symbolic links */
if(S_ISLNK(trueinfo.st_mode)) {
if((linksz=true_readlink(truepath,linkpath,PATH_MAX))<0) return -1;
linkpath[linksz]='\0';
if(true_symlink(linkpath,translpath)!=0) return -1;
}
/* regular file */
if(S_ISREG(trueinfo.st_mode)) {
if((truefd=true_open(truepath,O_RDONLY))<0) return -1;
if((translfd=true_open( translpath,
O_WRONLY|O_CREAT|O_TRUNC,
trueinfo.st_mode))<0 ) {
close(truefd);
return -1;
}
while((bytes=read(truefd,buffer,BUFSIZ))>0)
write(translfd,buffer,bytes);
close(truefd);
close(translfd);
}
/* directory */
if(S_ISDIR(trueinfo.st_mode)) {
if(true_mkdir(translpath,trueinfo.st_mode)) return -1;
}
/* block special file */
if(S_ISBLK(trueinfo.st_mode)) {
if(true_mknod( translpath,trueinfo.st_mode|S_IFBLK,
trueinfo.st_rdev )) return -1;
}
/* character special file */
if(S_ISCHR(trueinfo.st_mode)) {
if(true_mknod( translpath,trueinfo.st_mode|S_IFCHR,
trueinfo.st_rdev )) return -1;
}
/* fifo special file */
if(S_ISFIFO(trueinfo.st_mode)) {
if(true_mknod(translpath,trueinfo.st_mode|S_IFIFO,0))
return -1;
}
timbuf.actime=trueinfo.st_atime;
timbuf.modtime=trueinfo.st_mtime;
true_utime(translpath,&timbuf);
if(!S_ISLNK(trueinfo.st_mode)) {
true_chown(translpath,trueinfo.st_uid,trueinfo.st_gid);
true_chmod(translpath,trueinfo.st_mode);
}
}
return 0;
}
/*
* procedure = / rc:=path_excluded(truepath) /
*
* task = / indicates if the given path is or is hosted under any
* of the exclusion list members. /
*
* returns = / 0 is not a member
* 1 is a member /
*
* note = / __instw.exclude must be initialized /
*
*/
static inline int path_excluded(const char *truepath) {
string_t *pnext;
int result;
result=0;
pnext=__instw.exclude;
while(pnext!=NULL) {
if(strstr(truepath,pnext->string)==truepath) {
result=1;
break;
}
pnext=pnext->next;
}
return result;
}
/*
* procedure = / rc:=unlink_recursive(truepath) /
*
* task = / dangerous function that unlink either a file or
* an entire subtree. /
*
* returns = / 0 ok. recursive unlink done
* -1 failed. cf errno /
*
* note = /
* --this procedure was needed by instw_makedirls(), in order to
* erase a previously created temporary subtree.
* --it must be called with an absolute path, and only to delete
* well identified trees.
* --i think it is a very weak implementation, so avoid using it
* to unlink too deep trees, or rewrite it to avoid recursivity.
* /
*
*/
static int unlink_recursive(const char *truepath) {
int rcod;
struct stat trueinfo;
DIR *wdir;
struct dirent *went;
char wpath[PATH_MAX+1];
struct stat winfo;
#if DEBUG
debug(2,"unlink_recursive(%s)\n",truepath);
#endif
rcod=true_lstat(truepath,&trueinfo);
if(rcod<0 && errno!=ENOENT) return -1;
if(rcod!=0) return 0;
if(S_ISDIR(trueinfo.st_mode)) {
wdir=true_opendir(truepath);
if(wdir==NULL) return -1;
while((went=true_readdir(wdir))!=NULL) {
/* we avoid big inifinite recursion troubles */
if( went->d_name[0]=='.' &&
( (went->d_name[1]=='\0') ||
( went->d_name[1]=='.' &&
went->d_name[2]=='\0') ) )
{ continue; }
/* let's get the absolute path to this entry */
strcpy(wpath,truepath);
strcat(wpath,"/");
strcat(wpath,went->d_name);
rcod=true_lstat(wpath,&winfo);
if(rcod!=0) {
closedir(wdir);
return -1;
}
if(S_ISDIR(winfo.st_mode)) {
unlink_recursive(wpath);
true_rmdir(wpath);
} else {
true_unlink(wpath);
}
}
closedir(wdir);
true_rmdir(truepath);
} else {
true_unlink(truepath);
}
return rcod;
}
/*
* procedure = / rc:=expand_path(&list,prefix,suffix) /
*
* task = / from a given path, generates all the paths that could
* be derived from it, through symlinks expansion. /
*
* note = /
* --this procedure has been implemented to enhance the method used
* to reference files that have been translated.
* --briefly, it is necessary to reference all the paths that could
* lead to a file, not only the path and the associated real path.
* /
*/
int expand_path(string_t **list,const char *prefix,const char *suffix) {
char nprefix[PATH_MAX+1];
char nwork[PATH_MAX+1];
char nsuffix[PATH_MAX+1];
char lnkpath[PATH_MAX+1];
size_t lnksz=0;
string_t *pthis=NULL;
string_t *list1=NULL;
string_t *list2=NULL;
struct stat ninfo;
int rcod=0;
char pnp[PATH_MAX+1];
char pns[PATH_MAX+1];
size_t len;
#if DEBUG
debug(4,"expand_path(%p,%s,%s)\n",list,prefix,suffix);
#endif
/* nothing more to expand, stop condition */
if(suffix[0]=='\0') {
(*list)=mallok(string_t,1);
(*list)->string=malloc(strlen(prefix)+1);
strcpy((*list)->string,prefix);
(*list)->next=NULL;
finalize(0);
}
/* we parse the next suffix subscript */
parse_suffix(pnp,pns,suffix);
strcpy(nprefix,prefix);
strcat(nprefix,pnp);
strcpy(nsuffix,pns);
rcod=true_lstat(nprefix,&ninfo);
if( (rcod!=0) ||
(rcod==0 && !S_ISLNK(ninfo.st_mode))) {
expand_path(list,nprefix,nsuffix);
} else {
expand_path(&list1,nprefix,nsuffix);
lnksz=true_readlink(nprefix,lnkpath,PATH_MAX);
lnkpath[lnksz]='\0';
if(lnkpath[0]!='/') {
strcpy(nprefix,prefix);
len=strlen(lnkpath);
if(lnkpath[len-1]=='/') {lnkpath[len-1]='\0';}
strcpy(nwork,"/");
strcat(nwork,lnkpath);
strcat(nwork,nsuffix);
strcpy(nsuffix,nwork);
expand_path(&list2,nprefix,nsuffix);
} else {
len=strlen(lnkpath);
if(lnkpath[len-1]=='/') {lnkpath[len-1]='\0';}
strcpy(nprefix,"");
strcpy(nwork,lnkpath);
strcat(nwork,nsuffix);
strcpy(nsuffix,nwork);
expand_path(&list2,nprefix,nsuffix);
}
*list=list1;
pthis=*list;
while(pthis->next!=NULL) {pthis=pthis->next;}
pthis->next=list2;
}
finalize:
return rcod;
}
int parse_suffix(char *pnp,char *pns,const char *suffix) {
int rcod=0;
char *p;
strcpy(pnp,suffix);
strcpy(pns,"");
p=pnp;
if(*p=='\0') {
strcpy(pns,"");
} else {
p++;
while((*p)!='\0') {
if(*p=='/') {
strcpy(pns,p);
*p='\0';
break;
}
p++;
}
}
return rcod;
}
/*
* *****************************************************************************
*/
static int __instw_printdirent(struct dirent *entry) {
if(entry!=NULL) {
debug( 4,
"entry(%p) {\n"
"\td_ino : %ld\n"
"\td_off : %ld\n"
"\td_reclen : %d\n"
"\td_type : %d\n"
"\td_name : \"%.*s\"\n",
entry,
entry->d_ino,
entry->d_off,
entry->d_reclen,
(int)entry->d_type,
(int)entry->d_reclen,(char*)(entry->d_name)
);
} else {
debug( 4,"entry(null) \n");
}
return 0;
}
static int __instw_printdirent64(struct dirent64 *entry) {
if(entry!=NULL) {
debug( 4,
"entry(%p) {\n"
"\td_ino : %lld\n"
"\td_off : %lld\n"
"\td_reclen : %d\n"
"\td_type : %d\n"
"\td_name : \"%.*s\"\n",
entry,
entry->d_ino,
entry->d_off,
entry->d_reclen,
(int)entry->d_type,
(int)entry->d_reclen,(char*)(entry->d_name)
);
} else {
debug( 4,"entry(null) \n");
}
return 0;
}
/*
* *****************************************************************************
*/
#ifdef DEBUG
static int instw_print(instw_t *instw) {
string_t *pnext;
int i;
debug( 4,
"instw(%p) {\n"
"\tgstatus : %d\n"
"\terror : %d\n"
"\tstatus : %d\n"
"\tdbglvl : %d\n"
"\tpid : %d\n"
"\troot : \"%.*s\"\n"
"\tbackup : \"%.*s\"\n"
"\ttransl : \"%.*s\"\n"
"\tmeta : \"%.*s\"\n"
"\tmtransl : \"%.*s\"\n"
"\tmdirls : \"%.*s\"\n",
instw,
instw->gstatus,
instw->error,
instw->status,
instw->dbglvl,
instw->pid,
PATH_MAX,(char*)((instw->root)?:"(null)"),
PATH_MAX,(char*)((instw->backup)?:"(null)"),
PATH_MAX,(char*)((instw->transl)?:"(null)"),
PATH_MAX,(char*)((instw->meta)?:"(null)"),
PATH_MAX,(char*)((instw->mtransl)?:"(null)"),
PATH_MAX,(char*)((instw->mdirls)?:"(null)")
);
pnext=instw->exclude;
i=0;
while(pnext!=NULL) {
debug( 4,
"\texclude : (%02d) \"%.*s\"\n",
++i,PATH_MAX,pnext->string );
pnext=pnext->next;
}
debug( 4,
"\tpath : \"%.*s\"\n"
"\treslvpath : \"%.*s\"\n"
"\ttruepath : \"%.*s\"\n"
"\ttranslpath : \"%.*s\"\n",
PATH_MAX,(char*)(instw->path),
PATH_MAX,(char*)(instw->reslvpath),
PATH_MAX,(char*)(instw->truepath),
PATH_MAX,(char*)(instw->translpath)
);
pnext=instw->equivpaths;
i=0;
while(pnext!=NULL) {
debug( 4,
"\tequivpaths : (%02d) \"%.*s\"\n",
++i,PATH_MAX,pnext->string );
pnext=pnext->next;
}
debug( 4,
"\tmtranslpath : \"%.*s\"\n"
"\tmdirlspath : \"%.*s\"\n"
"}\n",
PATH_MAX,(char*)(instw->mtranslpath),
PATH_MAX,(char*)(instw->mdirlspath)
);
return 0;
}
#endif
/*
* procedure = / rc:=instw_init() /
*
* task = / initializes the '__transl' fields, and fills the fields
* provided by the environment.
* this structure is a reference enabling faster
* local structure creations. /
*
* returns = / 0 ok. env set
* -1 failed. /
*/
static int instw_init(void) {
char *proot;
char *pbackup;
char *ptransl;
char *pdbglvl;
struct stat info;
char wrkpath[PATH_MAX+1];
char *pexclude;
char *exclude;
string_t **ppnext;
int okinit;
int okbackup;
int oktransl;
int okwrap;
#if DEBUG
/*
* We set the requested dynamic debug level
*/
__instw.dbglvl=0;
if((pdbglvl=getenv("INSTW_DBGLVL"))) {
__instw.dbglvl=atoi(pdbglvl);
if(__instw.dbglvl>4) { __instw.dbglvl=4; }
if(__instw.dbglvl<0) { __instw.dbglvl=0; }
}
debug(2,"instw_init()\n");
#endif
okinit=0;
okbackup=0;
oktransl=0;
okwrap=0;
__instw.gstatus=0;
__instw.error=0;
__instw.status=0;
__instw.pid=getpid();
__instw.root=NULL;
__instw.backup=NULL;
__instw.transl=NULL;
__instw.meta=NULL;
__instw.mtransl=NULL;
__instw.mdirls=NULL;
__instw.exclude=NULL;
__instw.path[0]='\0';
__instw.reslvpath[0]='\0';
__instw.truepath[0]='\0';
__instw.translpath[0]='\0';
__instw.equivpaths=NULL;
__instw.mtranslpath[0]='\0';
__instw.mdirlspath[0]='\0';
/* nothing can be activated without that, anyway */
if((proot=getenv("INSTW_ROOTPATH"))) {
realpath(proot,wrkpath);
if(wrkpath[strlen(wrkpath)-1]=='/')
wrkpath[strlen(wrkpath)-1]='\0';
__instw.root=malloc(strlen(wrkpath)+1);
if(NULL==__instw.root) return -1;
strcpy(__instw.root,wrkpath);
/* this root path must exist */
if(__instw.root[0]=='\0' || true_stat(__instw.root,&info)) {
fprintf(stderr,
"Please check the INSTW_ROOTPATH and "
"be sure that it does exist please !\n"
"given value : %s\n", __instw.root);
return -1;
}
if((pbackup=getenv("INSTW_BACKUP"))) {
if( !strcmp(pbackup,"1") ||
!strcmp(pbackup,"yes") ||
!strcmp(pbackup,"true") ) {
if((strlen(__instw.root)+strlen(_BACKUP))>PATH_MAX) {
fprintf(stderr,
"Backup path would exceed PATH_MAX. abending.\n");
return -1;
}
__instw.backup=malloc(strlen(__instw.root)+strlen(_BACKUP)+1);
if(NULL==__instw.backup) return -1;
strcpy(__instw.backup,__instw.root);
strcat(__instw.backup,_BACKUP);
/* we create the path that precautiously shouldn't exist */
true_mkdir(__instw.backup,S_IRWXU);
okbackup=1;
}
else if( strcmp(pbackup,"0") &&
strcmp(pbackup,"no") &&
strcmp(pbackup,"false") ) {
fprintf(stderr,
"Please check the INSTW_BACKUP value please !\n"
"Recognized values are : 1/0,yes/no,true/false.\n");
return -1;
}
}
if((ptransl=getenv("INSTW_TRANSL"))) {
if( !strcmp(ptransl,"1") ||
!strcmp(ptransl,"yes") ||
!strcmp(ptransl,"true") ) {
if((strlen(__instw.root)+strlen(_TRANSL))>PATH_MAX) {
fprintf(stderr,
"Transl path would exceed PATH_MAX. abending.\n");
return -1;
}
__instw.transl=malloc(strlen(__instw.root)+strlen(_TRANSL)+1);
if(NULL==__instw.transl) return -1;
strcpy(__instw.transl,__instw.root);
strcat(__instw.transl,_TRANSL);
/* we create the path that precautiously shouldn't exist */
true_mkdir(__instw.transl,S_IRWXU);
if((strlen(__instw.root)+strlen(_META))>PATH_MAX) {
fprintf(stderr,
"Meta path would exceed PATH_MAX. abending.\n");
return -1;
}
__instw.meta=malloc(strlen(__instw.root)+strlen(_META)+1);
if(NULL==__instw.meta) return -1;
strcpy(__instw.meta,__instw.root);
strcat(__instw.meta,_META);
/* we create the path that precautiously shouldn't exist */
true_mkdir(__instw.meta,S_IRWXU);
__instw.mtransl=malloc(strlen(__instw.meta)+strlen(_MTRANSL)+1);
if(NULL==__instw.mtransl) return -1;
strcpy(__instw.mtransl,__instw.meta);
strcat(__instw.mtransl,_MTRANSL);
/* we create the path that precautiously shouldn't exist */
true_mkdir(__instw.mtransl,S_IRWXU);
__instw.mdirls=malloc(strlen(__instw.meta)+strlen(_MDIRLS)+1);
if(NULL==__instw.mdirls) return -1;
strcpy(__instw.mdirls,__instw.meta);
strcat(__instw.mdirls,_MDIRLS);
/* we create the path that precautiously shouldn't exist */
true_mkdir(__instw.mdirls,S_IRWXU);
oktransl=1;
}
else if( strcmp(ptransl,"0") &&
strcmp(ptransl,"no") &&
strcmp(ptransl,"false") ) {
fprintf(stderr,
"Please check the INSTW_TRANSL value please !\n"
"Recognized values are : 1/0,yes/no,true/false.\n");
return -1;
}
}
}
/*
* we end up constructing the exclusion list
*/
ppnext=&__instw.exclude;
/* we systematically add the root directory */
if(__instw.gstatus&INSTW_OKTRANSL) {
*ppnext=mallok(string_t,1);
if(*ppnext==NULL) return -1;
(*ppnext)->string=NULL;
(*ppnext)->next=NULL;
realpath(__instw.root,wrkpath);
(*ppnext)->string=malloc(strlen(wrkpath)+1);
strcpy((*ppnext)->string,wrkpath);
ppnext=&(*ppnext)->next;
}
if((pexclude=getenv("INSTW_EXCLUDE"))) {
exclude=malloc(strlen(pexclude)+1);
strcpy(exclude,pexclude);
pexclude=strtok(exclude,",");
while(pexclude!=NULL) {
*ppnext=malloc(sizeof(string_t));
if(*ppnext==NULL) return -1;
(*ppnext)->string=NULL;
(*ppnext)->next=NULL;
/* let's store the next excluded path */
if(strlen(pexclude)>PATH_MAX) return -1;
realpath(pexclude,wrkpath);
(*ppnext)->string=malloc(strlen(wrkpath)+1);
strcpy((*ppnext)->string,wrkpath);
ppnext=&(*ppnext)->next;
pexclude=strtok(NULL,",");
}
}
okinit=1;
okwrap=1;
if(okinit) __instw.gstatus |= INSTW_INITIALIZED;
if(okwrap) __instw.gstatus |= INSTW_OKWRAP;
if(okbackup) __instw.gstatus |= INSTW_OKBACKUP;
if(oktransl) __instw.gstatus |= INSTW_OKTRANSL;
#if DEBUG
debug(4,"__instw(%p)\n",&__instw);
instw_print(&__instw);
#endif
return 0;
}
/*
* procedure = / rc:=instw_fini() /
*
* task = / properly finalizes the instw job /
*
* returns = / 0 ok. env set
* -1 failed. /
*/
static int instw_fini(void) {
int rcod=0;
string_t *pnext;
string_t *pthis;
#if DEBUG
debug(2,"instw_fini()\n");
#endif
if( !(__instw.gstatus & INSTW_INITIALIZED) ) finalize(0);
__instw.gstatus &= ~INSTW_INITIALIZED;
if(__instw.root != NULL) {free(__instw.root);__instw.root=NULL;}
if(__instw.backup != NULL) {free(__instw.backup);__instw.backup=NULL;}
if(__instw.transl != NULL) {free(__instw.transl);__instw.transl=NULL;}
if(__instw.meta != NULL) {free(__instw.meta);__instw.meta=NULL;}
if(__instw.mtransl != NULL) {free(__instw.mtransl);__instw.mtransl=NULL;}
if(__instw.mdirls != NULL) {free(__instw.mdirls);__instw.mdirls=NULL;}
pthis=__instw.exclude;
while(pthis != NULL) {
free(pthis->string);
pnext=pthis->next;
free(pthis);
pthis=pnext;
}
__instw.exclude=NULL;
finalize:
return rcod;
}
/*
* procedure = / rc:=instw_new(instw) /
*
* task = / Initializes a new instw_t structure before any work on it /
*
* returns = / 0 ok. ready to be used
* -1 failed. /
*/
static int instw_new(instw_t *instw) {
int rcod=0;
*instw=__instw;
instw->error=0;
instw->status=0;
instw->path[0]='\0';
instw->reslvpath[0]='\0';
instw->truepath[0]='\0';
instw->translpath[0]='\0';
instw->equivpaths=NULL;
instw->mtranslpath[0]='\0';
instw->mdirlspath[0]='\0';
return rcod;
}
/*
* procedure = / rc:=instw_delete(instw) /
*
* task = / properly finalizes an instw structure /
*
* returns = / 0 ok. ready to be used
* -1 failed. /
*/
static int instw_delete(instw_t *instw) {
int rcod=0;
string_t *pnext;
string_t *pthis;
pthis=instw->equivpaths;
while(pthis != NULL) {
free(pthis->string);
pnext=pthis->next;
free(pthis);
pthis=pnext;
}
instw->status=0;
return rcod;
}
/*
* procedure = / rc:=instw_setmetatransl(instw) /
*
* task = / Refreshes as mush as possible the translation
* status of a translated file /
*
* note = /
* --this procedure is meant to be called after the various
* translation status flags have been setted.
* the only thing it does is referencing a file that has been
* flagged as "translated".
* if it is, we musn't try to use the eventual real version
* of the file anymore, hence the full referencement under /mtransl.
*
* --in some cases, for example when you create manually a subtree
* and a file in this subtree directly directly in the translated fs
* (yes, it is possible) the meta infos won't exist.
* so, to be able to cope with this case, we firstly try to
* create the full reference to the file, and if this fails, we try
* to reference all the traversed directories.
* /
*/
static int instw_setmetatransl(instw_t *instw) {
int rcod=0;
struct stat info;
char mtransldir[PATH_MAX+1];
char mtranslpath[PATH_MAX+1];
char reslvpath[PATH_MAX+1];
size_t mesz=0;
int i=0;
string_t *pthis;
#if DEBUG
debug(3,"instw_setmetatransl(%p)\n",instw);
instw_print(instw);
#endif
if( !(instw->gstatus & INSTW_INITIALIZED) ||
!(instw->gstatus & INSTW_OKTRANSL) ) finalize(0);
if(!(instw->status & INSTW_TRANSLATED) ) finalize(0);
if(instw->equivpaths==NULL) {
expand_path(&(instw->equivpaths),"",instw->reslvpath);
}
#if DEBUG
instw_print(instw);
#endif
pthis=instw->equivpaths;
while(pthis!=NULL) {
strcpy(mtranslpath,instw->mtransl);
strcat(mtranslpath,pthis->string);
strcpy(reslvpath,pthis->string);
if( (true_stat(mtranslpath,&info)) &&
(true_mkdir(mtranslpath,S_IRWXU)) ) {
strcpy(mtransldir,mtranslpath);
mesz=strlen(instw->mtransl);
for(i=0;reslvpath[i]!='\0';i++) {
mtransldir[mesz+i]=reslvpath[i];
if(reslvpath[i]=='/') {
mtransldir[mesz+i+1]='\0';
true_mkdir(mtransldir,S_IRWXU);
}
}
true_mkdir(mtranslpath,S_IRWXU);
}
pthis=pthis->next;
}
finalize:
return rcod;
}
/*
* procedure = / rc:=instw_setpath(instw,path) /
*
* task = / sets the 'instw->path' field and updates all the fields that
* can be deduced from 'path', such as 'instw->translpath'. /
*
* inputs = / path The given path, as is
* outputs = / instw->path A stored copy of 'path'
* instw->truepath The given path, canonicalized
* instw->translpath The real translated path
* instw->mtranslpath The translation status path /
*
* returns = / 0 ok. path set
* -1 failed. cf errno /
*/
static int instw_setpath(instw_t *instw,const char *path) {
size_t relen;
size_t trlen;
size_t melen;
#if DEBUG
debug(2,"instw_setpath(%p,%s)\n",instw,path);
#endif
instw->status=0;
strncpy(instw->path,path,PATH_MAX);
instw->truepath[0]='\0';
if(instw->path[0]!='/') {
true_getcwd(instw->truepath,PATH_MAX+1);
if(instw->truepath[strlen(instw->truepath)-1]!='/'){
strcat(instw->truepath,"/");
}
strcat(instw->truepath,instw->path);
} else {
strcpy(instw->truepath,instw->path);
}
relen=strlen(instw->truepath);
/*
* if library is not completely initialized, or if translation
* is not active, we make things so it is equivalent to the
* to the identity, this avoid needs to cope with special cases.
*/
if( !(instw->gstatus&INSTW_INITIALIZED) ||
!(instw->gstatus&INSTW_OKTRANSL)) {
strncpy(instw->reslvpath,instw->truepath,PATH_MAX);
strncpy(instw->translpath,instw->truepath,PATH_MAX);
return 0;
}
/*
* we fill instw->reslvpath , applying the inversed translation
* if truepath is inside /transl.
*/
if(strstr(instw->truepath,instw->transl)==instw->truepath) {
strcpy(instw->reslvpath,instw->truepath+strlen(instw->transl));
} else {
strcpy(instw->reslvpath,instw->truepath);
}
/*
* if instw->path is relative, no troubles.
* but if it is absolute and located under /transl, we have
* to untranslate it.
*/
if( (instw->path[0]=='/') &&
(strstr(instw->path,instw->transl)==instw->path)) {
strcpy(instw->path,instw->reslvpath);
}
/*
* We must detect early 'path' matching with already translated files
*/
if(path_excluded(instw->truepath)) {
strncpy(instw->translpath,instw->truepath,PATH_MAX);
instw->status |= ( INSTW_TRANSLATED | INSTW_IDENTITY);
} else {
/* Building the real translated path */
strncpy(instw->translpath,instw->transl,PATH_MAX);
trlen=strlen(instw->translpath);
if((trlen+relen)>PATH_MAX) {
instw->error=errno=ENAMETOOLONG;
return -1;
}
strncat(instw->translpath,instw->reslvpath,PATH_MAX-trlen);
instw->translpath[PATH_MAX]='\0';
}
/* Building the translation status path */
strncpy(instw->mtranslpath,instw->mtransl,PATH_MAX);
instw->mtranslpath[PATH_MAX]='\0';
melen=strlen(instw->mtranslpath);
if((melen+relen)>PATH_MAX) {
instw->error=errno=ENAMETOOLONG;
return -1;
}
strncat(instw->mtranslpath,instw->reslvpath,PATH_MAX-trlen);
instw->mtranslpath[PATH_MAX]='\0';
return 0;
}
/*
* procedure = / rc:=instw_getstatus(instw,status) /
*
* outputs = / status instw->path flags field status in the translated fs
* INSTW_ISINROOT file exists in the real fs
* INSTW_ISINTRANSL file exists in the translated fs
* INSTW_TRANSLATED file has been translated /
*
* returns = / 0 ok. stated
* -1 failed. cf errno /
*/
static int instw_getstatus(instw_t *instw,int *status) {
struct stat inode;
struct stat rinode;
struct stat tinode;
#if DEBUG
debug(2,"instw_getstatus(%p,%p)\n",instw,status);
#endif
/*
* is the file referenced as being translated ?
*/
if( (instw->gstatus&INSTW_INITIALIZED) &&
(instw->gstatus&INSTW_OKTRANSL) &&
!(instw->status&INSTW_TRANSLATED) &&
!true_stat(instw->mtranslpath,&inode) ) {
instw->status |= INSTW_TRANSLATED;
}
/*
* do the file currently exist in the translated fs ?
*/
if( (instw->gstatus&INSTW_INITIALIZED) &&
(instw->gstatus&INSTW_OKTRANSL) &&
!true_stat(instw->translpath,&tinode) ) {
instw->status |= INSTW_ISINTRANSL;
}
/*
* is it a newly created file, or a modified one ?
*/
if( instw->gstatus&INSTW_INITIALIZED &&
!true_stat(instw->reslvpath,&rinode) ) {
instw->status |= INSTW_ISINROOT;
}
/*
* if the file exists, why is it not referenced as
* being translated ?
* we have to reference it and all the traversed
* directories leading to it.
*/
if( (instw->gstatus&INSTW_INITIALIZED) &&
(instw->gstatus&INSTW_OKTRANSL) &&
(instw->status&INSTW_ISINTRANSL) &&
!(instw->status&INSTW_TRANSLATED) ) {
instw->status |= INSTW_TRANSLATED;
instw_setmetatransl(instw);
}
/*
* are the public resolved path and its translated counterpart
* identical ? if so, we flag it
*/
if( (instw->gstatus & INSTW_INITIALIZED) &&
(instw->gstatus & INSTW_OKTRANSL) &&
(instw->status & INSTW_TRANSLATED) &&
(0==(strcmp(instw->truepath,instw->translpath))) ) {
instw->status |= INSTW_IDENTITY;
}
*status=instw->status;
return 0;
}
/*
* procedure = / rc:=instw_apply(instw) /
*
* task = / actually do the translation prepared in 'transl' /
*
* note = / --after a call to instw_apply(), the translation related
* status flags are updated.
* --if a translation is requested and if the original file
* exists, all parent directories are created and referenced
* if necessary.
* if the original file does not exist, we translate at
* least the existing path. /
*
* returns = / 0 ok. translation done
* -1 failed. cf errno /
*/
static int instw_apply(instw_t *instw) {
int rcod=0;
int status=0;
char dirpart[PATH_MAX+1];
char basepart[PATH_MAX+1];
char *pdir;
char *pbase;
struct stat reslvinfo;
instw_t iw;
char wpath[PATH_MAX+1];
size_t wsz=0;
char linkpath[PATH_MAX+1];
#if DEBUG
debug(2,"instw_apply(%p)\n",instw);
instw_print(instw);
#endif
/*
* if library incompletely initialized or if translation
* is inactive, nothing to apply
*/
if( !(instw->gstatus&INSTW_INITIALIZED) ||
!(instw->gstatus&INSTW_OKTRANSL) ) finalize(0);
/* let's get the file translation status */
if(instw_getstatus(instw,&status)) finalize(-1);
/* we ignore files already translated */
if(status & INSTW_TRANSLATED) return 0;
strcpy(basepart,instw->reslvpath);
strcpy(dirpart,instw->reslvpath);
pbase=basename(basepart);
pdir=dirname(dirpart);
/* recursivity termination test, */
if(pdir[0]=='/' && pdir[1]=='\0' && pbase[0]=='\0') {
instw->status|=INSTW_TRANSLATED;
finalize(0);
}
instw_new(&iw);
instw_setpath(&iw,pdir);
instw_apply(&iw);
instw_delete(&iw);
/* will we have to copy the original file ? */
if(!true_lstat(instw->reslvpath,&reslvinfo)) {
copy_path(instw->reslvpath,instw->transl);
/* a symlink ! we have to translate the target */
if(S_ISLNK(reslvinfo.st_mode)) {
wsz=true_readlink(instw->reslvpath,wpath,PATH_MAX);
wpath[wsz]='\0';
instw_new(&iw);
if(wpath[0]!='/') {
strcpy(linkpath,pdir);
strcat(linkpath,"/");
strcat(linkpath,wpath);
} else {
strcpy(linkpath,wpath);
}
instw_setpath(&iw,linkpath);
instw_apply(&iw);
instw_delete(&iw);
}
}
instw->status|=INSTW_TRANSLATED;
instw_setmetatransl(instw);
finalize:
return rcod;
}
/*
* procedure = / rc:=instw_filldirls(instw) /
*
* task = / used to create dummy entries in the mdirlspath reflecting
* the content that would have been accessible with no
* active translation. /
*
* note = /
* --This procedure must be called after instw_makedirls() has been
* called itself.
* --It implies that the translated directory and the real one are
* distincts, but it does not matter if one of them, or both is empty
* /
*/
static int instw_filldirls(instw_t *instw) {
int rcod=0;
DIR *wdir;
struct dirent *went;
char spath[PATH_MAX+1];
char dpath[PATH_MAX+1];
char lpath[PATH_MAX+1];
struct stat sinfo;
struct stat dinfo;
int wfd;
size_t wsz;
instw_t iw_entry;
int status=0;
#if DEBUG
debug(2,"instw_filldirls(%p)\n",instw);
#endif
if((wdir=true_opendir(instw->translpath))==NULL) { return -1; }
while((went=true_readdir(wdir))!=NULL) {
if( went->d_name[0]=='.' &&
( (went->d_name[1]=='\0') ||
( went->d_name[1]=='.' &&
went->d_name[2]=='\0') ) )
{ continue; }
strcpy(spath,instw->translpath);
strcat(spath,"/");
strcat(spath,went->d_name);
if(true_lstat(spath,&sinfo)) { continue; }
strcpy(dpath,instw->mdirlspath);
strcat(dpath,"/");
strcat(dpath,went->d_name);
/* symbolic links */
if(S_ISLNK(sinfo.st_mode)) {
if((wsz=true_readlink(spath,lpath,PATH_MAX))>=0) {
lpath[wsz]='\0';
true_symlink(lpath,dpath);
#if DEBUG
debug(4,"\tfilled symlink : %s\n",dpath);
#endif
}
}
/* regular file */
if(S_ISREG(sinfo.st_mode)) {
if((wfd=true_creat(dpath,sinfo.st_mode))>=0) {
close(wfd);
#if DEBUG
debug(4,"\tfilled regular file : %s\n",dpath);
#endif
}
}
/* directory */
if(S_ISDIR(sinfo.st_mode)) {
true_mkdir(dpath,sinfo.st_mode);
#if DEBUG
debug(4,"\tfilled directory : %s\n",dpath);
#endif
}
/* block special file */
if(S_ISBLK(sinfo.st_mode)) {
true_mknod(dpath,sinfo.st_mode|S_IFBLK,sinfo.st_rdev);
#if DEBUG
debug(4,"\tfilled special block : %s\n",dpath);
#endif
}
/* character special file */
if(S_ISCHR(sinfo.st_mode)) {
true_mknod(dpath,sinfo.st_mode|S_IFCHR,sinfo.st_rdev);
#if DEBUG
debug(4,"\tfilled special char : %s\n",dpath);
#endif
}
/* fifo special file */
if(S_ISFIFO(sinfo.st_mode)) {
true_mknod(dpath,sinfo.st_mode|S_IFIFO,0);
#if DEBUG
debug(4,"\tfilled special fifo : %s\n",dpath);
#endif
}
}
closedir(wdir);
if((wdir=true_opendir(instw->reslvpath))==NULL) return -1;
while((went=true_readdir(wdir))!=NULL) {
if( went->d_name[0]=='.' &&
( (went->d_name[1]=='\0') ||
( went->d_name[1]=='.' &&
went->d_name[2]=='\0') ) )
{ continue; }
strcpy(spath,instw->reslvpath);
strcat(spath,"/");
strcat(spath,went->d_name);
if(true_lstat(spath,&sinfo)) { continue; }
instw_new(&iw_entry);
instw_setpath(&iw_entry,spath);
instw_getstatus(&iw_entry,&status);
/*
* This entry exists in the real fs, but has been
* translated and destroyed in the translated fs.
* So, we mustn't present it !!!
*/
if( (status & INSTW_TRANSLATED) &&
!(status & INSTW_ISINTRANSL) ) { continue; }
strcpy(dpath,instw->mdirlspath);
strcat(dpath,"/");
strcat(dpath,went->d_name);
/* already exists in the translated fs, we iterate */
if(!true_lstat(dpath,&dinfo)) { continue; }
/* symbolic links */
if(S_ISLNK(sinfo.st_mode)) {
if((wsz=true_readlink(spath,lpath,PATH_MAX))>=0) {
lpath[wsz]='\0';
true_symlink(lpath,dpath);
#if DEBUG
debug(4,"\tfilled symlink : %s\n",dpath);
#endif
}
}
/* regular file */
if(S_ISREG(sinfo.st_mode)) {
if((wfd=true_creat(dpath,sinfo.st_mode))>=0) {
close(wfd);
#if DEBUG
debug(4,"\tfilled regular file : %s\n",dpath);
#endif
}
}
/* directory */
if(S_ISDIR(sinfo.st_mode)) {
true_mkdir(dpath,sinfo.st_mode);
#if DEBUG
debug(4,"\tfilled directory : %s\n",dpath);
#endif
}
/* block special file */
if(S_ISBLK(sinfo.st_mode)) {
true_mknod(dpath,sinfo.st_mode|S_IFBLK,sinfo.st_rdev);
#if DEBUG
debug(4,"\tfilled special block : %s\n",dpath);
#endif
}
/* character special file */
if(S_ISCHR(sinfo.st_mode)) {
true_mknod(dpath,sinfo.st_mode|S_IFCHR,sinfo.st_rdev);
#if DEBUG
debug(4,"\tfilled special char : %s\n",dpath);
#endif
}
/* fifo special file */
if(S_ISFIFO(sinfo.st_mode)) {
true_mknod(dpath,sinfo.st_mode|S_IFIFO,0);
#if DEBUG
debug(4,"\tfilled special fifo : %s\n",dpath);
#endif
}
instw_delete(&iw_entry);
}
closedir(wdir);
return rcod;
}
/*
* procedure = / rc:=instw_makedirls(instw) /
*
* task = / eventually prepares a fake temporary directory used to
* present 'overlaid' content to opendir(),readdir()... /
*
* note = /
* --This procedure must be called after instw_setpath().
*
* --The "fake" temporary directories are created and...forgotten.
* If we need to reuse later the same directory, it is previously
* erased, which ensures that it is correclty refreshed.
* /
*
* returns = / 0 ok. makedirls done
* -1 failed. cf errno /
*/
static int instw_makedirls(instw_t *instw) {
int rcod=0;
int status=0;
struct stat translinfo;
struct stat dirlsinfo;
char wdirname[NAME_MAX+1];
#if DEBUG
debug(2,"instw_makedirls(%p)\n",instw);
#endif
/*
* if library incompletely initialized or if translation
* is inactive, nothing to do
*/
if( !(instw->gstatus&INSTW_INITIALIZED) ||
!(instw->gstatus&INSTW_OKTRANSL)) {
strcpy(instw->mdirlspath,instw->path);
return 0;
}
/* let's get the file translation status */
if(instw_getstatus(instw,&status)) return -1;
if( !(status&INSTW_TRANSLATED) ||
((status&INSTW_TRANSLATED) && (status&INSTW_IDENTITY)) ) {
strcpy(instw->mdirlspath,instw->path);
} else {
/* if it's a new directory, we open it in
* the translated fs .
* otherwise, it means that we will have to construct a
* merged directory.
*/
if(!(status & INSTW_ISINROOT)) {
strcpy(instw->mdirlspath,instw->translpath);
} else {
rcod=true_stat(instw->translpath,&translinfo);
sprintf(wdirname,"/%d_%lld_%lld",
instw->pid,
(long long int) translinfo.st_dev,
(long long int) translinfo.st_ino);
strcpy(instw->mdirlspath,instw->mdirls);
strcat(instw->mdirlspath,wdirname);
/* we erase a previous identical dirls */
if(!true_stat(instw->mdirlspath,&dirlsinfo)) {
unlink_recursive(instw->mdirlspath);
}
true_mkdir(instw->mdirlspath,S_IRWXU);
/* we construct the merged directory here */
instw_filldirls(instw);
}
}
#if DEBUG
instw_print(instw);
#endif
return rcod;
}
/*
*
*/
static int backup(const char *path) {
char checkdir[BUFSIZ];
char backup_path[BUFSIZ];
int placeholder,
i,
blen;
struct stat inode,backup_inode;
struct utimbuf timbuf;
#if DEBUG
debug(2,"========= backup () ========= path: %s\n", path);
#endif
/* INSTW_OKBACKUP not set, we won't do any backups */
if (!(__instw.gstatus&INSTW_OKBACKUP)) {
#ifdef DEBUG
debug(3,"Backup not enabled, path: %s\n", path);
#endif
return 0;
}
/* Check if this is inside /dev */
if (strstr (path, "/dev") == path) {
#if DEBUG
debug(3,"%s is inside /dev. Ignoring.\n", path);
#endif
return 0;
}
/* Now check for /tmp */
if (strstr (path, "/tmp") == path) {
#if DEBUG
debug(3,"%s is inside /tmp. Ignoring.\n", path);
#endif
return 0;
}
/* Finally, the backup path itself */
if (strstr (path,__instw.backup ) == path) {
#if DEBUG
debug(3,"%s is inside the backup path. Ignoring.\n", path);
#endif
return 0;
}
/* Does it exist already? */
#if DEBUG
debug(3,"Exists %s?\n", path);
#endif
if (true_stat(path, &inode) < 0) {
/* It doesn't exist, we'll tag it so we won't back it up */
/* if we run into it later */
strcpy(backup_path,__instw.backup );
strncat(backup_path, "/no-backup", 11);
strcat(backup_path, path);
make_path(backup_path);
/* This one's just a placeholder */
placeholder = true_creat(backup_path, S_IREAD);
if (!(placeholder < 0)) close (placeholder);
#if DEBUG
debug(3,"does not exist\n");
#endif
return 0;
}
/* Is this one tagged for no backup (i.e. it didn't previously exist)? */
strcpy (backup_path,__instw.backup);
strncat (backup_path, "/no-backup", 11);
strcat (backup_path, path);
if (true_stat (backup_path, &backup_inode) >= 0) {
#if DEBUG
debug(3,"%s must not be backed up\n", backup_path);
#endif
return 0;
}
#if DEBUG
debug(3,"Si existe, veamos de que tipo es.\n");
#endif
/* Append the path to the backup_path */
strcpy (backup_path,__instw.backup);
strcat (backup_path, path);
/* Create the directory tree for this file in the backup dir */
make_path (backup_path);
/* let's backup the source file */
if(copy_path(path,__instw.backup))
return -1;
/* Check the owner and permission of the created directories */
i=0;
blen = strlen (__instw.backup);
while ( path[i] != '\0' ) {
checkdir[i] = backup_path[blen+i] = path[i];
if (checkdir[i] == '/') { /* Each time a '/' is found, check if the */
checkdir[i+1] = '\0'; /* path exists. If it does, set it's perms. */
if (!true_stat (checkdir, &inode)) {
backup_path[blen+i+1]='\0';
timbuf.actime=inode.st_atime;
timbuf.modtime=inode.st_mtime;
true_utime(backup_path, &timbuf);
true_chmod(backup_path, inode.st_mode);
true_chown(backup_path, inode.st_uid, inode.st_gid);
}
}
i++;
}
return 0;
}
time_t time (time_t *timer) {
TIMECOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"time\n");
#endif
return true_time(timer);
}
/*
* *****************************************************************************
*/
int chdir(const char *pathname) {
int result;
instw_t instw;
int status;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"chdir(%s)n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_chdir(pathname);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
instw_getstatus(&instw,&status);
if(status&INSTW_TRANSLATED && !(status&INSTW_ISINROOT)) {
result=true_chdir(instw.translpath);
debug(3,"\teffective chdir(%s)\n",instw.translpath);
} else {
result=true_chdir(pathname);
debug(3,"\teffective chdir(%s)\n",pathname);
}
instw_delete(&instw);
return result;
}
int chmod(const char *path, mode_t mode) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"chmod(%s,mode)\n",path);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_chmod(path,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,path);
#if DEBUG
instw_print(&instw);
#endif
backup (instw.truepath);
instw_apply(&instw);
result = true_chmod(instw.translpath, mode);
log("%d\tchmod\t%s\t0%04o\t#%s\n",result,
instw.reslvpath,mode,error(result));
instw_delete(&instw);
return result;
}
int chown(const char *path, uid_t owner, gid_t group) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"chown(%s,owner,group)\n",path);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_chown(path,owner,group);
return result;
}
instw_new(&instw);
instw_setpath(&instw,path);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_chown(instw.translpath,owner,group);
log("%d\tchown\t%s\t%d\t%d\t#%s\n",result,
instw.reslvpath,owner,group,error(result));
instw_delete(&instw);
return result;
}
int chroot(const char *path) {
int result;
char canonic[MAXPATHLEN];
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"chroot(%s)\n",path);
#endif
canonicalize(path, canonic);
result = true_chroot(path);
/*
* From now on, another log file will be written if
* INSTW_LOGFILE is set
*/
log("%d\tchroot\t%s\t#%s\n", result, canonic, error(result));
return result;
}
int creat(const char *pathname, mode_t mode) {
/* Is it a system call? */
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"creat(%s,mode)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_creat(pathname,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result = true_open(instw.translpath,O_CREAT|O_WRONLY|O_TRUNC,mode);
log("%d\tcreat\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int fchmod(int filedes, mode_t mode) {
int result;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"fchmod\n");
#endif
result = true_fchmod(filedes, mode);
log("%d\tfchmod\t%d\t0%04o\t#%s\n",result,filedes,mode,error(result));
return result;
}
int fchown(int fd, uid_t owner, gid_t group) {
int result;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"fchown\n");
#endif
result = true_fchown(fd, owner, group);
log("%d\tfchown\t%d\t%d\t%d\t#%s\n",result,fd,owner,group,error(result));
return result;
}
FILE *fopen(const char *pathname, const char *mode) {
FILE *result;
instw_t instw;
int status=0;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"fopen(%s,%s)\n",pathname,mode);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_fopen(pathname,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+') {
backup(instw.truepath);
instw_apply(&instw);
log("%d\tfopen\t%s\t#%s\n",(int)result,
instw.reslvpath,error(result));
}
instw_getstatus(&instw,&status);
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective fopen(%s)",instw.translpath);
result=true_fopen(instw.translpath,mode);
} else {
debug(4,"\teffective fopen(%s)",instw.path);
result=true_fopen(instw.path,mode);
}
if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+')
log("%d\tfopen\t%s\t#%s\n",(int)result,
instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int ftruncate(int fd, TRUNCATE_T length) {
int result;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"ftruncate\n");
#endif
result = true_ftruncate(fd, length);
log("%d\tftruncate\t%d\t%d\t#%s\n",result,fd,(int)length,error(result));
return result;
}
char *getcwd(char *buffer,size_t size) {
char wpath[PATH_MAX+1];
char *result;
char *wptr;
size_t wsize;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"getcwd(%p,%ld)\n",buffer,(long int)size);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_getcwd(buffer,size);
return result;
}
if( __instw.gstatus&INSTW_INITIALIZED &&
__instw.gstatus&INSTW_OKTRANSL &&
(NULL!=(result=true_getcwd(wpath,sizeof(wpath)))) ) {
/* we untranslate any translated path */
if(strstr(wpath,__instw.transl)==wpath) {
wptr=wpath+strlen(__instw.transl);
wsize=strlen(wptr)+1;
} else {
wptr=wpath;
wsize=strlen(wptr)+1;
}
if (buffer == NULL) {
if (size !=0 && size < wsize) {
result=NULL;
errno=(size<=0?EINVAL:ERANGE);
} else {
result=malloc(wsize);
if(result == NULL) {
errno=ENOMEM;
} else {
strcpy(result,wptr);
}
}
} else {
if(size>=wsize) {
strcpy(buffer,wptr);
} else {
result=NULL;
errno=(size<=0?EINVAL:ERANGE);
}
}
} else {
result=true_getcwd(buffer,size);
}
#if DEBUG
debug(3,"\teffective getcwd(%s,%ld)\n",
(result?buffer:"(null)"),(long int)size);
#endif
return result;
}
int lchown(const char *path, uid_t owner, gid_t group) {
/* Linux specific? */
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"lchown(%s,owner,group)\n",path);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_lchown(path,owner,group);
return result;
}
instw_new(&instw);
instw_setpath(&instw,path);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_lchown(instw.translpath,owner,group);
log("%d\tlchown\t%s\t%d\t%d\t#%s\n",result,
instw.reslvpath,owner,group,error(result));
instw_delete(&instw);
return result;
}
int link(const char *oldpath, const char *newpath) {
int result;
instw_t instw_o;
instw_t instw_n;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"link(%s,%s)\n",oldpath,newpath);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_link(oldpath,newpath);
return result;
}
instw_new(&instw_o);
instw_new(&instw_n);
instw_setpath(&instw_o,oldpath);
instw_setpath(&instw_n,newpath);
#if DEBUG
instw_print(&instw_o);
instw_print(&instw_n);
#endif
backup(instw_o.truepath);
instw_apply(&instw_o);
instw_apply(&instw_n);
result=true_link(instw_o.translpath,instw_n.translpath);
log("%d\tlink\t%s\t%s\t#%s\n",result,
instw_o.reslvpath,instw_n.reslvpath,error(result));
instw_delete(&instw_o);
instw_delete(&instw_n);
return result;
}
int mkdir(const char *pathname, mode_t mode) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"mkdir(%s,mode)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_mkdir(pathname,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
instw_apply(&instw);
result=true_mkdir(instw.translpath,mode);
log("%d\tmkdir\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int __xmknod(int version,const char *pathname, mode_t mode,dev_t *dev) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"mknod(%s,mode,dev)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_xmknod(version,pathname,mode,dev);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
instw_apply(&instw);
backup(instw.truepath);
result=true_xmknod(version,instw.translpath,mode,dev);
log("%d\tmknod\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int open(const char *pathname, int flags, ...) {
/* Eventually, there is a third parameter: it's mode_t mode */
va_list ap;
mode_t mode;
int result;
instw_t instw;
int status;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"open(%s,%d,mode)\n",pathname,flags);
#endif
va_start(ap, flags);
mode = va_arg(ap, mode_t);
va_end(ap);
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_open(pathname,flags,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
if(flags & (O_WRONLY | O_RDWR)) {
backup(instw.truepath);
instw_apply(&instw);
}
instw_getstatus(&instw,&status);
if(status&INSTW_TRANSLATED)
result=true_open(instw.translpath,flags,mode);
else
result=true_open(instw.path,flags,mode);
if(flags & (O_WRONLY | O_RDWR))
log("%d\topen\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
/*
*
*/
DIR *opendir(const char *dirname) {
DIR *result;
instw_t instw;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"opendir(%s)\n",dirname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_opendir(dirname);
return result;
}
instw_new(&instw);
instw_setpath(&instw,dirname);
instw_makedirls(&instw);
#if DEBUG
instw_print(&instw);
#endif
result=true_opendir(instw.mdirlspath);
instw_delete(&instw);
return result;
}
struct dirent *readdir(DIR *dir) {
struct dirent *result;
if (!libc_handle)
initialize();
#if DEBUG
debug(3,"readdir(%p)\n",dir);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_readdir(dir);
return result;
}
result=true_readdir(dir);
#if DEBUG
__instw_printdirent(result);
#endif
return result;
}
int readlink(const char *path,char *buf,size_t bufsiz) {
int result;
instw_t instw;
int status;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"readlink(\"%s\",%p,%ld)\n",path,buf,(long int)bufsiz);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_readlink(path,buf,bufsiz);
return result;
}
instw_new(&instw);
instw_setpath(&instw,path);
instw_getstatus(&instw,&status);
#if DEBUG
instw_print(&instw);
#endif
if(status&INSTW_TRANSLATED)
result=true_readlink(instw.translpath,buf,bufsiz);
else
result=true_readlink(instw.path,buf,bufsiz);
instw_delete(&instw);
return result;
}
char *realpath(const char *file_name,char *resolved_name) {
char *result;
if (!libc_handle)
initialize();
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_realpath(file_name,resolved_name);
return result;
}
result=true_realpath(file_name,resolved_name);
return result;
}
int rename(const char *oldpath, const char *newpath) {
int result;
instw_t oldinstw;
instw_t newinstw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"rename(\"%s\",\"%s\")\n",oldpath,newpath);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_rename(oldpath,newpath);
return result;
}
instw_new(&oldinstw);
instw_new(&newinstw);
instw_setpath(&oldinstw,oldpath);
instw_setpath(&newinstw,newpath);
#if DEBUG
instw_print(&oldinstw);
instw_print(&newinstw);
#endif
backup(oldinstw.truepath);
instw_apply(&oldinstw);
instw_apply(&newinstw);
result=true_rename(oldinstw.translpath,newinstw.translpath);
log("%d\trename\t%s\t%s\t#%s\n",result,
oldinstw.reslvpath,newinstw.reslvpath,error(result));
instw_delete(&oldinstw);
instw_delete(&newinstw);
return result;
}
int rmdir(const char *pathname) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"rmdir(%s)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_rmdir(pathname);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
backup(instw.truepath);
instw_apply(&instw);
result=true_rmdir(instw.translpath);
log("%d\trmdir\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int scandir( const char *dir,struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const void *,const void *) ) {
int result;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"scandir(%s,%p,%p,%p)\n",dir,namelist,select,compar);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_scandir(dir,namelist,select,compar);
return result;
}
result=true_scandir(dir,namelist,select,compar);
return result;
}
int __xstat(int version,const char *pathname,struct stat *info) {
int result;
instw_t instw;
int status;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"stat(%s,%p)\n",pathname,info);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_xstat(version,pathname,info);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
instw_getstatus(&instw,&status);
#if DEBUG
instw_print(&instw);
#endif
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective stat(%s,%p)\n",
instw.translpath,info);
result=true_xstat(version,instw.translpath,info);
} else {
debug(4,"\teffective stat(%s,%p)\n",
instw.path,info);
result=true_xstat(version,instw.path,info);
}
instw_delete(&instw);
return result;
}
int __lxstat(int version,const char *pathname,struct stat *info) {
int result;
instw_t instw;
int status;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"lstat(%s,%p)\n",pathname,info);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_lxstat(version,pathname,info);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
instw_getstatus(&instw,&status);
#if DEBUG
instw_print(&instw);
#endif
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective lstat(%s,%p)\n",
instw.translpath,info);
result=true_lxstat(version,instw.translpath,info);
} else {
debug(4,"\teffective lstat(%s,%p)\n",
instw.path,info);
result=true_lxstat(version,instw.path,info);
}
instw_delete(&instw);
return result;
}
int symlink(const char *pathname, const char *slink) {
int result;
instw_t instw;
instw_t instw_slink;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"symlink(%s,%s)\n",pathname,slink);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_symlink(pathname,slink);
return result;
}
instw_new(&instw);
instw_new(&instw_slink);
instw_setpath(&instw,pathname);
instw_setpath(&instw_slink,slink);
#if DEBUG
instw_print(&instw_slink);
#endif
backup(instw_slink.truepath);
instw_apply(&instw_slink);
result=true_symlink(pathname,instw_slink.translpath);
log("%d\tsymlink\t%s\t%s\t#%s\n",
result,instw.path,instw_slink.reslvpath,error(result));
instw_delete(&instw);
instw_delete(&instw_slink);
return result;
}
int truncate(const char *path, TRUNCATE_T length) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"truncate(%s,length)\n",path);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_truncate(path,length);
return result;
}
instw_new(&instw);
instw_setpath(&instw,path);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_truncate(instw.translpath,length);
log("%d\ttruncate\t%s\t%d\t#%s\n",result,
instw.reslvpath,(int)length,error(result));
instw_delete(&instw);
return result;
}
int unlink(const char *pathname) {
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"unlink(%s)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_unlink(pathname);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_unlink(instw.translpath);
log("%d\tunlink\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int utime (const char *pathname, const struct utimbuf *newtimes) {
int result;
instw_t instw;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"utime(%s,newtimes)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_utime(pathname,newtimes);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_utime(instw.translpath,newtimes);
log("%d\tutime\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int utimes (const char *pathname, const struct timeval *newtimes) {
int result;
instw_t instw;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"utimes(%s,newtimes)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_utimes(pathname,newtimes);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_utimes(instw.translpath,newtimes);
log("%d\tutimes\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int access (const char *pathname, int type) {
int result;
instw_t instw;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"access(%s,%d)\n",pathname,type);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_access(pathname,type);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_access(instw.translpath,type);
log("%d\taccess\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
#if(GLIBC_MINOR >= 1)
int creat64(const char *pathname, __mode_t mode) {
/* Is it a system call? */
int result;
instw_t instw;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"creat64(%s,mode)\n",pathname);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_creat64(pathname,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_open64(instw.translpath,O_CREAT | O_WRONLY | O_TRUNC, mode);
log("%d\tcreat\t%s\t#%s\n",result,instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int ftruncate64(int fd, __off64_t length) {
int result;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"ftruncate64\n");
#endif
result = true_ftruncate64(fd, length);
log("%d\tftruncate\t%d\t%d\t#%s\n",result,fd,(int)length,error(result));
return result;
}
FILE *fopen64(const char *pathname, const char *mode) {
FILE *result;
instw_t instw;
int status;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"fopen64(%s,%s)\n",pathname,mode);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_fopen64(pathname,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+') {
backup(instw.truepath);
instw_apply(&instw);
}
instw_getstatus(&instw,&status);
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective fopen64(%s)",instw.translpath);
result=true_fopen64(instw.translpath,mode);
} else {
debug(4,"\teffective fopen64(%s)",instw.path);
result=true_fopen64(instw.path,mode);
}
if(mode[0]=='w'||mode[0]=='a'||mode[1]=='+')
log("%d\tfopen64\t%s\t#%s\n",(int)result,
instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
int open64(const char *pathname, int flags, ...) {
/* Eventually, there is a third parameter: it's mode_t mode */
va_list ap;
mode_t mode;
int result;
instw_t instw;
int status;
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"open64(%s,%d,mode)\n",pathname,flags);
#endif
va_start(ap, flags);
mode = va_arg(ap, mode_t);
va_end(ap);
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_open64(pathname,flags,mode);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
#if DEBUG
instw_print(&instw);
#endif
if(flags & (O_WRONLY | O_RDWR)) {
backup(instw.truepath);
instw_apply(&instw);
}
instw_getstatus(&instw,&status);
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective open64(%s)",instw.translpath);
result=true_open64(instw.translpath,flags,mode);
} else {
debug(4,"\teffective open64(%s)",instw.path);
result=true_open64(instw.path,flags,mode);
}
if(flags & (O_WRONLY | O_RDWR))
log("%d\topen\t%s\t#%s\n",result,
instw.reslvpath,error(result));
instw_delete(&instw);
return result;
}
struct dirent64 *readdir64(DIR *dir) {
struct dirent64 *result;
if (!libc_handle)
initialize();
#if DEBUG
debug(3,"readdir64(%p)\n",dir);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_readdir64(dir);
return result;
}
result=true_readdir64(dir);
#if DEBUG
__instw_printdirent64(result);
#endif
return result;
}
int scandir64( const char *dir,struct dirent64 ***namelist,
int (*select)(const struct dirent64 *),
int (*compar)(const void *,const void *) ) {
int result;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"scandir64(%s,%p,%p,%p)\n",dir,namelist,select,compar);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_scandir64(dir,namelist,select,compar);
return result;
}
result=true_scandir64(dir,namelist,select,compar);
return result;
}
int __xstat64(int version,const char *pathname,struct stat64 *info) {
int result;
instw_t instw;
int status;
#if DEBUG
debug(2,"stat64(%s,%p)\n",pathname,info);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_xstat64(version,pathname,info);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
instw_getstatus(&instw,&status);
#if DEBUG
instw_print(&instw);
#endif
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective stat64(%s,%p)\n",
instw.translpath,info);
result=true_xstat64(version,instw.translpath,info);
} else {
debug(4,"\teffective stat64(%s,%p)\n",
instw.path,info);
result=true_xstat64(version,instw.path,info);
}
instw_delete(&instw);
return result;
}
int __lxstat64(int version,const char *pathname,struct stat64 *info) {
int result;
instw_t instw;
int status;
#if DEBUG
debug(2,"lstat64(%s,%p)\n",pathname,info);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_lxstat64(version,pathname,info);
return result;
}
instw_new(&instw);
instw_setpath(&instw,pathname);
instw_getstatus(&instw,&status);
#if DEBUG
instw_print(&instw);
#endif
if(status&INSTW_TRANSLATED) {
debug(4,"\teffective lstat64(%s,%p)\n",
instw.translpath,info);
result=true_lxstat64(version,instw.translpath,info);
} else {
debug(4,"\teffective lstat64(%s,%p)\n",
instw.path,info);
result=true_lxstat64(version,instw.path,info);
}
instw_delete(&instw);
return result;
}
int truncate64(const char *path, __off64_t length) {
int result;
instw_t instw;
if (!libc_handle)
initialize();
REFCOUNT;
if (!libc_handle)
initialize();
#if DEBUG
debug(2,"truncate64(%s,length)\n",path);
#endif
/* We were asked to work in "real" mode */
if( !(__instw.gstatus & INSTW_INITIALIZED) ||
!(__instw.gstatus & INSTW_OKWRAP) ) {
result=true_truncate64(path,length);
return result;
}
instw_new(&instw);
instw_setpath(&instw,path);
#if DEBUG
instw_print(&instw);
#endif
backup(instw.truepath);
instw_apply(&instw);
result=true_truncate64(instw.translpath,length);
log("%d\ttruncate\t%s\t%d\t#%s\n",result,
instw.reslvpath,(int)length,error(result));
instw_delete(&instw);
return result;
}
#endif /* GLIBC_MINOR >= 1 */