xref: /minix/minix/lib/libsffs/read.c (revision 0a6a1f1d)
1 /* This file contains file and directory reading file system call handlers.
2  *
3  * The entry points into this file are:
4  *   do_read		perform the READ file system call
5  *   do_getdents	perform the GETDENTS file system call
6  *
7  * Created:
8  *   April 2009 (D.C. van Moolenbroek)
9  */
10 
11 #include "inc.h"
12 
13 #include <dirent.h>
14 
15 /*===========================================================================*
16  *				do_read					     *
17  *===========================================================================*/
18 ssize_t do_read(ino_t ino_nr, struct fsdriver_data *data, size_t count,
19 	off_t pos, int call)
20 {
21 /* Read data from a file.
22  */
23   struct inode *ino;
24   size_t size, off;
25   char *ptr;
26   int r, chunk;
27 
28   if ((ino = find_inode(ino_nr)) == NULL)
29 	return EINVAL;
30 
31   if (IS_DIR(ino)) return EISDIR;
32 
33   if ((r = get_handle(ino)) != OK)
34 	return r;
35 
36   assert(count > 0);
37 
38   /* Use the buffer from below to eliminate extra copying. */
39   size = sffs_table->t_readbuf(&ptr);
40   off = 0;
41 
42   while (count > 0) {
43 	chunk = MIN(count, size);
44 
45 	if ((r = sffs_table->t_read(ino->i_file, ptr, chunk, pos)) <= 0)
46 		break;
47 
48 	chunk = r;
49 
50 	if ((r = fsdriver_copyout(data, off, ptr, chunk)) != OK)
51 		break;
52 
53 	count -= chunk;
54 	off += chunk;
55 	pos += chunk;
56   }
57 
58   if (r < 0)
59 	return r;
60 
61   return off;
62 }
63 
64 /*===========================================================================*
65  *				do_getdents				     *
66  *===========================================================================*/
67 ssize_t do_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
68 	off_t *posp)
69 {
70 /* Retrieve directory entries.
71  */
72   struct fsdriver_dentry fsdentry;
73   char name[NAME_MAX+1];
74   struct inode *ino, *child;
75   struct sffs_attr attr;
76   off_t pos;
77   int r;
78   /* must be at least sizeof(struct dirent) + NAME_MAX */
79   static char buf[BLOCK_SIZE];
80 
81   if ((ino = find_inode(ino_nr)) == NULL)
82 	return EINVAL;
83 
84   if (!IS_DIR(ino)) return ENOTDIR;
85 
86   if (*posp < 0 || *posp >= ULONG_MAX) return EINVAL;
87 
88   /* We are going to need at least one free inode to store children in. */
89   if (!have_free_inode()) return ENFILE;
90 
91   /* If we don't have a directory handle yet, get one now. */
92   if ((r = get_handle(ino)) != OK)
93 	return r;
94 
95   fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf));
96 
97   /* We use the seek position as file index number. The first position is for
98    * the "." entry, the second position is for the ".." entry, and the next
99    * position numbers each represent a file in the directory.
100    */
101   for (;;) {
102 	/* Determine which inode and name to use for this entry.
103 	 * We have no idea whether the host will give us "." and/or "..",
104 	 * so generate our own and skip those from the host.
105 	 */
106 	pos = (*posp)++;
107 
108 	if (pos == 0) {
109 		/* Entry for ".". */
110 		child = ino;
111 
112 		strcpy(name, ".");
113 
114 		get_inode(child);
115 	}
116 	else if (pos == 1) {
117 		/* Entry for "..", but only when there is a parent. */
118 		if (ino->i_parent == NULL)
119 			continue;
120 
121 		child = ino->i_parent;
122 
123 		strcpy(name, "..");
124 
125 		get_inode(child);
126 	}
127 	else {
128 		/* Any other entry, not being "." or "..". */
129 		attr.a_mask = SFFS_ATTR_MODE;
130 
131 		r = sffs_table->t_readdir(ino->i_dir, pos - 2, name,
132 			sizeof(name), &attr);
133 
134 		if (r != OK) {
135 			/* No more entries? Then close the handle and stop. */
136 			if (r == ENOENT) {
137 				put_handle(ino);
138 
139 				break;
140 			}
141 
142 			/* FIXME: what if the error is ENAMETOOLONG? */
143 			return r;
144 		}
145 
146 		if (!strcmp(name, ".") || !strcmp(name, ".."))
147 			continue;
148 
149 		if ((child = lookup_dentry(ino, name)) == NULL) {
150 			child = get_free_inode();
151 
152 			/* We were promised a free inode! */
153 			assert(child != NULL);
154 
155 			child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
156 
157 			add_dentry(ino, name, child);
158 		}
159 	}
160 
161 	r = fsdriver_dentry_add(&fsdentry, INODE_NR(child), name, strlen(name),
162 		IS_DIR(child) ? DT_DIR : DT_REG);
163 
164 	put_inode(child);
165 
166 	if (r < 0)
167 		return r;
168 	if (r == 0)
169 		break;
170   }
171 
172   return fsdriver_dentry_finish(&fsdentry);
173 }
174