When reading from or writing to a node that has an attached pager, use pager_memcpy () instead of accessing the cache directly. This enables tarfs_read_node () and tarfs_write_node () too see and affect the changes made to the file through the memory mapping. --- tarfs.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 13 deletions(-)
diff --git a/tarfs.c b/tarfs.c index 3b9bcf590..5195d5771 100644 --- a/tarfs.c +++ b/tarfs.c @@ -759,11 +759,43 @@ tarfs_lookup_node (struct node** node, struct node* dir, const char* name) error_t tarfs_read_node (struct node *node, off_t offset, size_t *len, void* data) { + struct pager *pager; + memory_object_t memobj; + error_t err; + if (S_ISDIR (node->nn_stat.st_mode)) { *len = 0; return EISDIR; } + + if (node->nn->hardlink) + node = node->nn->hardlink; + + if (node->nn_stat.st_size <= offset) + { + *len = 0; + return 0; + } + if (node->nn_stat.st_size - offset < *len) + *len = node->nn_stat.st_size - offset; + + /* If we have a pager, always go through it. + The pager will call into the cache if needed. */ + pager = NODE_INFO(node)->pager; + if (pager) + { + ports_port_ref (pager); + pthread_mutex_unlock (&node->lock); + memobj = pager_get_port (pager); + mach_port_insert_right (mach_task_self (), memobj, memobj, + MACH_MSG_TYPE_MAKE_SEND); + err = pager_memcpy (pager, memobj, offset, data, len, VM_PROT_READ); + mach_port_deallocate (mach_task_self (), memobj); + pthread_mutex_lock (&node->lock); + ports_port_deref (pager); + return err; + } else return cache_read (node, offset, *len, data, len); } @@ -774,27 +806,54 @@ error_t tarfs_write_node (struct node *node, off_t offset, size_t *len, void *data) { IF_RWFS; + struct node *original_node; + struct pager *pager; + memory_object_t memobj; + error_t err; if (S_ISDIR (node->nn_stat.st_mode)) { *len = 0; return EISDIR; } - else + + original_node = node; + if (node->nn->hardlink) + node = node->nn->hardlink; + + /* First, grow the node if we need to. This would happen automatically + for the cache path below, but not for the pager path. */ + if (offset + *len > node->nn_stat.st_size) { - error_t err; - /* Checks whether we need to actually write to another node. - (hard links are not handled by cache_write ()). */ - struct node *what = node->nn->hardlink ? node->nn->hardlink : node; - + err = cache_set_size (node, offset + *len); + if (err) + return err; + } + + /* If we have a pager, always go through it. + The pager will call into the cache if needed. */ + pager = NODE_INFO(node)->pager; + if (pager) + { + ports_port_ref (pager); + pthread_mutex_unlock (&node->lock); + memobj = pager_get_port (pager); + mach_port_insert_right (mach_task_self (), memobj, memobj, + MACH_MSG_TYPE_MAKE_SEND); + err = pager_memcpy (pager, memobj, offset, data, len, + VM_PROT_READ|VM_PROT_WRITE); + mach_port_deallocate (mach_task_self (), memobj); + pthread_mutex_lock (&node->lock); + ports_port_deref (pager); + } + else err = cache_write (node, offset, data, *len, len); - /* Synchronize stat with hard link's target. */ - if ((! err) && (what != node)) - node->nn_stat.st_size = what->nn_stat.st_size; + /* Synchronize stat with hard link's target. */ + if ((! err) && (original_node != node)) + original_node->nn_stat.st_size = node->nn_stat.st_size; - return err; - } + return err; } /* Update NODE stat structure and mark it as dirty. */ @@ -1035,7 +1094,6 @@ tarfs_io_map (struct node *node, memory_object_t *rdobj, memory_object_t *wrobj) return 0; } - /* Rounds SIZE to the upper RECORDSIZE. */ static inline size_t @@ -1171,7 +1229,12 @@ tarfs_sync_fs (int wait) char *path; size_t size; - /* Lock the node first */ + /* If the node has a pager, ask it to flush + its state back into the cache. Do this before + locking the node. */ + if (NODE_INFO(node)->pager) + pager_sync (NODE_INFO(node)->pager, 1); + pthread_mutex_lock (&node->lock); have_to_sync = (tar->offset != file_offs + RECORDSIZE); path = fs_get_path_from_root (netfs_root_node, node); -- 2.31.1