15eefd0feSDavid van Moolenbroek /* VTreeFS - file.c - file and directory I/O */
25eefd0feSDavid van Moolenbroek
35eefd0feSDavid van Moolenbroek #include "inc.h"
45eefd0feSDavid van Moolenbroek #include <dirent.h>
55eefd0feSDavid van Moolenbroek
65eefd0feSDavid van Moolenbroek #define GETDENTS_BUFSIZ 4096
75eefd0feSDavid van Moolenbroek
85eefd0feSDavid van Moolenbroek static char *buf = NULL;
95eefd0feSDavid van Moolenbroek static size_t bufsize = 0;
105eefd0feSDavid van Moolenbroek
115eefd0feSDavid van Moolenbroek /*
125eefd0feSDavid van Moolenbroek * Initialize the main buffer used for I/O. Return OK or an error code.
135eefd0feSDavid van Moolenbroek */
145eefd0feSDavid van Moolenbroek int
init_buf(size_t size)155eefd0feSDavid van Moolenbroek init_buf(size_t size)
165eefd0feSDavid van Moolenbroek {
175eefd0feSDavid van Moolenbroek
185eefd0feSDavid van Moolenbroek /* A default buffer size, for at least getdents. */
195eefd0feSDavid van Moolenbroek if (size < GETDENTS_BUFSIZ)
205eefd0feSDavid van Moolenbroek size = GETDENTS_BUFSIZ;
215eefd0feSDavid van Moolenbroek
225eefd0feSDavid van Moolenbroek if ((buf = malloc(size)) == NULL)
235eefd0feSDavid van Moolenbroek return ENOMEM;
245eefd0feSDavid van Moolenbroek
255eefd0feSDavid van Moolenbroek bufsize = size;
265eefd0feSDavid van Moolenbroek return OK;
275eefd0feSDavid van Moolenbroek }
285eefd0feSDavid van Moolenbroek
295eefd0feSDavid van Moolenbroek /*
305eefd0feSDavid van Moolenbroek * Free up the I/O buffer.
315eefd0feSDavid van Moolenbroek */
325eefd0feSDavid van Moolenbroek void
cleanup_buf(void)335eefd0feSDavid van Moolenbroek cleanup_buf(void)
345eefd0feSDavid van Moolenbroek {
355eefd0feSDavid van Moolenbroek
365eefd0feSDavid van Moolenbroek free(buf);
375eefd0feSDavid van Moolenbroek
385eefd0feSDavid van Moolenbroek buf = NULL;
395eefd0feSDavid van Moolenbroek bufsize = 0;
405eefd0feSDavid van Moolenbroek }
415eefd0feSDavid van Moolenbroek
425eefd0feSDavid van Moolenbroek /*
435eefd0feSDavid van Moolenbroek * Read from a file.
445eefd0feSDavid van Moolenbroek */
455eefd0feSDavid van Moolenbroek ssize_t
fs_read(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t pos,int __unused call)465eefd0feSDavid van Moolenbroek fs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
475eefd0feSDavid van Moolenbroek off_t pos, int __unused call)
485eefd0feSDavid van Moolenbroek {
495eefd0feSDavid van Moolenbroek struct inode *node;
505eefd0feSDavid van Moolenbroek size_t off, chunk;
515eefd0feSDavid van Moolenbroek ssize_t r, len;
525eefd0feSDavid van Moolenbroek
535eefd0feSDavid van Moolenbroek /* Try to get inode by its inode number. */
545eefd0feSDavid van Moolenbroek if ((node = find_inode(ino_nr)) == NULL)
555eefd0feSDavid van Moolenbroek return EINVAL;
565eefd0feSDavid van Moolenbroek
575eefd0feSDavid van Moolenbroek /* Check whether the node is a regular file. */
585eefd0feSDavid van Moolenbroek if (!S_ISREG(node->i_stat.mode))
595eefd0feSDavid van Moolenbroek return EINVAL;
605eefd0feSDavid van Moolenbroek
615eefd0feSDavid van Moolenbroek /* For deleted files or with no read hook, feign an empty file. */
625eefd0feSDavid van Moolenbroek if (is_inode_deleted(node) || vtreefs_hooks->read_hook == NULL)
635eefd0feSDavid van Moolenbroek return 0; /* EOF */
645eefd0feSDavid van Moolenbroek
655eefd0feSDavid van Moolenbroek assert(buf != NULL);
665eefd0feSDavid van Moolenbroek assert(bufsize > 0);
675eefd0feSDavid van Moolenbroek
685eefd0feSDavid van Moolenbroek /*
695eefd0feSDavid van Moolenbroek * Call the read hook to fill the result buffer, repeatedly for as long
705eefd0feSDavid van Moolenbroek * as 1) the request is not yet fully completed, and 2) the read hook
715eefd0feSDavid van Moolenbroek * fills the entire buffer.
725eefd0feSDavid van Moolenbroek */
735eefd0feSDavid van Moolenbroek for (off = 0; off < bytes; ) {
745eefd0feSDavid van Moolenbroek /* Get the next result chunk by calling the read hook. */
755eefd0feSDavid van Moolenbroek chunk = bytes - off;
765eefd0feSDavid van Moolenbroek if (chunk > bufsize)
775eefd0feSDavid van Moolenbroek chunk = bufsize;
785eefd0feSDavid van Moolenbroek
795eefd0feSDavid van Moolenbroek len = vtreefs_hooks->read_hook(node, buf, chunk, pos,
805eefd0feSDavid van Moolenbroek get_inode_cbdata(node));
815eefd0feSDavid van Moolenbroek
825eefd0feSDavid van Moolenbroek /* Copy any resulting data to user space. */
835eefd0feSDavid van Moolenbroek if (len > 0)
845eefd0feSDavid van Moolenbroek r = fsdriver_copyout(data, off, buf, len);
855eefd0feSDavid van Moolenbroek else
865eefd0feSDavid van Moolenbroek r = len; /* EOF or error */
875eefd0feSDavid van Moolenbroek
885eefd0feSDavid van Moolenbroek /*
895eefd0feSDavid van Moolenbroek * If an error occurred, but we already produced some output,
905eefd0feSDavid van Moolenbroek * return a partial result. Otherwise return the error.
915eefd0feSDavid van Moolenbroek */
925eefd0feSDavid van Moolenbroek if (r < 0)
93*7c48de6cSDavid van Moolenbroek return (off > 0) ? (ssize_t)off : r;
945eefd0feSDavid van Moolenbroek
955eefd0feSDavid van Moolenbroek off += len;
965eefd0feSDavid van Moolenbroek pos += len;
975eefd0feSDavid van Moolenbroek
985eefd0feSDavid van Moolenbroek if ((size_t)len < bufsize)
995eefd0feSDavid van Moolenbroek break;
1005eefd0feSDavid van Moolenbroek }
1015eefd0feSDavid van Moolenbroek
1025eefd0feSDavid van Moolenbroek return off;
1035eefd0feSDavid van Moolenbroek }
1045eefd0feSDavid van Moolenbroek
1055eefd0feSDavid van Moolenbroek /*
1065eefd0feSDavid van Moolenbroek * Write to a file.
1075eefd0feSDavid van Moolenbroek */
1085eefd0feSDavid van Moolenbroek ssize_t
fs_write(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t pos,int __unused call)1095eefd0feSDavid van Moolenbroek fs_write(ino_t ino_nr, struct fsdriver_data * data, size_t bytes, off_t pos,
1105eefd0feSDavid van Moolenbroek int __unused call)
1115eefd0feSDavid van Moolenbroek {
1125eefd0feSDavid van Moolenbroek struct inode *node;
1135eefd0feSDavid van Moolenbroek size_t off, chunk;
1145eefd0feSDavid van Moolenbroek ssize_t r;
1155eefd0feSDavid van Moolenbroek
1165eefd0feSDavid van Moolenbroek if ((node = find_inode(ino_nr)) == NULL)
1175eefd0feSDavid van Moolenbroek return EINVAL;
1185eefd0feSDavid van Moolenbroek
1195eefd0feSDavid van Moolenbroek if (!S_ISREG(node->i_stat.mode))
1205eefd0feSDavid van Moolenbroek return EINVAL;
1215eefd0feSDavid van Moolenbroek
1225eefd0feSDavid van Moolenbroek if (is_inode_deleted(node) || vtreefs_hooks->write_hook == NULL)
1235eefd0feSDavid van Moolenbroek return EACCES;
1245eefd0feSDavid van Moolenbroek
1255eefd0feSDavid van Moolenbroek if (bytes == 0)
1265eefd0feSDavid van Moolenbroek return 0;
1275eefd0feSDavid van Moolenbroek
1285eefd0feSDavid van Moolenbroek assert(buf != NULL);
1295eefd0feSDavid van Moolenbroek assert(bufsize > 0);
1305eefd0feSDavid van Moolenbroek
1315eefd0feSDavid van Moolenbroek /*
1325eefd0feSDavid van Moolenbroek * Call the write hook to process the incoming data, repeatedly for as
1335eefd0feSDavid van Moolenbroek * long as 1) the request is not yet fully completed, and 2) the write
1345eefd0feSDavid van Moolenbroek * hook processes at least some of the given data.
1355eefd0feSDavid van Moolenbroek */
1365eefd0feSDavid van Moolenbroek for (off = 0; off < bytes; ) {
1375eefd0feSDavid van Moolenbroek chunk = bytes - off;
1385eefd0feSDavid van Moolenbroek if (chunk > bufsize)
1395eefd0feSDavid van Moolenbroek chunk = bufsize;
1405eefd0feSDavid van Moolenbroek
1415eefd0feSDavid van Moolenbroek /* Copy the data from user space. */
1425eefd0feSDavid van Moolenbroek r = fsdriver_copyin(data, off, buf, chunk);
1435eefd0feSDavid van Moolenbroek
1445eefd0feSDavid van Moolenbroek /* Call the write hook for the chunk. */
1455eefd0feSDavid van Moolenbroek if (r == OK)
1465eefd0feSDavid van Moolenbroek r = vtreefs_hooks->write_hook(node, buf, chunk, pos,
1475eefd0feSDavid van Moolenbroek get_inode_cbdata(node));
1485eefd0feSDavid van Moolenbroek
1495eefd0feSDavid van Moolenbroek /*
1505eefd0feSDavid van Moolenbroek * If an error occurred, but we already processed some input,
1515eefd0feSDavid van Moolenbroek * return a partial result. Otherwise return the error.
1525eefd0feSDavid van Moolenbroek */
1535eefd0feSDavid van Moolenbroek if (r < 0)
154*7c48de6cSDavid van Moolenbroek return (off > 0) ? (ssize_t)off : r;
1555eefd0feSDavid van Moolenbroek
1565eefd0feSDavid van Moolenbroek off += r;
1575eefd0feSDavid van Moolenbroek pos += r;
1585eefd0feSDavid van Moolenbroek
1595eefd0feSDavid van Moolenbroek if ((size_t)r == 0)
1605eefd0feSDavid van Moolenbroek break;
1615eefd0feSDavid van Moolenbroek }
1625eefd0feSDavid van Moolenbroek
1635eefd0feSDavid van Moolenbroek return off;
1645eefd0feSDavid van Moolenbroek }
1655eefd0feSDavid van Moolenbroek
1665eefd0feSDavid van Moolenbroek /*
1675eefd0feSDavid van Moolenbroek * Truncate a file.
1685eefd0feSDavid van Moolenbroek */
1695eefd0feSDavid van Moolenbroek int
fs_trunc(ino_t ino_nr,off_t start_pos,off_t end_pos)1705eefd0feSDavid van Moolenbroek fs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos)
1715eefd0feSDavid van Moolenbroek {
1725eefd0feSDavid van Moolenbroek struct inode *node;
1735eefd0feSDavid van Moolenbroek
1745eefd0feSDavid van Moolenbroek if ((node = find_inode(ino_nr)) == NULL)
1755eefd0feSDavid van Moolenbroek return EINVAL;
1765eefd0feSDavid van Moolenbroek
1775eefd0feSDavid van Moolenbroek if (!S_ISREG(node->i_stat.mode))
1785eefd0feSDavid van Moolenbroek return EINVAL;
1795eefd0feSDavid van Moolenbroek
1805eefd0feSDavid van Moolenbroek if (is_inode_deleted(node) || vtreefs_hooks->trunc_hook == NULL)
1815eefd0feSDavid van Moolenbroek return EACCES;
1825eefd0feSDavid van Moolenbroek
1835eefd0feSDavid van Moolenbroek /* TODO: translate this case into all-zeroes write callbacks. */
1845eefd0feSDavid van Moolenbroek if (end_pos != 0)
1855eefd0feSDavid van Moolenbroek return EINVAL;
1865eefd0feSDavid van Moolenbroek
1875eefd0feSDavid van Moolenbroek return vtreefs_hooks->trunc_hook(node, start_pos,
1885eefd0feSDavid van Moolenbroek get_inode_cbdata(node));
1895eefd0feSDavid van Moolenbroek }
1905eefd0feSDavid van Moolenbroek
1915eefd0feSDavid van Moolenbroek /*
1925eefd0feSDavid van Moolenbroek * Retrieve directory entries.
1935eefd0feSDavid van Moolenbroek */
1945eefd0feSDavid van Moolenbroek ssize_t
fs_getdents(ino_t ino_nr,struct fsdriver_data * data,size_t bytes,off_t * posp)1955eefd0feSDavid van Moolenbroek fs_getdents(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
1965eefd0feSDavid van Moolenbroek off_t * posp)
1975eefd0feSDavid van Moolenbroek {
1985eefd0feSDavid van Moolenbroek struct fsdriver_dentry fsdentry;
1995eefd0feSDavid van Moolenbroek struct inode *node, *child;
2005eefd0feSDavid van Moolenbroek const char *name;
2015eefd0feSDavid van Moolenbroek off_t pos;
2025eefd0feSDavid van Moolenbroek int r, skip, get_next, indexed;
2035eefd0feSDavid van Moolenbroek
2045eefd0feSDavid van Moolenbroek if (*posp >= ULONG_MAX)
2055eefd0feSDavid van Moolenbroek return EIO;
2065eefd0feSDavid van Moolenbroek
2075eefd0feSDavid van Moolenbroek if ((node = find_inode(ino_nr)) == NULL)
2085eefd0feSDavid van Moolenbroek return EINVAL;
2095eefd0feSDavid van Moolenbroek
2105eefd0feSDavid van Moolenbroek indexed = node->i_indexed;
2115eefd0feSDavid van Moolenbroek get_next = FALSE;
2125eefd0feSDavid van Moolenbroek child = NULL;
2135eefd0feSDavid van Moolenbroek
2145eefd0feSDavid van Moolenbroek /* Call the getdents hook, if any, to "refresh" the directory. */
2155eefd0feSDavid van Moolenbroek if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) {
2165eefd0feSDavid van Moolenbroek r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node));
2175eefd0feSDavid van Moolenbroek if (r != OK)
2185eefd0feSDavid van Moolenbroek return r;
2195eefd0feSDavid van Moolenbroek }
2205eefd0feSDavid van Moolenbroek
2215eefd0feSDavid van Moolenbroek assert(buf != NULL);
2225eefd0feSDavid van Moolenbroek assert(bufsize > 0);
2235eefd0feSDavid van Moolenbroek
2245eefd0feSDavid van Moolenbroek fsdriver_dentry_init(&fsdentry, data, bytes, buf, bufsize);
2255eefd0feSDavid van Moolenbroek
226f912036bSDavid van Moolenbroek for (;;) {
2275eefd0feSDavid van Moolenbroek /* Determine which inode and name to use for this entry. */
2285eefd0feSDavid van Moolenbroek pos = (*posp)++;
2295eefd0feSDavid van Moolenbroek
2305eefd0feSDavid van Moolenbroek if (pos == 0) {
2315eefd0feSDavid van Moolenbroek /* The "." entry. */
2325eefd0feSDavid van Moolenbroek child = node;
2335eefd0feSDavid van Moolenbroek name = ".";
2345eefd0feSDavid van Moolenbroek } else if (pos == 1) {
2355eefd0feSDavid van Moolenbroek /* The ".." entry. */
2365eefd0feSDavid van Moolenbroek child = get_parent_inode(node);
2375eefd0feSDavid van Moolenbroek if (child == NULL)
2385eefd0feSDavid van Moolenbroek child = node;
2395eefd0feSDavid van Moolenbroek name = "..";
2405eefd0feSDavid van Moolenbroek } else if (pos - 2 < indexed) {
2415eefd0feSDavid van Moolenbroek /* All indexed entries. */
2425eefd0feSDavid van Moolenbroek child = get_inode_by_index(node, pos - 2);
2435eefd0feSDavid van Moolenbroek
2445eefd0feSDavid van Moolenbroek /*
2455eefd0feSDavid van Moolenbroek * If there is no inode with this particular index,
2465eefd0feSDavid van Moolenbroek * continue with the next index number.
2475eefd0feSDavid van Moolenbroek */
2485eefd0feSDavid van Moolenbroek if (child == NULL) continue;
2495eefd0feSDavid van Moolenbroek
2505eefd0feSDavid van Moolenbroek name = child->i_name;
2515eefd0feSDavid van Moolenbroek } else {
2525eefd0feSDavid van Moolenbroek /* All non-indexed entries. */
2535eefd0feSDavid van Moolenbroek /*
2545eefd0feSDavid van Moolenbroek * If this is the first loop iteration, first get to
2555eefd0feSDavid van Moolenbroek * the non-indexed child identified by the current
2565eefd0feSDavid van Moolenbroek * position.
2575eefd0feSDavid van Moolenbroek */
2585eefd0feSDavid van Moolenbroek if (get_next == FALSE) {
2595eefd0feSDavid van Moolenbroek skip = pos - indexed - 2;
2605eefd0feSDavid van Moolenbroek child = get_first_inode(node);
2615eefd0feSDavid van Moolenbroek
2625eefd0feSDavid van Moolenbroek /* Skip indexed children. */
2635eefd0feSDavid van Moolenbroek while (child != NULL &&
2645eefd0feSDavid van Moolenbroek child->i_index != NO_INDEX)
2655eefd0feSDavid van Moolenbroek child = get_next_inode(child);
2665eefd0feSDavid van Moolenbroek
2675eefd0feSDavid van Moolenbroek /* Skip to the right position. */
2685eefd0feSDavid van Moolenbroek while (child != NULL && skip-- > 0)
2695eefd0feSDavid van Moolenbroek child = get_next_inode(child);
2705eefd0feSDavid van Moolenbroek
2715eefd0feSDavid van Moolenbroek get_next = TRUE;
2725eefd0feSDavid van Moolenbroek } else
2735eefd0feSDavid van Moolenbroek child = get_next_inode(child);
2745eefd0feSDavid van Moolenbroek
2755eefd0feSDavid van Moolenbroek /* No more children? Then stop. */
2765eefd0feSDavid van Moolenbroek if (child == NULL)
2775eefd0feSDavid van Moolenbroek break;
2785eefd0feSDavid van Moolenbroek
2795eefd0feSDavid van Moolenbroek assert(!is_inode_deleted(child));
2805eefd0feSDavid van Moolenbroek
2815eefd0feSDavid van Moolenbroek name = child->i_name;
2825eefd0feSDavid van Moolenbroek }
2835eefd0feSDavid van Moolenbroek
2845eefd0feSDavid van Moolenbroek /* Add the directory entry to the output. */
2855eefd0feSDavid van Moolenbroek r = fsdriver_dentry_add(&fsdentry,
2865eefd0feSDavid van Moolenbroek (ino_t)get_inode_number(child), name, strlen(name),
2875eefd0feSDavid van Moolenbroek IFTODT(child->i_stat.mode));
2885eefd0feSDavid van Moolenbroek if (r < 0)
2895eefd0feSDavid van Moolenbroek return r;
290f912036bSDavid van Moolenbroek if (r == 0)
291f912036bSDavid van Moolenbroek break;
292f912036bSDavid van Moolenbroek }
2935eefd0feSDavid van Moolenbroek
2945eefd0feSDavid van Moolenbroek return fsdriver_dentry_finish(&fsdentry);
2955eefd0feSDavid van Moolenbroek }
296