@@ -23,11 +23,29 @@ #define APPFS_CACHEDIR "/var/cache/appfs" #endif /* Debugging macros */ #ifdef DEBUG -#define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); } +int appfs_debug_fd = STDERR_FILENO; +#define APPFS_DEBUG(x...) { \ + char buf[8192]; \ + int bufoff = 0; \ + if (appfs_debug_fd == -1) { \ + appfs_debug_fd = open("/tmp/appfsd.log", O_WRONLY | O_APPEND | O_CREAT, 0600); \ + }; \ + bufoff = snprintf(buf, sizeof(buf), "[debug] [t=%llx] %s:%i:%s: ", (unsigned long long) pthread_self(), __FILE__, __LINE__, __func__); \ + if (bufoff < sizeof(buf)) { \ + bufoff += snprintf(buf + bufoff, sizeof(buf) - bufoff, x); \ + }; \ + if (bufoff < sizeof(buf)) { \ + bufoff += snprintf(buf + bufoff, sizeof(buf) - bufoff, "\n");\ + } \ + if (bufoff > sizeof(buf)) { \ + bufoff = sizeof(buf); \ + }; \ + write(appfs_debug_fd, buf, bufoff); \ +} #else #define APPFS_DEBUG(x...) /**/ #endif /* @@ -61,10 +79,11 @@ */ pthread_mutex_t appfs_tcl_big_global_lock = PTHREAD_MUTEX_INITIALIZER; #define appfs_call_libtcl_enter pthread_mutex_lock(&appfs_tcl_big_global_lock); #define appfs_call_libtcl_exit pthread_mutex_unlock(&appfs_tcl_big_global_lock); #else +#warning Using a Threaded Tcl interpreter may cause memory leaks #define appfs_call_libtcl_enter /**/ #define appfs_call_libtcl_exit /**/ #endif #define appfs_call_libtcl(x...) appfs_call_libtcl_enter x appfs_call_libtcl_exit @@ -379,10 +398,12 @@ if (interp == NULL) { interp = appfs_create_TclInterp(NULL); if (interp == NULL) { + APPFS_DEBUG("Create interp failed, returningin failure."); + return(NULL); } pthread_ret = pthread_setspecific(interpKey, interp); if (pthread_ret != 0) { @@ -407,10 +428,12 @@ va_list argp; int retval; int i; if (interp == NULL) { + APPFS_DEBUG("Invalid interpreter passed in, returning in failure."); + return(TCL_ERROR); } objv = (void *) ckalloc(sizeof(*objv) * objc); @@ -476,10 +499,12 @@ ctx = fuse_get_context(); if (ctx == NULL) { /* Unable to lookup user for some reason */ /* Return an unprivileged user ID */ + APPFS_DEBUG("Unable to lookup user for some reason, returninng user ID of 1"); + return(1); } return(ctx->uid); } @@ -498,10 +523,12 @@ ctx = fuse_get_context(); if (ctx == NULL) { /* Unable to lookup user for some reason */ /* Return an unprivileged user ID */ + APPFS_DEBUG("Unable to lookup group for some reason, returninng group ID of 1"); + return(1); } return(ctx->gid); } @@ -672,10 +699,11 @@ if (pthread_ret != 0) { APPFS_DEBUG("Unable to unlock path_info cache mutex !"); return; } + return; } static void appfs_get_path_info_cache_rm(const char *path, uid_t uid) { unsigned int hash_idx; @@ -775,22 +803,28 @@ fsuid = appfs_get_fsuid(); cache_ret = appfs_get_path_info_cache_get(path, fsuid, pathinfo); if (cache_ret == 0) { if (pathinfo->type == APPFS_PATHTYPE_DOES_NOT_EXIST) { + APPFS_DEBUG("Returning from cache: does not exist \"%s\"", path); + return(-ENOENT); } if (pathinfo->type == APPFS_PATHTYPE_INVALID) { + APPFS_DEBUG("Returning from cache: invalid object \"%s\"", path); + return(-EIO); } return(0); } interp = appfs_TclInterp(); if (interp == NULL) { + APPFS_DEBUG("error: Unable to get an interpreter"); + return(-EIO); } appfs_call_libtcl(Tcl_Preserve(interp);) @@ -844,10 +878,12 @@ return(-EIO); } if (attr_value == NULL) { + APPFS_DEBUG("error: Unable to get type for \"%s\" from Tcl", path); + appfs_call_libtcl(Tcl_Release(interp);) return(-EIO); } @@ -937,10 +973,12 @@ Tcl_Release(interp); ) if (retval == 0) { appfs_get_path_info_cache_add(path, fsuid, pathinfo); + } else { + APPFS_DEBUG("error: Invalid type for \"%s\" from Tcl", path); } return(retval); } @@ -1093,10 +1131,16 @@ pathinfo.type = APPFS_PATHTYPE_INVALID; retval = appfs_get_path_info(path, &pathinfo); if (retval != 0) { + if (retval == -ENOENT) { + APPFS_DEBUG("get_path_info returned ENOENT, returning it as well."); + } else { + APPFS_DEBUG("error: get_path_info failed"); + } + return(retval); } memset(stbuf, 0, sizeof(struct stat)); @@ -1163,10 +1207,12 @@ APPFS_DEBUG("Enter (path = %s, ...)", path); interp = appfs_TclInterp(); if (interp == NULL) { + APPFS_DEBUG("error: Unable to get an interpreter"); + return(0); } appfs_call_libtcl(Tcl_Preserve(interp);) @@ -1222,10 +1268,12 @@ gpi_ret = appfs_get_path_info(path, &pathinfo); if ((fi->flags & (O_WRONLY|O_CREAT)) == (O_CREAT|O_WRONLY)) { /* The file will be created if it does not exist */ if (gpi_ret != 0 && gpi_ret != -ENOENT) { + APPFS_DEBUG("error: get_path_info failed"); + return(gpi_ret); } mode = "create"; @@ -1235,10 +1283,12 @@ */ appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1); } else { /* The file must already exist */ if (gpi_ret != 0) { + APPFS_DEBUG("error: get_path_info failed"); + return(gpi_ret); } mode = ""; @@ -1246,15 +1296,19 @@ mode = "write"; } } if (pathinfo.type == APPFS_PATHTYPE_DIRECTORY) { + APPFS_DEBUG("error: Asked to open a directory."); + return(-EISDIR); } interp = appfs_TclInterp(); if (interp == NULL) { + APPFS_DEBUG("error: Unable to get an interpreter"); + return(-EIO); } appfs_call_libtcl(Tcl_Preserve(interp);) @@ -1275,19 +1329,23 @@ ) appfs_call_libtcl(Tcl_Release(interp);) if (real_path == NULL) { + APPFS_DEBUG("error: real_path was NULL.") + return(-EIO); } APPFS_DEBUG("Translated request to open %s to opening %s (mode = \"%s\")", path, real_path, mode); fh = open(real_path, fi->flags, 0600); if (fh < 0) { - return(-EIO); + APPFS_DEBUG("error: open failed"); + + return(errno * -1); } fi->fh = fh; return(0); @@ -1298,48 +1356,90 @@ appfs_get_path_info_cache_rm(path, appfs_get_fsuid()); close_ret = close(fi->fh); if (close_ret != 0) { - return(-EIO); + APPFS_DEBUG("error: close failed"); + + return(errno * -1); } return(0); } static int appfs_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - off_t lseek_ret; ssize_t read_ret; + int retval; - APPFS_DEBUG("Enter (path = %s, ...)", path); + APPFS_DEBUG("Enter (path = %s, buf, %lli, %lli, fd=%lli)", path, (long long) size, (long long) offset, (long long) fi->fh); - lseek_ret = lseek(fi->fh, offset, SEEK_SET); - if (lseek_ret != offset) { - return(-EIO); + retval = 0; + + while (size != 0) { + read_ret = pread(fi->fh, buf, size, offset); + + if (read_ret < 0) { + APPFS_DEBUG("error: read failed"); + + return(errno * -1); + } + + if (read_ret == 0) { + break; + } + + size -= read_ret; + buf += read_ret; + offset += read_ret; + retval += read_ret; + } + + if (size != 0) { + APPFS_DEBUG("error: incomplete read (this is an error because FUSE will request the exact length of the file)"); + + return(0); } - read_ret = read(fi->fh, buf, size); + APPFS_DEBUG("Returning: %i", retval); - return(read_ret); + return(retval); } static int appfs_fuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - off_t lseek_ret; ssize_t write_ret; + int retval; APPFS_DEBUG("Enter (path = %s, ...)", path); appfs_get_path_info_cache_rm(path, appfs_get_fsuid()); - lseek_ret = lseek(fi->fh, offset, SEEK_SET); - if (lseek_ret != offset) { - return(-EIO); + retval = 0; + + while (size != 0) { + write_ret = pwrite(fi->fh, buf, size, offset); + + if (write_ret < 0) { + APPFS_DEBUG("error: write failed"); + + return(errno * -1); + } + + if (write_ret == 0) { + break; + } + + size -= write_ret; + buf += write_ret; + offset += write_ret; + retval += write_ret; + } + + if (size != 0) { + APPFS_DEBUG("error: incomplete write"); } - write_ret = write(fi->fh, buf, size); - - return(write_ret); + return(retval); } static int appfs_fuse_mknod(const char *path, mode_t mode, dev_t device) { char *real_path; int mknod_ret; @@ -1766,11 +1866,11 @@ } /* * Terminate a thread */ -static void appfs_terminate_interp(void *_interp) { +static void appfs_terminate_interp_and_thread(void *_interp) { Tcl_Interp *interp; APPFS_DEBUG("Called: _interp = %p", _interp); if (_interp == NULL) { @@ -1784,10 +1884,12 @@ APPFS_DEBUG("Terminating interpreter due to thread termination"); appfs_call_libtcl( Tcl_DeleteInterp(interp); ) + + Tcl_FinalizeThread(); return; } /* @@ -1868,11 +1970,11 @@ /* * Create a thread-specific-data (TSD) key for each thread to refer * to its own Tcl interpreter. Tcl interpreters must be unique per * thread and new threads are dynamically created by FUSE. */ - pthread_ret = pthread_key_create(&interpKey, appfs_terminate_interp); + pthread_ret = pthread_key_create(&interpKey, appfs_terminate_interp_and_thread); if (pthread_ret != 0) { fprintf(stderr, "Unable to create TSD key for Tcl. Aborting.\n"); return(1); }