| From 2a755d2ce77147c6319d18e7b155a7278398f35b Mon Sep 17 00:00:00 2001 |
| From: Hans Beckerus <hans.beckerus at gmail.com> |
| Date: Mon, 9 Aug 2021 21:08:18 +0200 |
| Subject: [PATCH] Make sure reader thread is terminated properly |
| |
| Under some conditions the reader thread responsible for carrying data |
| from the child process to the I/O buffer may fail to terminate at close |
| and file system thread gets blocked waiting for join to complete. |
| Terminate the reader thread using a control message rather then using |
| the brute force call to pthread_cancel(). |
| |
| Additional updates made in this patch to increase robustness include: |
| - use of unbuffered- rather than buffered read on pipe |
| - increase size of pipe on systems that allows it |
| - do not try to promote rdlock to wrlock due to the potential race |
| condition |
| - remove some unnecessary initializations |
| - improve code in I/O buffer to make it more readable |
| - set default debug level to 0 (in case of --enable-debug) |
| - use a per session mutex in I/O buffer rather than a global one |
| - perform dry-run extraction outside of child process and before it |
| is spawned |
| |
| Note that the majority of the changes done as part of this patch only |
| affects extraction of compressed (-m1 and above) and/or encrypted archives. |
| |
| Resolves-issue: #165 |
| Signed-off-by: Hans Beckerus <hans.beckerus at gmail.com> |
| --- |
| configure.ac | 4 +- |
| src/debug.h | 4 +- |
| src/filecache.h | 4 +- |
| src/iobuffer.c | 111 ++++++++++------ |
| src/iobuffer.h | 25 ++-- |
| src/optdb.c | 2 +- |
| src/rar2fs.c | 337 +++++++++++++++++++++++------------------------- |
| 7 files changed, 257 insertions(+), 230 deletions(-) |
| |
| diff --git a/configure.ac b/configure.ac |
| index bee9255..805be18 100644 |
| --- a/configure.ac |
| +++ b/configure.ac |
| @@ -264,11 +264,11 @@ AC_SUBST(UNRAR_LDFLAGS) |
| enableval= |
| AC_ARG_ENABLE([debug], |
| [AS_HELP_STRING([--enable-debug[[=LEVEL]]], |
| - [enable debug mode [LEVEL=4]])], |
| + [enable debug mode [LEVEL=0]])], |
| [], []) |
| if test x"$enableval" != x"no"; then |
| if test x"$enableval" = x"yes"; then |
| - debug_level=4 |
| + debug_level=0 |
| else |
| debug_level=$enableval |
| fi |
| diff --git a/src/debug.h b/src/debug.h |
| index 30e5ae1..ad9200f 100644 |
| --- a/src/debug.h |
| +++ b/src/debug.h |
| @@ -36,7 +36,7 @@ |
| #define DEBUG_ 5 |
| #endif |
| |
| -#if defined ( DEBUG_ ) |
| +#if DEBUG_ > 0 |
| #if DEBUG_ > 2 |
| #define ELVL_ 2 |
| #else |
| @@ -51,7 +51,7 @@ |
| #define ENTER_(...) |
| #endif |
| |
| -#ifdef DEBUG_ |
| +#if DEBUG_ > 0 |
| #define printd(l, fmt, ...) \ |
| do{ \ |
| if (l <= DEBUG_) \ |
| diff --git a/src/filecache.h b/src/filecache.h |
| index d413496..b0026b0 100644 |
| --- a/src/filecache.h |
| +++ b/src/filecache.h |
| @@ -61,7 +61,7 @@ struct filecache_entry { |
| unsigned int vsize_resolved:1; |
| unsigned int :21; |
| unsigned int unresolved:1; |
| - unsigned int :1; |
| + unsigned int dry_run_done:1; |
| unsigned int check_atime:1; |
| unsigned int direct_io:1; |
| unsigned int avi_tested:1; |
| @@ -71,7 +71,7 @@ struct filecache_entry { |
| unsigned int avi_tested:1; |
| unsigned int direct_io:1; |
| unsigned int check_atime:1; |
| - unsigned int :1; |
| + unsigned int dry_run_done:1; |
| unsigned int unresolved:1; |
| unsigned int :21; |
| unsigned int vsize_resolved:1; |
| diff --git a/src/iobuffer.c b/src/iobuffer.c |
| index d81f99d..b191f94 100644 |
| --- a/src/iobuffer.c |
| +++ b/src/iobuffer.c |
| @@ -36,35 +36,31 @@ |
| size_t iob_hist_sz = 0; |
| size_t iob_sz = 0; |
| |
| -static pthread_mutex_t io_mutex = PTHREAD_MUTEX_INITIALIZER; |
| - |
| -#define SPACE_LEFT(ri, wi) (IOB_SZ - SPACE_USED((ri), (wi))) |
| +/* -1 to avoid wi = ri */ |
| +#define SPACE_LEFT(ri, wi) ((IOB_SZ - SPACE_USED((ri), (wi))) - IOB_HIST_SZ - 1) |
| #define SPACE_USED(ri, wi) (((wi) - (ri)) & (IOB_SZ-1)) |
| |
| /*! |
| ***************************************************************************** |
| * |
| ****************************************************************************/ |
| -size_t iob_write(struct iob *dest, FILE *fp, int hist) |
| +size_t iob_write(struct iob *iob, int fd) |
| { |
| unsigned tot = 0; |
| - pthread_mutex_lock(&io_mutex); |
| - unsigned int lwi = dest->wi; /* read once */ |
| - unsigned int lri = dest->ri; |
| - pthread_mutex_unlock(&io_mutex); |
| - size_t left = SPACE_LEFT(lri, lwi) - 1; /* -1 to avoid wi = ri */ |
| - if (IOB_HIST_SZ && hist == IOB_SAVE_HIST) { |
| - left = left > IOB_HIST_SZ ? left - IOB_HIST_SZ : 0; |
| - if (!left) { |
| - return 0; /* quick exit */ |
| - } |
| - } |
| + pthread_mutex_lock(&iob->lock); |
| + unsigned int lwi = iob->wi; /* read once */ |
| + unsigned int lri = iob->ri; |
| + pthread_mutex_unlock(&iob->lock); |
| + size_t left = SPACE_LEFT(lri, lwi); |
| + if (!left) |
| + return 0; /* quick exit */ |
| + |
| unsigned int chunk = IOB_SZ - lwi; /* assume one large chunk */ |
| chunk = chunk < left ? chunk : left; /* reconsider assumption */ |
| while (left > 0) { |
| - size_t n = fread(dest->data_p + lwi, 1, chunk, fp); |
| + ssize_t n = read(fd, iob->data_p + lwi, chunk); |
| if (n != chunk) { |
| - if (ferror(fp)) { |
| + if (n < 0) { |
| perror("read"); |
| break; |
| } |
| @@ -77,12 +73,12 @@ size_t iob_write(struct iob *dest, FILE *fp, int hist) |
| chunk -= n; |
| chunk = !chunk ? left : chunk; |
| } |
| - pthread_mutex_lock(&io_mutex); |
| - dest->wi = lwi; |
| - dest->used = SPACE_USED(dest->ri, lwi); /* dest->ri might have changed */ |
| - pthread_mutex_unlock(&io_mutex); |
| + pthread_mutex_lock(&iob->lock); |
| + iob->wi = lwi; |
| + iob->used = SPACE_USED(iob->ri, lwi); /* iob->ri might have changed */ |
| + pthread_mutex_unlock(&iob->lock); |
| MB(); |
| - dest->offset += tot; |
| + iob->offset += tot; |
| |
| return tot; |
| } |
| @@ -91,13 +87,13 @@ size_t iob_write(struct iob *dest, FILE *fp, int hist) |
| ***************************************************************************** |
| * |
| ****************************************************************************/ |
| -size_t iob_read(char *dest, struct iob *src, size_t size, size_t off) |
| +size_t iob_read(char *dest, struct iob *iob, size_t size, size_t off) |
| { |
| size_t tot = 0; |
| - pthread_mutex_lock(&io_mutex); |
| - unsigned int lri = src->ri; /* read once */ |
| - size_t used = src->used; |
| - pthread_mutex_unlock(&io_mutex); |
| + pthread_mutex_lock(&iob->lock); |
| + unsigned int lri = iob->ri; /* read once */ |
| + size_t used = iob->used; |
| + pthread_mutex_unlock(&iob->lock); |
| if (off) { |
| /* consume offset */ |
| off = off < used ? off : used; |
| @@ -108,17 +104,17 @@ size_t iob_read(char *dest, struct iob *src, size_t size, size_t off) |
| unsigned int chunk = IOB_SZ - lri; /* assume one large chunk */ |
| chunk = chunk < size ? chunk : size; /* reconsider assumption */ |
| while (size) { |
| - memcpy(dest, src->data_p + lri, chunk); |
| + memcpy(dest, iob->data_p + lri, chunk); |
| lri = (lri + chunk) & (IOB_SZ - 1); |
| tot += chunk; |
| size -= chunk; |
| dest += chunk; |
| chunk = size; |
| } |
| - pthread_mutex_lock(&io_mutex); |
| - src->ri = lri; |
| - src->used -= tot; /* src->used might have changed */ |
| - pthread_mutex_unlock(&io_mutex); |
| + pthread_mutex_lock(&iob->lock); |
| + iob->ri = lri; |
| + iob->used -= tot; /* iob->used might have changed */ |
| + pthread_mutex_unlock(&iob->lock); |
| |
| return tot; |
| } |
| @@ -127,21 +123,21 @@ size_t iob_read(char *dest, struct iob *src, size_t size, size_t off) |
| ***************************************************************************** |
| * |
| ****************************************************************************/ |
| -size_t iob_copy(char *dest, struct iob *src, size_t size, size_t pos) |
| +size_t iob_copy(char *dest, struct iob *iob, size_t size, size_t pos) |
| { |
| size_t tot = 0; |
| unsigned int chunk = IOB_SZ - pos; /* assume one large chunk */ |
| chunk = chunk < size ? chunk : size; /* reconsider assumption */ |
| - pthread_mutex_lock(&io_mutex); |
| + pthread_mutex_lock(&iob->lock); |
| while (size) { |
| - memcpy(dest, src->data_p + pos, chunk); |
| + memcpy(dest, iob->data_p + pos, chunk); |
| pos = (pos + chunk) & (IOB_SZ - 1); |
| tot += chunk; |
| size -= chunk; |
| dest += chunk; |
| chunk = size; |
| } |
| - pthread_mutex_unlock(&io_mutex); |
| + pthread_mutex_unlock(&iob->lock); |
| return tot; |
| } |
| |
| @@ -165,3 +161,46 @@ void iob_destroy() |
| { |
| } |
| |
| +/*! |
| + ***************************************************************************** |
| + * |
| + ****************************************************************************/ |
| +struct iob *iob_alloc(size_t size) |
| +{ |
| + struct iob *iob; |
| + |
| + iob = calloc(1, size); |
| + if (!iob) |
| + return NULL; |
| + |
| + pthread_mutex_init(&iob->lock, NULL); |
| + pthread_mutex_init(&iob->io_lock, NULL); |
| + |
| + return iob; |
| +} |
| + |
| +/*! |
| + ***************************************************************************** |
| + * |
| + ****************************************************************************/ |
| +void iob_free(struct iob *iob) |
| +{ |
| + if (iob) { |
| + pthread_mutex_destroy(&iob->lock); |
| + pthread_mutex_destroy(&iob->io_lock); |
| + free(iob); |
| + } |
| +} |
| + |
| +/*! |
| + ***************************************************************************** |
| + * |
| + ****************************************************************************/ |
| +int iob_full(struct iob *iob) |
| +{ |
| + int res; |
| + pthread_mutex_lock(&iob->lock); |
| + res = !SPACE_LEFT(iob->ri, iob->wi); |
| + pthread_mutex_unlock(&iob->lock); |
| + return res; |
| +} |
| diff --git a/src/iobuffer.h b/src/iobuffer.h |
| index cdcd3ab..2558361 100644 |
| --- a/src/iobuffer.h |
| +++ b/src/iobuffer.h |
| @@ -35,17 +35,12 @@ |
| #define IOB_SZ_DEFAULT (4 * 1024 * 1024) |
| #ifdef USE_STATIC_IOB_ |
| #define IOB_SZ IOB_SZ_DEFAULT |
| -#define IOB_HIST_SZ (IOB_SZ/2) |
| +#define IOB_HIST_SZ (IOB_SZ / 2) |
| #else |
| #define IOB_SZ (iob_sz) |
| #define IOB_HIST_SZ (iob_hist_sz) |
| #endif |
| |
| -#define IOB_NO_HIST 0 |
| -#define IOB_SAVE_HIST 1 |
| - |
| -#define IOB_RST(b) (memset((b), 0, sizeof(struct iob) + IOB_SZ)) |
| - |
| struct idx_info { |
| int fd; |
| int mmap; |
| @@ -53,22 +48,25 @@ struct idx_info { |
| }; |
| |
| struct iob { |
| + int fd; |
| struct idx_info idx; |
| off_t offset; |
| volatile size_t ri; |
| volatile size_t wi; |
| size_t used; |
| + pthread_mutex_t lock; |
| + pthread_mutex_t io_lock; |
| uint8_t data_p[]; |
| }; |
| |
| size_t |
| -iob_write(struct iob *dest, FILE *fp, int hist); |
| +iob_write(struct iob *iob, int fd); |
| |
| size_t |
| -iob_read(char *dest, struct iob *src, size_t size, size_t off); |
| +iob_read(char *dest, struct iob *iob, size_t size, size_t off); |
| |
| size_t |
| -iob_copy(char *dest, struct iob *src, size_t size, size_t pos); |
| +iob_copy(char *dest, struct iob *iob, size_t size, size_t pos); |
| |
| extern size_t iob_hist_sz; |
| extern size_t iob_sz; |
| @@ -79,5 +77,14 @@ iob_init(); |
| void |
| iob_destroy(); |
| |
| +struct iob * |
| +iob_alloc(size_t size); |
| + |
| +void |
| +iob_free(struct iob *iob); |
| + |
| +int |
| +iob_full(struct iob *iob); |
| + |
| #endif |
| |
| diff --git a/src/optdb.c b/src/optdb.c |
| index f86f5d4..51e63a6 100644 |
| --- a/src/optdb.c |
| +++ b/src/optdb.c |
| @@ -174,7 +174,7 @@ int optdb_save(int opt, const char *s) |
| if (s1) |
| free(s1); |
| |
| -#ifdef DEBUG_ |
| +#if DEBUG_ > 0 |
| { |
| int i; |
| printd(5, "option %d : ", opt); |
| diff --git a/src/rar2fs.c b/src/rar2fs.c |
| index 1d97d8a..3703206 100644 |
| --- a/src/rar2fs.c |
| +++ b/src/rar2fs.c |
| @@ -78,10 +78,10 @@ |
| #define MOUNT_ARCHIVE 1 |
| |
| #define RD_IDLE 0 |
| -#define RD_WAKEUP 1 |
| +#define RD_TERM 1 |
| #define RD_SYNC_NOREAD 2 |
| #define RD_SYNC_READ 3 |
| -#define RD_READ_ASYNC 4 |
| +#define RD_ASYNC_READ 4 |
| |
| /*#define DEBUG_READ*/ |
| |
| @@ -92,6 +92,7 @@ struct volume_handle { |
| |
| struct io_context { |
| FILE* fp; |
| + int pfd[2]; |
| off_t pos; |
| struct iob *buf; |
| pid_t pid; |
| @@ -127,7 +128,6 @@ struct io_handle { |
| char *path; /* type = all */ |
| }; |
| |
| -#define FH_ZERO(fh) ((fh) = 0) |
| #define FH_ISSET(fh) (fh) |
| #define FH_SETCONTEXT(fh, v) (FH_TOIO(fh)->u.context = (v)) |
| #define FH_SETFD(fh, v) (FH_TOIO(fh)->u.fd = (v)) |
| @@ -393,8 +393,10 @@ static int __wait_thread(struct io_context *op) |
| static int __wake_thread(struct io_context *op, int req) |
| { |
| pthread_mutex_lock(&op->rd_req_mutex); |
| - while (op->rd_req) /* sync */ |
| - pthread_cond_wait(&op->rd_req_cond, &op->rd_req_mutex); |
| + if (req != RD_ASYNC_READ) { |
| + while (op->rd_req) /* sync */ |
| + pthread_cond_wait(&op->rd_req_cond, &op->rd_req_mutex); |
| + } |
| op->rd_req = req; |
| pthread_cond_signal(&op->rd_req_cond); |
| pthread_mutex_unlock(&op->rd_req_mutex); |
| @@ -637,8 +639,7 @@ static struct filecache_entry *path_lookup_miss(const char *path, struct stat *s |
| |
| /*! |
| ***************************************************************************** |
| - * This function must always be called with an aquired rdlock but never |
| - * a wrlock. It is however possible that the rdlock is promoted to a wrlock. |
| + * This function must always be called with an aquired wrlock. |
| ****************************************************************************/ |
| static struct filecache_entry *path_lookup(const char *path, struct stat *stbuf) |
| { |
| @@ -652,8 +653,6 @@ static struct filecache_entry *path_lookup(const char *path, struct stat *stbuf) |
| e_p = path_lookup_miss(path, stbuf); |
| if (!e_p) { |
| if (e2_p && e2_p->flags.unresolved) { |
| - pthread_rwlock_unlock(&file_access_lock); |
| - pthread_rwlock_wrlock(&file_access_lock); |
| e2_p->flags.unresolved = 0; |
| if (stbuf) |
| memcpy(stbuf, &e2_p->stat, sizeof(struct stat)); |
| @@ -701,33 +700,40 @@ static int __stop_child(pid_t pid) |
| ***************************************************************************** |
| * |
| ****************************************************************************/ |
| -static FILE *popen_(const struct filecache_entry *entry_p, pid_t *cpid) |
| +static int popen_(struct io_context *op, pid_t *cpid, int *pfd) |
| { |
| - int fd = -1; |
| - int pfd[2] = {-1,}; |
| - |
| pid_t pid; |
| + int ret; |
| + struct filecache_entry *entry_p = op->entry_p; |
| + |
| + pfd[0] = -1; |
| + pfd[1] = -1; |
| + |
| + /* For folder mounts we need to perform an additional dummy |
| + * extraction attempt to avoid feeding the file descriptor |
| + * with garbage data in case of wrong password or CRC errors. */ |
| + if (!entry_p->flags.dry_run_done && mount_type == MOUNT_FOLDER) { |
| + ret = extract_rar(entry_p->rar_p, entry_p->file_p, NULL); |
| + if (ret && ret != ERAR_UNKNOWN) |
| + goto error; |
| + entry_p->flags.dry_run_done = 1; |
| + } |
| + |
| if (pipe(pfd) == -1) { |
| perror("pipe"); |
| goto error; |
| } |
| +#ifdef F_SETPIPE_SZ |
| + /* Increase pipe capacity to some common limit when requested from an |
| + * unprivileged process. */ |
| + fcntl(pfd[0], F_SETPIPE_SZ, 1000000); |
| +#endif |
| |
| pid = fork(); |
| if (pid == 0) { |
| - int ret = 0; |
| setpgid(getpid(), 0); |
| close(pfd[0]); /* Close unused read end */ |
| - /* For folder mounts we need to perform an additional dummy |
| - * extraction attempt to avoid feeding the file descriptor |
| - * with garbage data in case of wrong password or CRC errors. */ |
| - if (mount_type == MOUNT_FOLDER) |
| - ret = extract_rar(entry_p->rar_p, |
| - entry_p->file_p, |
| - NULL); |
| - if (!ret || ret == ERAR_UNKNOWN) |
| - ret = extract_rar(entry_p->rar_p, |
| - entry_p->file_p, |
| - (void *)(uintptr_t)pfd[1]); |
| + ret = extract_rar(entry_p->rar_p, entry_p->file_p, op); |
| close(pfd[1]); |
| _exit(ret); |
| } else if (pid < 0) { |
| @@ -738,27 +744,28 @@ static FILE *popen_(const struct filecache_entry *entry_p, pid_t *cpid) |
| /* This is the parent process. */ |
| close(pfd[1]); /* Close unused write end */ |
| *cpid = pid; |
| - return fdopen(pfd[0], "r"); |
| + return pfd[0]; |
| |
| error: |
| - if (fd >= 0) |
| - close(fd); |
| if (pfd[0] >= 0) |
| close(pfd[0]); |
| if (pfd[1] >= 0) |
| close(pfd[1]); |
| |
| - return NULL; |
| + return -1; |
| } |
| |
| /*! |
| ***************************************************************************** |
| * |
| ****************************************************************************/ |
| -static int pclose_(FILE *fp, pid_t pid) |
| +static int pclose_(struct io_context *op) |
| { |
| - fclose(fp); |
| - return __stop_child(pid); |
| + if (op->pfd[0] != -1) { |
| + close(op->pfd[0]); |
| + op->pfd[0] = -1; |
| + } |
| + return __stop_child(op->pid); |
| } |
| |
| /* Size of file in first volume number in which it exists */ |
| @@ -1508,10 +1515,11 @@ static int lread_rar(char *buf, size_t size, off_t offset, |
| if (sync_thread_read(op)) |
| return -EIO; |
| /* If there is still no data assume something went wrong. |
| - * Most likely CRC errors or an invalid password in the case |
| - * of encrypted aechives. |
| + * I/O buffer might simply be full and cannot receive more |
| + * data or otherwise most likely CRC errors or an invalid |
| + * password in the case of encrypted aechives. |
| */ |
| - if (op->buf->offset == offset_saved) |
| + if (op->buf->offset == offset_saved && !iob_full(op->buf)) |
| return -EIO; |
| } |
| if ((off_t)(offset + size) > op->buf->offset) { |
| @@ -1550,32 +1558,29 @@ static int lread_rar(char *buf, size_t size, off_t offset, |
| /* Take control of reader thread */ |
| if (sync_thread_noread(op)) |
| return -EIO; |
| - if (!feof(op->fp) && offset > op->buf->offset) { |
| + if (offset > op->buf->offset) { |
| /* consume buffer */ |
| op->pos += op->buf->used; |
| op->buf->ri = op->buf->wi; |
| op->buf->used = 0; |
| - (void)iob_write(op->buf, op->fp, IOB_SAVE_HIST); |
| + (void)iob_write(op->buf, op->pfd[0]); |
| sched_yield(); |
| } |
| |
| - if (!feof(op->fp)) { |
| - op->buf->ri = offset & (IOB_SZ - 1); |
| - op->buf->used -= (offset - op->pos); |
| - op->pos = offset; |
| + op->buf->ri = offset & (IOB_SZ - 1); |
| + op->buf->used -= (offset - op->pos); |
| + op->pos = offset; |
| |
| - /* Pull in rest of data if needed */ |
| - if ((size_t)(op->buf->offset - offset) < size) |
| - (void)iob_write(op->buf, op->fp, |
| - IOB_SAVE_HIST); |
| - } |
| + /* Pull in rest of data if needed */ |
| + if ((size_t)(op->buf->offset - offset) < size) |
| + (void)iob_write(op->buf, op->pfd[0]); |
| } |
| |
| if (size) { |
| int off = offset - op->pos; |
| n += iob_read(buf, op->buf, size, off); |
| op->pos += (off + size); |
| - if (__wake_thread(op, RD_READ_ASYNC)) |
| + if (__wake_thread(op, RD_ASYNC_READ)) |
| return -EIO; |
| } |
| |
| @@ -1619,7 +1624,6 @@ static int lrelease(struct fuse_file_info *fi) |
| close(FH_TOFD(fi->fh)); |
| printd(3, "(%05d) %s [0x%-16" PRIx64 "]\n", getpid(), "FREE", fi->fh); |
| free(FH_TOIO(fi->fh)); |
| - FH_ZERO(fi->fh); |
| return 0; |
| } |
| |
| @@ -2009,12 +2013,14 @@ static int CALLBACK extract_callback(UINT msg, LPARAM UserData, |
| LPARAM P1, LPARAM P2) |
| { |
| struct extract_cb_arg *cb_arg = (struct extract_cb_arg *)(UserData); |
| + struct io_context *op = cb_arg->arg; |
| + int ret; |
| |
| if (msg == UCM_PROCESSDATA) { |
| /* Handle the special case when asking for a quick "dry run" |
| * to test archive integrity. If all is well this will result |
| * in an ERAR_UNKNOWN error. */ |
| - if (!cb_arg->arg) { |
| + if (!op) { |
| if (!cb_arg->dry_run) { |
| cb_arg->dry_run = 1; |
| return 1; |
| @@ -2026,7 +2032,8 @@ static int CALLBACK extract_callback(UINT msg, LPARAM UserData, |
| * written after return from write() since the pipe is not |
| * opened using the O_NONBLOCK flag. |
| */ |
| - if (write((LPARAM)cb_arg->arg, (void *)P1, P2) == -1) { |
| + ret = write(op->pfd[1], (void *)P1, P2); |
| + if (ret == -1) { |
| /* |
| * Do not treat EPIPE as an error. It is the normal |
| * case when the process is terminted, ie. the pipe is |
| @@ -2094,10 +2101,8 @@ static int extract_rar(char *arch, const char *file, void *arg) |
| } |
| |
| extract_error: |
| - |
| if (hdl) |
| RARCloseArchive(hdl); |
| - |
| return ret; |
| } |
| |
| @@ -3196,7 +3201,7 @@ static int rar2_getattr(const char *path, struct stat *stbuf) |
| |
| struct filecache_entry *entry_p; |
| |
| - pthread_rwlock_rdlock(&file_access_lock); |
| + pthread_rwlock_wrlock(&file_access_lock); |
| entry_p = path_lookup(path, stbuf); |
| if (entry_p) { |
| if (entry_p != LOOP_FS_ENTRY) { |
| @@ -3226,7 +3231,7 @@ static int rar2_getattr(const char *path, struct stat *stbuf) |
| } |
| free(tmp); |
| |
| - pthread_rwlock_rdlock(&file_access_lock); |
| + pthread_rwlock_wrlock(&file_access_lock); |
| entry_p = path_lookup(path, stbuf); |
| if (entry_p) { |
| pthread_rwlock_unlock(&file_access_lock); |
| @@ -3274,7 +3279,7 @@ static int rar2_getattr2(const char *path, struct stat *stbuf) |
| |
| int res; |
| |
| - pthread_rwlock_rdlock(&file_access_lock); |
| + pthread_rwlock_wrlock(&file_access_lock); |
| if (path_lookup(path, stbuf)) { |
| pthread_rwlock_unlock(&file_access_lock); |
| dump_stat(stbuf); |
| @@ -3292,7 +3297,7 @@ static int rar2_getattr2(const char *path, struct stat *stbuf) |
| if (res) |
| return res; |
| |
| - pthread_rwlock_rdlock(&file_access_lock); |
| + pthread_rwlock_wrlock(&file_access_lock); |
| struct filecache_entry *entry_p = path_lookup(path, stbuf); |
| if (entry_p) { |
| pthread_rwlock_unlock(&file_access_lock); |
| @@ -3639,7 +3644,6 @@ static int rar2_releasedir2(const char *path, struct fuse_file_info *fi) |
| return -EIO; |
| free(FH_TOPATH(fi->fh)); |
| free(FH_TOIO(fi->fh)); |
| - FH_ZERO(fi->fh); |
| return 0; |
| } |
| |
| @@ -3662,7 +3666,6 @@ static int rar2_releasedir(const char *path, struct fuse_file_info *fi) |
| closedir(dp); |
| free(FH_TOPATH(fi->fh)); |
| free(FH_TOIO(fi->fh)); |
| - FH_ZERO(fi->fh); |
| return 0; |
| } |
| |
| @@ -3692,23 +3695,23 @@ static void *reader_task(void *arg) |
| } |
| goto restart; |
| } |
| - continue; |
| } |
| req = op->rd_req; |
| pthread_mutex_unlock(&op->rd_req_mutex); |
| |
| - printd(4, "Reader thread wakeup (fp:%p)\n", op->fp); |
| - if (req > RD_SYNC_NOREAD && !feof(op->fp)) |
| - (void)iob_write(op->buf, op->fp, IOB_SAVE_HIST); |
| - |
| + if (req == RD_TERM) |
| + goto out; |
| + printd(4, "Reader thread wakeup (fd:%d)\n", op->pfd[0]); |
| + if (req > RD_SYNC_NOREAD) |
| + (void)iob_write(op->buf, op->pfd[0]); |
| pthread_mutex_lock(&op->rd_req_mutex); |
| op->rd_req = RD_IDLE; |
| - pthread_cond_broadcast(&op->rd_req_cond); /* sync */ |
| + pthread_cond_signal(&op->rd_req_cond); /* sync */ |
| pthread_mutex_unlock(&op->rd_req_mutex); |
| } |
| |
| out: |
| - printd(4, "Reader thread stopped (fp:%p)\n", op->fp); |
| + printd(4, "Reader thread stopped (fd:%d)\n", op->pfd[0]); |
| return NULL; |
| } |
| |
| @@ -3914,7 +3917,7 @@ static int rar2_open(const char *path, struct fuse_file_info *fi) |
| fi->flags &= ~(O_CREAT | O_EXCL); |
| #endif |
| errno = 0; |
| - pthread_rwlock_rdlock(&file_access_lock); |
| + pthread_rwlock_wrlock(&file_access_lock); |
| entry_p = path_lookup(path, NULL); |
| |
| if (entry_p == NULL) { |
| @@ -3986,7 +3989,7 @@ static int rar2_open(const char *path, struct fuse_file_info *fi) |
| if (entry_p->flags.raw) { |
| fp = fopen(entry_p->rar_p, "r"); |
| if (fp != NULL) { |
| - io = malloc(sizeof(struct io_handle)); |
| + io = calloc(1, sizeof(struct io_handle)); |
| op = calloc(1, sizeof(struct io_context)); |
| if (!op || !io) |
| goto open_error; |
| @@ -3997,11 +4000,6 @@ static int rar2_open(const char *path, struct fuse_file_info *fi) |
| printd(3, "(%05d) %-8s%s [%-16p]\n", getpid(), "ALLOC", path, FH_TOCONTEXT(fi->fh)); |
| pthread_mutex_init(&op->raw_read_mutex, NULL); |
| op->fp = fp; |
| - op->pid = 0; |
| - op->seq = 0; |
| - op->buf = NULL; |
| - op->entry_p = NULL; |
| - op->pos = 0; |
| op->vno = -1; /* force a miss 1:st time */ |
| |
| /* |
| @@ -4012,7 +4010,7 @@ static int rar2_open(const char *path, struct fuse_file_info *fi) |
| * Since the file contents will never change this should save |
| * us from some user space calls! |
| */ |
| -#if 0 /* disable for now (issue #66) */ |
| +#if 1 /* disable for now (issue #66) */ |
| fi->keep_cache = 1; |
| #endif |
| |
| @@ -4029,114 +4027,103 @@ static int rar2_open(const char *path, struct fuse_file_info *fi) |
| goto open_error; |
| } |
| |
| - buf = malloc(P_ALIGN_(sizeof(struct iob) + IOB_SZ)); |
| + buf = iob_alloc(P_ALIGN_(sizeof(struct iob) + IOB_SZ)); |
| if (!buf) |
| goto open_error; |
| - IOB_RST(buf); |
| |
| - io = malloc(sizeof(struct io_handle)); |
| + io = calloc(1, sizeof(struct io_handle)); |
| op = calloc(1, sizeof(struct io_context)); |
| if (!op || !io) |
| goto open_error; |
| op->buf = buf; |
| - op->entry_p = NULL; |
| |
| - /* Open PIPE(s) and create child process */ |
| - fp = popen_(entry_p, &pid); |
| - if (fp != NULL) { |
| - FH_SETIO(fi->fh, io); |
| - FH_SETTYPE(fi->fh, IO_TYPE_RAR); |
| - FH_SETCONTEXT(fi->fh, op); |
| - printd(3, "(%05d) %-8s%s [%-16p]\n", getpid(), "ALLOC", |
| - path, FH_TOCONTEXT(fi->fh)); |
| - op->seq = 0; |
| - op->pos = 0; |
| - op->fp = fp; |
| - op->pid = pid; |
| - printd(4, "PIPE %p created towards child %d\n", |
| - op->fp, pid); |
| - |
| - pthread_mutex_init(&op->rd_req_mutex, NULL); |
| - pthread_cond_init(&op->rd_req_cond, NULL); |
| - op->rd_req = RD_IDLE; |
| + FH_SETIO(fi->fh, io); |
| + FH_SETTYPE(fi->fh, IO_TYPE_RAR); |
| + FH_SETCONTEXT(fi->fh, op); |
| + printd(3, "(%05d) %-8s%s [%-16p]\n", getpid(), "ALLOC", |
| + path, FH_TOCONTEXT(fi->fh)); |
| |
| - /* |
| - * The below will take precedence over keep_cache. |
| - * This flag will allow the filesystem to bypass the page cache using |
| - * the "direct_io" flag. This is not the same as O_DIRECT, it's |
| - * dictated by the filesystem not the application. |
| - * Since compressed archives might sometimes require fake data to be |
| - * returned in read requests, a cache might cause the same faulty |
| - * information to be propagated to sub-sequent reads. Setting this |
| - * flag will force _all_ reads to enter the filesystem. |
| - */ |
| + pthread_mutex_init(&op->rd_req_mutex, NULL); |
| + pthread_cond_init(&op->rd_req_cond, NULL); |
| + op->rd_req = RD_IDLE; |
| + fi->keep_cache = 0; |
| + |
| + /* |
| + * The below will take precedence over keep_cache. |
| + * This flag will allow the filesystem to bypass the page cache using |
| + * the "direct_io" flag. This is not the same as O_DIRECT, it's |
| + * dictated by the filesystem not the application. |
| + * Since compressed archives might sometimes require fake data to be |
| + * returned in read requests, a cache might cause the same faulty |
| + * information to be propagated to sub-sequent reads. Setting this |
| + * flag will force _all_ reads to enter the filesystem. |
| + */ |
| #if 0 /* disable for now */ |
| - if (entry_p->flags.direct_io) |
| - fi->direct_io = 1; |
| + if (entry_p->flags.direct_io) |
| + fi->direct_io = 1; |
| #endif |
| - |
| - /* Create reader thread */ |
| - if (pthread_create(&op->thread, &thread_attr, reader_task, (void *)op)) |
| - goto open_error; |
| - if (sync_thread_noread(op)) |
| - goto open_error; |
| - |
| - /* Promote to a write lock since we might need to |
| - * change the cache entry below. */ |
| - pthread_rwlock_unlock(&file_access_lock); |
| - pthread_rwlock_wrlock(&file_access_lock); |
| - |
| - buf->idx.data_p = MAP_FAILED; |
| - buf->idx.fd = -1; |
| - if (!preload_index(buf, path)) { |
| - entry_p->flags.save_eof = 0; |
| - entry_p->flags.direct_io = 0; |
| - fi->direct_io = 0; |
| - } else { |
| - /* Was the file removed ? */ |
| - if (get_save_eof(entry_p->rar_p) && !entry_p->flags.save_eof) { |
| - entry_p->flags.save_eof = 1; |
| - entry_p->flags.avi_tested = 0; |
| - } |
| + buf->idx.data_p = MAP_FAILED; |
| + buf->idx.fd = -1; |
| + if (!preload_index(buf, path)) { |
| + entry_p->flags.save_eof = 0; |
| + entry_p->flags.direct_io = 0; |
| + fi->direct_io = 0; |
| + } else { |
| + /* Was the file removed ? */ |
| + if (get_save_eof(entry_p->rar_p) && !entry_p->flags.save_eof) { |
| + entry_p->flags.save_eof = 1; |
| + entry_p->flags.avi_tested = 0; |
| } |
| + } |
| |
| - if (entry_p->flags.save_eof && !entry_p->flags.avi_tested) { |
| - if (check_avi_type(op)) |
| - entry_p->flags.save_eof = 0; |
| - entry_p->flags.avi_tested = 1; |
| - } |
| + if (entry_p->flags.save_eof && !entry_p->flags.avi_tested) { |
| + if (check_avi_type(op)) |
| + entry_p->flags.save_eof = 0; |
| + entry_p->flags.avi_tested = 1; |
| + } |
| |
| #ifdef DEBUG_READ |
| - char out_file[32]; |
| - sprintf(out_file, "%s.%d", "output", pid); |
| - op->dbg_fp = fopen(out_file, "w"); |
| + char out_file[32]; |
| + sprintf(out_file, "%s.%d", "output", pid); |
| + op->dbg_fp = fopen(out_file, "w"); |
| #endif |
| - /* |
| - * Make sure cache entry is filled in completely |
| - * before cloning it |
| - */ |
| - op->entry_p = filecache_clone(entry_p); |
| - if (!op->entry_p) |
| - goto open_error; |
| - goto open_end; |
| - } |
| + /* |
| + * Make sure cache entry is filled in completely |
| + * before cloning it |
| + */ |
| + op->entry_p = filecache_clone(entry_p); |
| + if (!op->entry_p) |
| + goto open_error; |
| + |
| + /* Open PIPE(s) and create child process */ |
| + if (popen_(op, &pid, op->pfd) == -1) |
| + goto open_error; |
| + op->pid = pid; |
| + printd(4, "PIPE %d/%d created towards child %u\n", |
| + op->pfd[0], op->pfd[1], pid); |
| + |
| + /* Create reader thread */ |
| + if (pthread_create(&op->thread, &thread_attr, reader_task, (void *)op)) |
| + goto open_error; |
| + if (sync_thread_read(op)) |
| + goto open_error; |
| + |
| + goto open_end; |
| } |
| |
| open_error: |
| pthread_rwlock_unlock(&file_access_lock); |
| - if (fp) { |
| - if (entry_p->flags.raw) |
| - fclose(fp); |
| - else |
| - pclose_(fp, pid); |
| - } |
| + if (fp && entry_p->flags.raw) |
| + fclose(fp); |
| free(io); |
| if (op) { |
| if (op->entry_p) |
| filecache_freeclone(op->entry_p); |
| + if (op->pid) |
| + pclose_(op); |
| free(op); |
| } |
| - free(buf); |
| + iob_free(buf); |
| |
| /* |
| * This is the best we can return here. So many different things |
| @@ -4526,30 +4513,25 @@ static int rar2_release(const char *path, struct fuse_file_info *fi) |
| FH_TOIO(fi->fh)->type == IO_TYPE_RAW) { |
| struct io_context *op = FH_TOCONTEXT(fi->fh); |
| free(FH_TOPATH(fi->fh)); |
| - if (op->fp) { |
| - if (op->entry_p->flags.raw) { |
| - printd(3, "Closing file handle %p\n", op->fp); |
| - fclose(op->fp); |
| - pthread_mutex_destroy(&op->raw_read_mutex); |
| - } else { |
| - if (!pthread_cancel(op->thread)) |
| - pthread_join(op->thread, NULL); |
| - |
| - pthread_cond_destroy(&op->rd_req_cond); |
| - pthread_mutex_destroy(&op->rd_req_mutex); |
| - |
| - if (pclose_(op->fp, op->pid)) |
| - printd(4, "child closed abnormally\n"); |
| - printd(4, "PIPE %p closed towards child %05d\n", |
| - op->fp, op->pid); |
| -#ifdef DEBUG_READ |
| - fclose(op->dbg_fp); |
| -#endif |
| - } |
| + if (op->fp && op->entry_p->flags.raw) { |
| + printd(3, "Closing file handle %p\n", op->fp); |
| + fclose(op->fp); |
| + pthread_mutex_destroy(&op->raw_read_mutex); |
| } |
| printd(3, "(%05d) %s [0x%-16" PRIx64 "]\n", getpid(), "FREE", fi->fh); |
| if (op->buf) { |
| - /* XXX clean up */ |
| + __wake_thread(op, RD_TERM); |
| + pthread_join(op->thread, NULL); |
| + pthread_cond_destroy(&op->rd_req_cond); |
| + pthread_mutex_destroy(&op->rd_req_mutex); |
| + |
| + if (pclose_(op)) |
| + printd(4, "child closed abnormally\n"); |
| + printd(4, "PIPE %d/%d closed towards child %u\n", |
| + op->pfd[0], op->pfd[1], op->pid); |
| +#ifdef DEBUG_READ |
| + fclose(op->dbg_fp); |
| +#endif |
| #ifdef HAVE_MMAP |
| if (op->buf->idx.data_p != MAP_FAILED && |
| op->buf->idx.mmap) |
| @@ -4561,12 +4543,11 @@ static int rar2_release(const char *path, struct fuse_file_info *fi) |
| free(op->buf->idx.data_p); |
| if (op->buf->idx.fd != -1) |
| close(op->buf->idx.fd); |
| - free(op->buf); |
| + iob_free(op->buf); |
| } |
| filecache_freeclone(op->entry_p); |
| free(op); |
| free(FH_TOIO(fi->fh)); |
| - FH_ZERO(fi->fh); |
| } else { |
| return lrelease(fi); |
| } |