#if 0 ifdef notdef * * POrtable Dodgy Filesystems in Userland (this used to be hacK!) * alias podfuk (v2) * pretend you are venus cache manager, and export virtual files to kernel * (plus, you can get kernel patch there; with it you will not have to cd * /overlay first. Very handy.) * * Copyright 1999 Pavel Machek, pavel@ucw.cz * * You can get more info at: * http://atrey.karlin.mff.cuni.cz/~pavel/podfuk/podfuk.html * * Distribute under GPL version 2 or later. * * Version 2.0 - it works, modulo strange things in tar archives. * Version 2.1 - tar archives + ftp are now fixed. * Version 2.2 - made it Makefile * Version 2.3 - attempt to make it deadlock free, no more complains for strange fids * Version 2.4 - make a bit more demonic * Version 2.5 - roll in changes by qrczak@knm.org.pl - handle uids right * Version 2.6 - add url, fix mknod, do not use ^#, fix paths * Version 2.7 - check return value for select + small fixes by Michael Hart * Version 2.8 - merged small fixes from Pat Beirne , preparation for rw support * Version 2.9 - added rw support, progress bar possibility: Pat Beirne * * !!This version has a problem, buried in VFS. A user copies two files, * A and B, both on the VFS. The sequence: open(A,O_RDONLY), open(B,WR_ONLY), * read(A), write(B), close(B), close(A). The first open() causes the * VFS to read to a local copy. The second open() makes a local empty temp file. * The first close() will write the temp file to the VFS AND FLUSH THE * VFS DIRECTORY CACHE!. When the second close() is called, it re-reads the * file from the VFS. Wasteful but not fatal. Bad.!! * * This version does not clean up the v_list linked list. Bad. !! * * Notice this file is *evil*. It is both valid c source and working * Makefile. You might want to ln -s podfuk.c Makefile. * * Edit MIDNIGHT= to point at root of mc sources, you need sources of * mc-4.5.5 or newer for this to work. (Youll miss file callback.h if * you use 4.5.30 or older - get it from homepage.) * * Edit KERNEL= to point include/ directory of your 2.2 kernel source * endif # DEBUG turns on diagnostic printing # USE_MONITOR makes calls to an X progress bar CFLAGS = -DUSE_MONITOR -DCOREL_DEF -Wall #CFLAGS = -DDEBUG -DUSE_MONITOR -DCOREL_DEF -Wall -g #CFLAGS = -DDEBUG -Wall -DNODAEMON MIDNIGHT=/home/patb/projects/mc-4.5.40 KERNEL=/usr/src/linux/include VFS=$(MIDNIGHT)/vfs LINUX=/usr/src/linux all: podfuk podfuk: $(VFS)/libvfs.so podfuk.c gcc podfuk.c `glib-config --libs --cflags` -I$(MIDNIGHT) -I$(KERNEL) $(VFS)/libvfs.so -o podfuk $(CFLAGS) podfuk-static: podfuk.c gcc podfuk.c `glib-config --libs --cflags` -I$(MIDNIGHT) -I$(KERNEL) $(VFS)/tcputil.so $(VFS)/fish.so $(VFS)/ftpfs.so $(VFS)/mcfs.so $(VFS)/utilvfs.so $(VFS)/local.so $(VFS)/vfs.so $(VFS)/tar.so $(VFS)/sfs.so $(VFS)/names.so $(VFS)/container.so $(VFS)/extfs.so $(VFS)/util-alone.so $(VFS)/util.sor $(VFS)/utilunix.sor $(VFS)/direntry.so -o podfuk-static $(CFLAGS) /dev/cfs0: mknod /dev/cfs0 c 67 5 /overlay: mkdir /overlay run: /dev/cfs0 /overlay podfuk { cd $(LINUX)/fs/coda; insmod coda.o; } sleep 1 { ./podfuk; } & sleep 1 mount /dev/cfs0 /overlay -tcoda clean: rm podfuk podfuk-static $(MIDNIGHT)/vfs/libvfs.so: echo 'Attempting to make libvfs.so from midnight' cd $(MIDNIGHT)/vfs make libvfs.so ifdef donotdefine #endif /* TTD: add timeouts for non-operative ftp sites a lookup request precipitates a login & read; is this neccessary for the root? how are we going to clean up the vfid chain? there could be an "infinite" number of entries */ /* Coda/Venus in a nutshell: * Coda is a filesystem, lives in the kernel * Venus is an app; podfuk is a replacement for venus * When a coda mount needs service, it sends a message * up to podfuk through a pipe * Coda/Podfuk both open /dev/cfs0 and make ioctl calls * Message includes ViceFid (coda file/dir handle) * which is: u32 volume-id, u32 node-id, u32 unique-num * Each file has an attribute struct: very similar to inode * * Typ sequence of calls to podfuk from coda: * file read: access(), open(), close() * (the read() is done through the local file) * dir listing: access(), open(), lookup(), access(), close() * file write: lookup(file), access(parent), create(file), getattr(), * open(), close(), setattr() * * disk scheme: * /mnt/ftp/aliasname * has placeholders such as * /mnt/ftp/corel -> /mnt/.mcvfs/#ftp:ftp.corel.com * /mnt/.mcvfs is mounted as -t coda */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VFS_STANDALONE 1 #include #include #ifndef USE_VFS #error Midnight was not configured to use vfs. Please configure it to use it. #endif #define PARANOIA 1 #define VERSION_STRING "2.9.1" #ifdef DEBUG # define DEBUG_PRINT printf # define FLUSH_TIME 15 #else # define DEBUG_PRINT if (0) printf # define FLUSH_TIME 300 #endif #if COREL_DEF # define MOUNT_POINT_STRING "/mnt/.mcvfs" #else # define MOUNT_POINT_STRING "/overlay" #endif /* There is nothing magic about these constants. I just selected them * so that kernel does not complain too loudly. */ #define MY_VOL 0x01234567 #define MY_VNODE 0xffffffff #define MY_INODE 0x12345678 #define MK_FID(f,v,n,u) {f.Volume=v;f.Vnode=n;f.Unique=u;} /* missing from vfs.h */ vfs * vfs_rosplit (char *path); extern int ftpfs_use_passive_connections; extern int ftpfs_retry_seconds; /* our handle to the pipe */ int cfs; /* our local object: NOTE: we try to do as little as possible * and trust that vfs will handle caching for us */ struct v_list { char *real_name; char *local_name; short usage; char has_changed; // dirty bit char is_dir; char is_created; // marker for files which do not yet exist at the other end char is_trunc; // marker for files which are to be sent to the other end /* timestamp ?? */ }; // accessors #define v_usage(x) ((struct v_list*)x.Unique)->usage #define v_created(x) ((struct v_list*)x.Unique)->is_created /* a linked list of the above objects */ GList *pList; int count = 0; struct stat generic_stat = { 0 /* dev */, 0 /* pad */, 0 /* inode */, __S_IFREG | 0666 /* mode */, 0, 0, /* uid, gid */ 0 /* device */, 0 /* pad */, 0 /* size */, 1024 /* blksize */ }; /* rest are 0 */ #ifdef USE_MONITOR int monitor_handle; char monitor_buf[300]; #endif /******************** shutdown and timeout **********************************/ void signal_handler(int signo) { mc_vfs_done(); exit(0); } void flush_VFids(void) { GList *pWalk, *pPrev; struct coda_purgefid_out cmd; DEBUG_PRINT( "cleaning the local VFid cache\n" ); /* note: we are deleting elements, so we have to walk carefully */ /* walk backward so that we purge children, then parents */ for ( pWalk = g_list_last(pList), pPrev = g_list_previous(pWalk); pPrev; pWalk = pPrev, pPrev = g_list_previous(pPrev)) { if (((struct v_list*)pWalk->data)->usage == 0) { struct v_list* v = (struct v_list*) pWalk->data; DEBUG_PRINT( "purging list element <%s>\n", v->real_name ); /* !!! OOPS !!! if a shell has a cwd on our mount point, then this purge will not work, leaving broken VFids !!! */ cmd.oh.opcode = CODA_PURGEFID; cmd.oh.unique = 0; MK_FID(cmd.CodaFid,MY_VOL,MY_VNODE,(long)v); write(cfs, &cmd, sizeof(cmd)); free( v->real_name ); if (v->local_name) free( v->local_name); free(pWalk->data); pList = g_list_remove_link(pList, pWalk); g_list_free(pWalk); } } } /******************** config ************************************************/ void set_global_var( const char* section, const char* var, const char* val ) { if ( !strcmp( var, "ftpfs_use_passive_connections" ) ) ftpfs_use_passive_connections = ( val[0] == '1' ); if ( !strcmp( var, "logfile" ) && val[0] ) { ftpfs_set_debug( val ); printf("logfile set to %s\n",val); } if ( !strcmp( var, "ftpfs_login_timeout") ) ftpfs_retry_seconds = atoi(val); } #if GLIB_MINOR_VERSION==1 inline char* g_strstrip(char*c) { char *p; for (p=c; *p && isspace(*p); p++); memmove(c,p,strlen(p)+1); for (p=c+strlen(c); p>c && isspace(p[-1]); p--); *p = 0; return c;} #endif /* load and parse an ini file: handles sections, comments and params */ void load_profile( const char* config_file, void (*setter) (const char*, const char*, const char*) ) { char* section = 0; FILE* f = fopen( config_file, "r" ); if ( !f ) return; while (!feof(f)) { char buf[250]; char *p = buf; fgets(buf,250,f); p = g_strstrip(buf); if (p[0] == '[') { g_free(section); g_strdelimit(p+1,"]",'\0'); section = g_strdup(p+1); //printf("got section header [%s]\n", section); } else if (p[0] == '#') { //printf("got a comment <%s>\n",p+1); } else { char *pp; pp = strchr(buf,'='); if (pp>buf && pp<(buf+strlen(buf)-1)) { *pp=0; (*setter)( section, g_strstrip(buf), g_strstrip(pp+1) ); //printf("got strings <%s> and <%s>\n",buf,pp+1); } } } g_free(section); fclose(f); } /*************************** debugging callback ****************************/ #ifdef USE_MONITOR void progress_func( char* m) { if (monitor_handle==-1) return; /* Send the progress string to the monitor. * Add an extra zero to delimit the string. */ write(monitor_handle,m,strlen(m)+1); } #endif // USE_MONITOR /************************** file name maintenance **************************/ char * look_name( ViceFid id ) { if ((id.Volume != MY_VOL) || (id.Vnode != MY_VNODE)) { printf( "Bad handle passed %lx/%lx/%lx\n", id.Volume, id.Vnode, id.Unique ); exit(1); } if (PARANOIA) { GList *pWalk; for( pWalk = pList; pWalk; pWalk = g_list_next(pWalk) ) { if (pWalk->data == (gpointer) id.Unique) break; } if (!pWalk) { printf( "Bad handle passed %lx/%lx/%lx\n", id.Volume, id.Vnode, id.Unique ); return 0; //exit(1); } } /* paranoia */ return ((struct v_list*)id.Unique)->real_name; } ViceFid alloc_vfid( char *name ) { ViceFid res; struct v_list *h = g_new0(struct v_list,1); pList = g_list_append(pList, h); h->real_name = strdup(name); count++; MK_FID(res,MY_VOL,MY_VNODE,(long)h); DEBUG_PRINT( "new vfid created at %p (file <%s>)\n",h,h->real_name ); return res; } #if 0 void dump_vfid_list(void) { GList* pWalk; for ( pWalk = pList; pWalk; pWalk = g_list_next(pWalk) ) { struct v_list* v = (struct v_list*) pWalk->data; printf("list element <%s>\n",v->real_name); } } #endif void delete_name( const char * real_name ) { GList *pWalk; /* at this point, the paranoid would check for a dirty file and write it out */ for( pWalk = pList; pWalk; pWalk = g_list_next(pWalk) ) { if (!strcmp( ((struct v_list*)pWalk->data)->real_name, real_name)) { struct v_list* v = (struct v_list*) (pWalk->data); free(v->real_name); if (v->local_name) free(v->local_name); pList = g_list_remove( pList, pWalk->data ); free(pWalk->data); count--; return; } } printf( "failure trying to delete the file %s\n", real_name ); } inline int CodaToVfsFlags( int flags ) { return ((flags & (C_O_READ | C_O_WRITE)) == (C_O_READ | C_O_WRITE)) ? O_RDWR : (((flags & (C_O_READ | C_O_WRITE)) == C_O_WRITE) ? O_WRONLY : O_RDONLY) | ((flags & C_O_TRUNC) ? O_TRUNC : 0) | ((flags & C_O_CREAT) ? O_CREAT : 0) | ((flags & C_O_EXCL) ? O_EXCL : 0); } /*********************local copy management ********************************/ inline char * get_local_copy( ViceFid id, int flags ) { struct v_list* v = (struct v_list*)id.Unique; /* I would normally put an assert here, but look_name() was called already */ /* truncated files do not need an ftp read first; this code cluster handles it */ v->is_created = 0; if (flags & C_O_WRITE) v->has_changed = 1; if (flags & C_O_TRUNC) { v->is_trunc = 1; v->local_name = tempnam( NULL, "podfu" ); close(open(v->local_name, CodaToVfsFlags(flags),0666)); /* touch the file */ return v->local_name; } else return v->local_name = mc_getlocalcopy(v->real_name); /* fetch from other end */ } void write_dir_entry( int handle, char *name ) { struct venus_dirent ent; int len; static int ino = MY_INODE; ent.d_fileno = ino++; ent.d_type = CDT_REG; /* the kernel does not use this */ ent.d_namlen = len = strlen(name); strcpy(ent.d_name, name); len += ((char *) &ent.d_name - (char *) &ent); ent.d_reclen = len; if (write(handle, &ent, len)<0) printf( "error writing dirent (%m)\n" ); /* FIXME: we should propagate error */ DEBUG_PRINT( "[%s]", name ); } char * get_dir_copy( ViceFid id ) { struct v_list* v = (struct v_list*) id.Unique; DIR *dir = mc_opendir(v->real_name); char *cache; int handle; struct dirent *ent1; char zeros[128] = {0,0, }; if (!dir) return NULL; v->is_dir = 1; DEBUG_PRINT( "converting dir to file " ); cache = tempnam (NULL, "dir"); handle = open (cache, O_WRONLY | O_CREAT | O_EXCL, 0600); if (handle == -1) { printf("can not open temp file for directory list (%m)\n" ); mc_closedir(dir); free(cache); return NULL; } DEBUG_PRINT( "%s:", cache ); /* copy a list file names out to a file */ while ((ent1 = mc_readdir(dir))) write_dir_entry(handle, ent1->d_name); write(handle, zeros, 2); DEBUG_PRINT(" "); close(handle); mc_closedir(dir); return v->local_name = cache; } void unget_local_copy( ViceFid id ) { struct v_list* v = (struct v_list*) id.Unique; if (v->local_name) { if (v->has_changed) DEBUG_PRINT( "saving the file %s ", v->local_name); /* NOTE: if we give mc_ungetlocalcopy() the exact correct localfile name, it will not free() the 2nd parameter string */ if (v->is_dir) { unlink( v->local_name ); } else if (v->is_trunc) { /* mc_filecopy() */ int hw = mc_open(v->real_name, O_WRONLY | O_CREAT | O_TRUNC, 0666); int hr = open(v->local_name, O_RDONLY); if (hw>=0 && hr>=0) { char*p = g_new(char,0x4000); int n = 0x4000; while (n>0) { n = read(hr,p,n); if (n>0) n = mc_write(hw,p,n); } mc_close(hw); close(hr); unlink(v->local_name); free(v->local_name); g_free(p); v->is_trunc = 0; } } else /* will free the local_name */ mc_ungetlocalcopy(v->real_name, v->local_name, v->has_changed); v->local_name = 0; v->has_changed = 0; } else { DEBUG_PRINT( "" ); } } /****************** attribute mangling *************************************/ inline int st2type(struct stat *s) { if (S_ISDIR(s->st_mode)) return C_VDIR; if (S_ISREG(s->st_mode)) return C_VREG; if (S_ISBLK(s->st_mode)) return C_VBLK; if (S_ISCHR(s->st_mode)) return C_VCHR; if (S_ISLNK(s->st_mode)) return C_VLNK; if (S_ISSOCK(s->st_mode)) return C_VSOCK; if (S_ISFIFO(s->st_mode)) return C_VFIFO; printf( "Unknown type in st2type (%o)\n", s->st_mode ); return C_VNON; } void st2attr(struct stat *s, struct coda_vattr *a) { static int id = 0; bzero(a, sizeof(struct coda_vattr)); a->va_type = st2type(s); #define COPY(x) a->va_##x = s->st_##x; COPY(mode); DEBUG_PRINT( "(mode = %o)", s->st_mode ); COPY(nlink); COPY(uid); COPY(gid); a->va_fileid = id++; COPY(size); a->va_blocksize = 1024; /* s->st_blksize; */ a->va_atime.tv_nsec = a->va_ctime.tv_nsec = a->va_mtime.tv_nsec = 0; a->va_atime.tv_sec = s->st_atime; a->va_mtime.tv_sec = s->st_mtime; a->va_ctime.tv_sec = s->st_ctime; a->va_gen = 0; a->va_flags = 0; a->va_bytes = s->st_blocks * s->st_blksize; a->va_filerev = 0; } /* Poor protection against deadlocks when podfuk is mounted on /overlay (right protection is VFS_NO_LOCALHASH, which is unfortunately in new midnights, only). In fact, "new midnight" currently means my own tree at my home machine :-) */ int legal_name(char *name) { if (!strncmp (name, MOUNT_POINT_STRING, strlen(MOUNT_POINT_STRING)) || (strchr(name, '#') && !vfs_rosplit(name))) return 0; return 1; } int main(int argc, char** argv) { char buf[2048]; time_t time_last_msg = (time_t) 0x7fffffff; if (argc>1) { printf("%s: a virtual file system extention to Linux\n",argv[0]); printf(" version " VERSION_STRING "\n"); printf(" usage: modprobe coda; %s; mount /dev/cfs0 /mnt/.mcvfs -tcoda\n",argv[0]); exit(1); } setvbuf(stdout, NULL, _IONBF, 0); cfs = open( "/dev/cfs0", O_RDWR ); if (cfs == -1) { printf( "Error opening cfs0: %m\n" ); exit(1); } #ifndef NODAEMON chdir("/"); if (fork()) { exit(0); } setsid(); /* to guarantee effect, the man page says this should be performed after a fork, as the child... */ DEBUG_PRINT( "podfuk spawned as %d, ", getpid() ); #endif DEBUG_PRINT( "Opened cfs0\n" ); #ifdef USE_MONITOR vfs_set_callback( CALL_INFO, progress_func ); monitor_handle = open("/var/tmp/podfuk.pipe", O_RDWR | O_NONBLOCK); if (monitor_handle==-1) DEBUG_PRINT( "failed to open pipe to monitor\n" ); #endif load_profile( "/etc/podfukrc", set_global_var ); mc_vfs_init(); signal(SIGTERM, signal_handler); /* graceful shutdown */ signal(SIGINT, signal_handler); signal(SIGPIPE, SIG_IGN); /* pipe faults are benign */ vfs_flags |= FL_ALWAYS_MAGIC; #ifdef FL_NO_LOCALHASH vfs_flags |= FL_NO_LOCALHASH; /* Avoid local deadlock when someone accesses /#ahoj#utar */ #else #warning Sorry, your midnight is old. It may deadlock under some uses. #endif while(1) { /* NOTE: both the inbuf/req and the outbuf/rep must be bigger than * just the union given in coda.h */ char in_buf[sizeof(union inputArgs) + CODA_MAXNAMLEN]; union inputArgs *req = (union inputArgs*) &in_buf[0]; char out_buf[sizeof(union outputArgs) + CODA_MAXNAMLEN]; union outputArgs *rep = (union outputArgs*) &out_buf[0]; struct stat st; int msg; int size; char *name; fd_set rfds; struct timeval tv; int retval; /* Watch stdin (fd 0) to see when it has input. */ FD_ZERO(&rfds); FD_SET(cfs, &rfds); /* Wait up to five seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; retval = select(cfs+1, &rfds, NULL, NULL, &tv); /* onError */ if (retval == -1) { printf( "select failed: %m\n" ); exit(1); } /* onTimeout */ if (retval == 0) { if (time(NULL)-time_last_msg > FLUSH_TIME) { flush_VFids(); time_last_msg = (time_t) 0x7fffffff; /* only flush once */ } } vfs_timeout_handler(); /* Don't rely on the value of tv now! */ msg = read(cfs, req, sizeof(*req) + 256); if (msg == -1) continue; DEBUG_PRINT( "got %3.3d byte command: opcode = %2.2ld ", msg, req->ih.opcode ); // printf( " (un=%d,pid=%d,pgid=%d) ", req.ih.unique, req.ih.pid, req.ih.pgid ); // printf( " (uid=%d,euid=%d,suid=%d,fsuid=%d) ",req.ih.cred.cr_uid,req.ih.cred.cr_euid,req.ih.cred.cr_suid,req.ih.cred.cr_fsuid ); setfsgid (req->ih.cred.cr_fsgid); vfs_gid = req->ih.cred.cr_fsgid; setfsuid (req->ih.cred.cr_fsuid); vfs_uid = req->ih.cred.cr_fsuid; rep->oh.opcode = req->ih.opcode; rep->oh.unique = req->ih.unique; rep->oh.result = ENOSYS; size = sizeof(rep->oh); #define CMD(x) rep->oh.result = 0; size = sizeof(rep->coda_##x); DEBUG_PRINT( "%.10s",#x ); #define CMD_NOREP(x) rep->oh.result = 0; DEBUG_PRINT( "%.10s",#x ); //#define DUMP(x) DEBUG_PRINT( "(%x/%x/%x:%s)", req->coda_##x.VFid.Volume, req->coda_##x.VFid.Vnode, req->coda_##x.VFid.Unique, look_name(req->coda_##x.VFid)); #define DUMP(x) DEBUG_PRINT( "\n\tbase:%s\n\t", look_name(req->coda_##x.VFid)); #define DUMP_NAME(x) DEBUG_PRINT( "\n\tbase:%s\tname:%s\n\t", look_name(req->coda_##x.VFid), (char*)req + req->coda_##x.name); #define STAT(x) if (!legal_name(x)) {rep->oh.result = ENOENT; break; } else if (mc_lstat(x, &st) == -1) { rep->oh.result = errno; DEBUG_PRINT( "file %s probably does not exist.",x ); break; } #define BUILD_NAME(x) name = ((char*) req) + req->coda_##x.name; sprintf( buf, "%s/%s", look_name(req->coda_##x.VFid), name); #define GET_VFID_name(x) if (NULL==(name=look_name(req->coda_##x.VFid))) {rep->oh.result = ENOENT; break;} switch (req->ih.opcode) { case CODA_ROOT: CMD(root); DEBUG_PRINT( ": " ); rep->coda_root.VFid = alloc_vfid( "/" ); v_usage(rep->coda_root.VFid) = 1; break; /********************* file props & access *******************/ case CODA_GETATTR: CMD(getattr); DUMP(getattr); GET_VFID_name(getattr); if (!v_created(req->coda_getattr.VFid)) { STAT(name); /* fills in st */ } else st = generic_stat; st2attr( &st, &rep->coda_getattr.attr ); break; case CODA_SETATTR: CMD_NOREP(setattr); DUMP(setattr); GET_VFID_name(setattr); DEBUG_PRINT( " try to set mode to %o, mdate to %lx and owner to %d: ", req->coda_setattr.attr.va_mode, req->coda_setattr.attr.va_mtime.tv_sec, req->coda_setattr.attr.va_uid ); if ( req->coda_setattr.attr.va_mode != (u_short) -1) mc_chmod( name, req->coda_setattr.attr.va_mode ); /* cannot change owner, group or dates .......*/ break; case CODA_ACCESS: /* this always returns TRUE; this seems to be ok with current CODA design */ CMD_NOREP(access); DUMP(access); DEBUG_PRINT( "flags:%x ", req->coda_access.flags ); GET_VFID_name(access); break; case CODA_LOOKUP: /* !! ttd: search for existing VFid's instead of making many new ones */ CMD(lookup); DUMP_NAME(lookup); GET_VFID_name(lookup); DEBUG_PRINT( "flags=%d: ", req->coda_lookup.flags ); BUILD_NAME(lookup); DEBUG_PRINT( "(stat: %s)", buf ); STAT(buf); /* fills in st */ rep->coda_lookup.VFid = alloc_vfid(buf); rep->coda_lookup.vtype = st2type(&st); break; case CODA_READLINK: CMD(readlink); DUMP(readlink); GET_VFID_name(readlink); /* put the string at the end of the structure * the reply packet must be smaller than the request packet */ retval = mc_readlink(name, (char*)&rep + sizeof(rep->coda_readlink), msg - sizeof(rep->coda_readlink)); if (retval<=0) { rep->oh.result = ENOENT; } else { rep->coda_readlink.count = retval; /* == strlen(sym_link_string) */ rep->coda_readlink.data = (void*) sizeof(rep->coda_readlink); size += retval; /* the structure is longer by this amount */ } break; /************************ open, close, create & mkdir ******************/ case CODA_OPEN: CMD(open); DUMP(open); DEBUG_PRINT( "flags:%#x ", req->coda_open.flags ); GET_VFID_name(open); if (!v_created(req->coda_open.VFid)) { STAT(name); } else st = generic_stat; if (S_ISDIR(st.st_mode)) { DEBUG_PRINT( "[open request on a directory]" ); name = get_dir_copy(req->coda_open.VFid); } else { #ifdef USE_MONITOR if (monitor_handle>=0) { char* pName; /* feeble attempt to hide the password */ if (!memcmp(name,"/#ftp:",6)) pName = strchr( name+1,'/' ); if (!pName) pName = name; sprintf(monitor_buf,"NAME %s",pName); write(monitor_handle, monitor_buf, strlen(monitor_buf+1)); } #endif name = get_local_copy(req->coda_open.VFid,req->coda_open.flags); } if (!name || (stat(name, &st)==-1)) { rep->oh.result = errno; break; } v_usage(req->coda_open.VFid) += 1; rep->coda_open.dev = st.st_dev; rep->coda_open.inode = st.st_ino; break; case CODA_CLOSE: CMD_NOREP(close); DUMP(close); GET_VFID_name(close); unget_local_copy(req->coda_close.VFid); DEBUG_PRINT( " the current VFid list has %d elements\n", g_list_length( pList ) ); // dump_vfid_list(); #ifdef USE_MONITOR if (monitor_handle>=0) { write(monitor_handle,"DONE",5); } #endif v_usage(req->coda_close.VFid) -= 1; break; case CODA_CREATE: CMD(create); DUMP_NAME(create); GET_VFID_name(create); BUILD_NAME(create); DEBUG_PRINT( "(create_name: %s)", buf ); rep->coda_create.VFid = alloc_vfid(buf); v_created(rep->coda_create.VFid) = 1; rep->coda_create.attr = req->coda_create.attr; break; case CODA_MKDIR: CMD(mkdir); DUMP_NAME(mkdir); GET_VFID_name(mkdir); BUILD_NAME(mkdir); DEBUG_PRINT( "(mkdir: %s)", buf ); if ( mc_mkdir( buf, req->coda_mkdir.attr.va_mode ) == 0) { rep->coda_mkdir.VFid = alloc_vfid(buf); rep->coda_mkdir.attr = req->coda_mkdir.attr; } else rep->oh.result = errno; break; /********************* deletions & rename ******************/ case CODA_RMDIR: CMD_NOREP(rmdir); DUMP_NAME(rmdir); GET_VFID_name(rmdir); BUILD_NAME(rmdir); DEBUG_PRINT( "(rmdir: %s)", buf ); if ( mc_rmdir( buf ) == 0) { rep->oh.result = 0; delete_name( buf ); } else rep->oh.result = errno; break; case CODA_REMOVE: CMD_NOREP(remove); DUMP_NAME(remove); GET_VFID_name(remove); BUILD_NAME(remove); DEBUG_PRINT( "(remove: %s)", buf ); if ( mc_unlink( buf ) == 0) { rep->oh.result = 0; delete_name( buf ); } else rep->oh.result = errno; break; case CODA_RENAME: CMD_NOREP(rename); { char buf2[256]; name = ((char *) req) + req->coda_rename.srcname; if (!name) { rep->oh.result = ENOENT; break; } sprintf( buf, "%s/%s", look_name(req->coda_rename.sourceFid), name ); name = ((char *) req) + req->coda_rename.destname; sprintf( buf2, "%s/%s", look_name(req->coda_rename.destFid), name ); DEBUG_PRINT( "src name=%s, dest name %s: ", buf, buf2 ); if ( mc_rename( buf, buf2 ) == 0) { rep->oh.result = 0; delete_name( buf ); } else rep->oh.result = errno; } break; default: DEBUG_PRINT("unimplemented coda call"); break; } DEBUG_PRINT( "returning %s (%ld), %d bytes\n", rep->oh.result ? "FAILURE" : "SUCCESS", rep->oh.result, size ); msg = write(cfs, rep, size); fflush(stdout); setegid (0); vfs_gid = 0; seteuid (0); vfs_uid = 0; time_last_msg = time(NULL); } /* while */ } /* Leave this in place - this is here so that code still is valid Makefile. */ #if 0 endif #endif