xref: /minix3/minix/lib/libvtreefs/file.c (revision 7c48de6cc4c6d56f2277d378dba01dbac8a8c3b9)
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