#include #include #include #include #include #include #include #include #include "vfs.h" #include static char vopen_copyonwrite[1024]; static char vopen_trunconwrite[1024]; static char *vopen_path[1024]; /* Check if the directory holding fname is a virtual directory (has a makefile.dat). If fname is . or .., return false as well */ static bool vopen_isvirtual(const char *fname) { bool ret = false; const char *pt = strrchr(fname,'/'); if (pt == NULL){ if (fname[0] == '.'){ if (fname[1] == '\0'){ return false; }else if (fname[1] == '.' && fname[2] == '\0'){ return false; } } struct stat st; ret = stat("makefile.dat",&st)!=-1; }else{ if (pt[1] == '.'){ if (pt[2] == '\0'){ return false; }else if (pt[2] == '.' && pt[3] == '\0'){ return false; } } char path[PATH_MAX]; strcpy (path,fname); strcpy (path+(int)(pt-fname),"/makefile.dat"); struct stat st; ret = stat(path,&st)!=-1; } return ret; } /* Return true if fname is a control file. Those files should be hidden to the user. */ static bool vopen_isctrl (const char *fname) { const char *pt = strrchr(fname,'/'); if (pt != NULL) fname = pt+1; return strcmp(fname,"makefile.dat")==0 || strcmp(fname,"makefile.del")==0; } /* Ouverture d'un fichier dans un r‚pertoire virtuel. (voir les fonction vdir_xxxx) V‚rifie si le path fname correspond existe localement ou dans l'archive. Si le fichier est ouvert en ‚criture et que le fichier est pr‚sent dans l'archive et pas localement, le fichier sera copier localement avant l'ouverture. Retourne NULL si erreur ou ne peut ouvrir ou fichier existe pas. Attention: L'ouverture en ‚criture seulement est identique … open (un peu plus lent peut-ˆtre). */ static int vopen ( const char *fname, int mode, int acces) /* Sert seulement avec O_CREAT */ { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(fname)){ if (vopen_isctrl(fname)){ ret = -1; errno = ENOENT; }else{ #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"open :%s:\n",fname); write (2,tmp,len); } #endif char reel[PATH_MAX]; int exist = vdir_exist (fname,reel); #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"reel %d :%s:\n",exist,reel); write (2,tmp,len); } #endif char copyonwrite = 0; char trunconwrite = 0; const char *orig_fname = fname; int oldmode = mode; const char *oldfname = fname; if (exist == 2){ /* Le fichier est pr‚sent dans l'archive. Si ouverture pour modification (pas seulement ‚criture), on doit copier le fichier */ fname = reel; if ((mode & O_APPEND) != 0 || (mode & O_RDWR)==O_RDWR){ copyonwrite = 1; }else if ((mode & (O_WRONLY|O_TRUNC|O_CREAT))==0){ /* Forcement mode lecture */ copyonwrite = 1; }else{ copyonwrite = 1; trunconwrite = 1; } mode = O_RDONLY; } char lnk[PATH_MAX]; int lenlnk = __readlink (fname,lnk,sizeof(lnk)); // fprintf (stderr,"readlink %d :%s: :%*.*s:\n",lenlnk,fname,lenlnk,lenlnk,lnk); if (lenlnk > 0){ lnk[lenlnk] = '\0'; const char *newfile = lnk; char abslnk[PATH_MAX]; if (lnk[0] != '/'){ const char *pt = strrchr(oldfname,'/'); if (pt != NULL){ strcpy (abslnk,oldfname); strcpy (abslnk+(int)(pt-oldfname)+1,lnk); newfile = abslnk; } } ret = vopen (newfile,oldmode,acces); if (ret == DISPATCH_DONTCARE){ ret = __libc_open (newfile,oldmode,acces); } }else{ ret = __libc_open (fname,mode,acces); if (ret != -1){ vopen_copyonwrite[ret] = copyonwrite; vopen_trunconwrite[ret] = trunconwrite; if (exist==2){ vopen_path[ret]= strdup(orig_fname); }else{ vopen_path[ret]= NULL; } } } } } return ret; } static int vwrite (int fd, const void *buf, unsigned size) { int ret = -1; if (fd >= 0 && fd <1024){ char docopy = vopen_copyonwrite[fd]; char dotrunc = vopen_trunconwrite[fd]; if (dotrunc){ // fprintf (stderr,"dotrunc fd = %d docopy %d %s\n",fd,docopy,vopen_path[fd]); const char *fname = vopen_path[fd]; int newfd = __libc_open (fname,O_RDWR|O_CREAT,0666); free ((char*)fname); vopen_path[fd] = NULL; struct stat st; if (fstat(fd,&st)!=-1) fchmod (newfd,st.st_mode); dup2 (newfd,fd); __libc_close (newfd); __libc_lseek (fd,0,SEEK_SET); vopen_copyonwrite[fd] = 0; vopen_trunconwrite[fd] = 0; }else if (docopy){ // fprintf (stderr,"docopy fd = %d docopy %d %s\n",fd,docopy,vopen_path[fd]); long oldpos = __libc_lseek (fd,0,SEEK_CUR); const char *fname = vopen_path[fd]; int newfd = __libc_open (fname,O_RDWR|O_CREAT,0666); free ((char*)fname); vopen_path[fd] = NULL; struct stat st; if (fstat(fd,&st)!=-1) fchmod (newfd,st.st_mode); int nb; char tmp[12*1024]; __libc_lseek (fd,0,SEEK_SET); while ((nb=__libc_read (fd,tmp,sizeof(tmp)))>0){ __libc_write (newfd,tmp,nb); } __libc_lseek (newfd,oldpos,SEEK_SET); dup2 (newfd,fd); __libc_close (newfd); vopen_copyonwrite[fd] = 0; } ret = __libc_write (fd,buf,size); }else{ ret = __libc_write (fd,buf,size); } return ret; } static int vaccess (const char *fname, int) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(fname)){ if (vopen_isctrl(fname)){ errno = ENOENT; ret = -1; }else{ char reel[PATH_MAX]; ret = vdir_exist (fname,reel) != 0 ? 0 : -1; } } return ret; } static int vclose (int fd) { free (vopen_path[fd]); vopen_path[fd] = NULL; return __libc_close (fd); } #if 0 static int vioctl (int fd, unsigned request, void *a1, void *a2, void *a3) { return __libc_ioctl (fd,request,a1,a2,a3); } #endif static int vstat (int ver, const char *fname, struct stat *buf) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(fname)){ if (vopen_isctrl(fname)){ errno = ENOENT; ret = -1; }else{ char reel[PATH_MAX]; int exist = vdir_exist (fname,reel); #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"vstat :%s: -> %d :%s:\n",fname,exist,reel); write (2,tmp,len); } #endif if (exist > 0){ char lnk[PATH_MAX]; int lenlnk = __readlink (exist == 2 ? reel : fname ,lnk,sizeof(lnk)); // fprintf (stderr,"readlink %d :%s: :%*.*s:\n",lenlnk,fname,lenlnk,lenlnk,lnk); if (lenlnk > 0){ lnk[lenlnk] = '\0'; const char *newfile = lnk; char abslnk[PATH_MAX]; if (lnk[0] != '/'){ const char *pt = strrchr(fname,'/'); if (pt != NULL){ strcpy (abslnk,fname); strcpy (abslnk+(int)(pt-fname)+1,lnk); newfile = abslnk; } } ret = vstat (ver,newfile,buf); if (ret == DISPATCH_DONTCARE){ ret = __xstat (ver,newfile,buf); } }else{ ret = __xstat (ver,reel,buf); } } } } return ret; } static int vlstat (int ver, const char *fname, struct stat *buf) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(fname)){ if (vopen_isctrl(fname)){ errno = ENOENT; ret = -1; }else{ char reel[PATH_MAX]; int exist = vdir_exist (fname,reel); #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"vlstat :%s: -> %d :%s:\n",fname,exist,reel); write (2,tmp,len); } #endif if (exist == 2){ ret = __lxstat (ver,reel,buf); } } } return ret; } static int vexecve ( const char *fname, char * const argv[], char * const env[]) { int ret = DISPATCH_DONTCARE; // fprintf (stderr,"vexecve test :%s:\n",fname); if (vopen_isvirtual(fname)){ char reel[PATH_MAX]; int exist = vdir_exist (fname,reel); #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"vexecve :%s: -> %d :%s:\n",fname,exist,reel); write (2,tmp,len); } #endif if (exist == 2){ ret = _dispatch_execve (reel,argv,env); } } return ret; } static int vunlink (const char *fname) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(fname)){ if (vopen_isctrl(fname)){ ret = -1; errno = ENOENT; }else{ ret = 0; char name[MAXSIZ_NAME]; char path[MAXSIZ_PATH]; char makefile_dat[MAXSIZ_PATH]; path_splitlex (fname,path,name); path_make (path,"makefile.dat",makefile_dat); if (!vfile_exist (makefile_dat)){ ret = unlink (fname); }else if (strcmp(name,"makefile.dat")==0 || strcmp(name,"makefile.del")==0){ errno = ENOENT; ret = -1; }else{ int exist = vdir_exist(fname,NULL); if (exist == 0){ errno = ENOENT; ret = -1; }else{ if (exist == 1){ /* le fichier est pr‚sent localement dans le r‚pertoire. */ ret = unlink (fname); } if (ret != -1 && vdir_unlink (fname) != -1){ FILE *fout; /* Calcul le path du fichier makefile.del */ char pathdel[MAXSIZ_PATH]; path_make (path,"makefile.del",pathdel); fout = fopen (pathdel,"a"); if (fout != NULL){ fprintf (fout,"%s\n",name); ret = fclose (fout); }else{ ret = -1; } } } } } } #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"vunlink :%s: -> %d\n",fname,ret); write (2,tmp,len); } #endif return ret; } static int vrename (const char *from, const char *to) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(from) || vopen_isvirtual(to)){ ret = 0; char name[MAXSIZ_NAME]; char path[MAXSIZ_PATH]; char makefile_dat[MAXSIZ_PATH]; path_splitlex (from,path,name); path_make (path,"makefile.dat",makefile_dat); if (!vfile_exist (makefile_dat)){ ret = rename (from,to); }else if (strcmp(name,"makefile.dat")==0 || strcmp(name,"makefile.del")==0){ ret = -1; }else{ char realpath[MAXSIZ_PATH]; int exist = vdir_exist(from,realpath); if (exist == 0){ ret = -1; }else if (exist == 1){ /* le fichier est pr‚sent localement dans le r‚pertoire. */ ret = rename (from,to); if (ret == 0){ vunlink (from); } }else if (exist == 2){ /* Le fichier est dans /kit/ombre, on doit faire un copy */ ret = file_copyp (realpath,to) == 1 ? 0 : -1; if (ret == 0){ vunlink (from); } } } } return ret; } static int vreadlink (const char *fname, char *buf, size_t bufsiz) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(fname)){ char reel[PATH_MAX]; int exist = vdir_exist (fname,reel); #if 0 { char tmp[1000]; int len = snprintf (tmp,sizeof(tmp),"vreadlink :%s: -> %d :%s:\n",fname,exist,reel); write (2,tmp,len); } #endif ret = -1; if (exist == 2){ ret = __readlink (reel,buf,bufsiz); }else{ ret = __readlink (fname,buf,bufsiz); } } return ret; } struct VIRT_DIR{ int nb; char **tb; int pos; DIR *dir; }; static DIR *vopendir(const char *dname) { DIR *ret = NULL; char makefile_dat[PATH_MAX]; snprintf (makefile_dat,sizeof(makefile_dat)-1,"%s/makefile.dat",dname); struct stat st; if (stat(makefile_dat,&st)!=-1){ VIRT_DIR *vdir = (VIRT_DIR*)calloc (1,sizeof(VIRT_DIR)); if (vdir != NULL){ char *tb[2000]; vdir->nb = vdir_getlist (dname,"*",tb,2000); // Sub-directories are always physically there, so we will // use the real readdir to scan them. This way, we are getting // the real inode number. #if 0 vdir->nb += vdir_getlistd (dname,"*",tb+vdir->nb,2000-vdir->nb); #endif //DEBUG (VDBG,("vdir_getlist :%s: %d\n",dname,vdir->nb)); if (vdir->nb >= 0){ int size = vdir->nb*sizeof(char*); vdir->tb = (char**)malloc(size); if (vdir->tb == NULL){ vdir->nb = 0; }else{ memcpy (vdir->tb,tb,size); ret = (DIR*) vdir; } } vdir->dir = __opendir (dname); } } return ret; } static int vclosedir(DIR *dir) { int ret = -1; if (dir != NULL){ VIRT_DIR *vdir = (VIRT_DIR*)dir; ret = 0; if (vdir->dir != NULL) ret = __closedir (vdir->dir); tbstr_free (vdir->tb,vdir->nb); free (vdir->tb); free (vdir); } return ret; } static struct dirent *vreaddir(DIR *dir) { struct dirent *ret = NULL; if (dir != NULL){ VIRT_DIR *vdir = (VIRT_DIR*)dir; while (1){ if (vdir->pos < vdir->nb){ static struct dirent dd; path_splitlex (vdir->tb[vdir->pos],NULL,dd.d_name); vdir->pos ++; if (strcmp(dd.d_name,"makefile.dat")!=0 && strcmp(dd.d_name,"makefile.del")!=0){ static int alloc_ino = 1; dd.d_ino = alloc_ino++; dd.d_reclen = strlen(dd.d_name); ret = ⅆ break; } }else{ if (vdir->dir != NULL){ while (1){ ret = __readdir (vdir->dir); if (ret == NULL) break; const char *filename = ret->d_name; if (strcmp(filename,"makefile.dat")==0 || strcmp(filename,"makefile.del")==0){ continue; }else{ bool found = false; for (int i=0; inb; i++){ // fprintf (stderr,"Compare :%s: :%s:\n",filename,vdir->tb[i]); if (strcmp(filename,vdir->tb[i])==0){ found = true; break; } } if (!found) break; } } } break; } } } return ret; } #if __GLIBC__ > 2 || ( __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) static int vopen_setxattr (const char *path, const char *name, const void *value, size_t size, int flags) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __setxattr (reel,name,value,size,flags); }else{ ret = __setxattr (path,name,value,size,flags); } } } return ret; } static int vopen_lsetxattr (const char *path, const char *name, const void *value, size_t size, int flags) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __lsetxattr (reel,name,value,size,flags); }else{ ret = __lsetxattr (path,name,value,size,flags); } } } return ret; } static ssize_t vopen_getxattr (const char *path, const char *name, void *value, size_t size) { ssize_t ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __getxattr (reel,name,value,size); }else{ ret = __getxattr (path,name,value,size); } } } return ret; } static ssize_t vopen_lgetxattr (const char *path, const char *name, void *value, size_t size) { ssize_t ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __lgetxattr (reel,name,value,size); }else{ ret = __lgetxattr (path,name,value,size); } } } return ret; } static ssize_t vopen_listxattr (const char *path, char *list, size_t size) { ssize_t ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __listxattr (reel,list,size); }else{ ret = __listxattr (path,list,size); } } } return ret; } static ssize_t vopen_llistxattr (const char *path, char *list, size_t size) { ssize_t ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __llistxattr (reel,list,size); }else{ ret = __llistxattr (path,list,size); } } } return ret; } static int vopen_removexattr (const char *path, const char *name) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __removexattr (reel,name); }else{ ret = __removexattr (path,name); } } } return ret; } static int vopen_lremovexattr (const char *path, const char *name) { int ret = DISPATCH_DONTCARE; if (vopen_isvirtual(path)){ if (vopen_isctrl(path)){ ret = -1; errno = ENOENT; }else{ char reel[PATH_MAX]; int exist = vdir_exist (path,reel); ret = -1; if (exist == 2){ ret = __lremovexattr (reel,name); }else{ ret = __lremovexattr (path,name); } } } return ret; } #endif static DISPATCH m={ NULL, vopen, vclose, // close, vwrite, // write, NULL, // read, NULL, // ioctl, NULL, // lseek, vstat, NULL, // __fxstat, vlstat, NULL, // chdir, vopendir, vreaddir, vclosedir, vrename, vunlink, NULL, // rmdir NULL, // mkdir NULL, // socket NULL, // connect NULL, // bind NULL, // chmod NULL, // chown NULL, // lchown vreadlink, // readlink NULL, // utime vaccess, // access vaccess, // euidaccess NULL, // link NULL, // symlink NULL, // fchmod NULL, // fchown vexecve, // execve NULL, // rewinddir NULL, // seekdir #if __GLIBC__ > 2 || ( __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) setxattr: vopen_setxattr, lsetxattr: vopen_lsetxattr, fsetxattr: NULL, getxattr: vopen_getxattr, lgetxattr: vopen_lgetxattr, fgetxattr: NULL, listxattr: vopen_listxattr, llistxattr: vopen_llistxattr, flistxattr: NULL, removexattr: vopen_removexattr, lremovexattr: vopen_lremovexattr, fremovexattr: NULL, #endif }; static REGISTER_DISPATCH mm (m);