1 /* VTreeFS - file.c - file and directory I/O */ 2 3 #include "inc.h" 4 #include <dirent.h> 5 6 #define GETDENTS_BUFSIZ 4096 7 8 static char *buf = NULL; 9 static size_t bufsize = 0; 10 11 /* 12 * Initialize the main buffer used for I/O. Return OK or an error code. 13 */ 14 int 15 init_buf(size_t size) 16 { 17 18 /* A default buffer size, for at least getdents. */ 19 if (size < GETDENTS_BUFSIZ) 20 size = GETDENTS_BUFSIZ; 21 22 if ((buf = malloc(size)) == NULL) 23 return ENOMEM; 24 25 bufsize = size; 26 return OK; 27 } 28 29 /* 30 * Free up the I/O buffer. 31 */ 32 void 33 cleanup_buf(void) 34 { 35 36 free(buf); 37 38 buf = NULL; 39 bufsize = 0; 40 } 41 42 /* 43 * Read from a file. 44 */ 45 ssize_t 46 fs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes, 47 off_t pos, int __unused call) 48 { 49 struct inode *node; 50 size_t off, chunk; 51 ssize_t r, len; 52 53 /* Try to get inode by its inode number. */ 54 if ((node = find_inode(ino_nr)) == NULL) 55 return EINVAL; 56 57 /* Check whether the node is a regular file. */ 58 if (!S_ISREG(node->i_stat.mode)) 59 return EINVAL; 60 61 /* For deleted files or with no read hook, feign an empty file. */ 62 if (is_inode_deleted(node) || vtreefs_hooks->read_hook == NULL) 63 return 0; /* EOF */ 64 65 assert(buf != NULL); 66 assert(bufsize > 0); 67 68 /* 69 * Call the read hook to fill the result buffer, repeatedly for as long 70 * as 1) the request is not yet fully completed, and 2) the read hook 71 * fills the entire buffer. 72 */ 73 for (off = 0; off < bytes; ) { 74 /* Get the next result chunk by calling the read hook. */ 75 chunk = bytes - off; 76 if (chunk > bufsize) 77 chunk = bufsize; 78 79 len = vtreefs_hooks->read_hook(node, buf, chunk, pos, 80 get_inode_cbdata(node)); 81 82 /* Copy any resulting data to user space. */ 83 if (len > 0) 84 r = fsdriver_copyout(data, off, buf, len); 85 else 86 r = len; /* EOF or error */ 87 88 /* 89 * If an error occurred, but we already produced some output, 90 * return a partial result. Otherwise return the error. 91 */ 92 if (r < 0) 93 return (off > 0) ? (ssize_t)off : r; 94 95 off += len; 96 pos += len; 97 98 if ((size_t)len < bufsize) 99 break; 100 } 101 102 return off; 103 } 104 105 /* 106 * Write to a file. 107 */ 108 ssize_t 109 fs_write(ino_t ino_nr, struct fsdriver_data * data, size_t bytes, off_t pos, 110 int __unused call) 111 { 112 struct inode *node; 113 size_t off, chunk; 114 ssize_t r; 115 116 if ((node = find_inode(ino_nr)) == NULL) 117 return EINVAL; 118 119 if (!S_ISREG(node->i_stat.mode)) 120 return EINVAL; 121 122 if (is_inode_deleted(node) || vtreefs_hooks->write_hook == NULL) 123 return EACCES; 124 125 if (bytes == 0) 126 return 0; 127 128 assert(buf != NULL); 129 assert(bufsize > 0); 130 131 /* 132 * Call the write hook to process the incoming data, repeatedly for as 133 * long as 1) the request is not yet fully completed, and 2) the write 134 * hook processes at least some of the given data. 135 */ 136 for (off = 0; off < bytes; ) { 137 chunk = bytes - off; 138 if (chunk > bufsize) 139 chunk = bufsize; 140 141 /* Copy the data from user space. */ 142 r = fsdriver_copyin(data, off, buf, chunk); 143 144 /* Call the write hook for the chunk. */ 145 if (r == OK) 146 r = vtreefs_hooks->write_hook(node, buf, chunk, pos, 147 get_inode_cbdata(node)); 148 149 /* 150 * If an error occurred, but we already processed some input, 151 * return a partial result. Otherwise return the error. 152 */ 153 if (r < 0) 154 return (off > 0) ? (ssize_t)off : r; 155 156 off += r; 157 pos += r; 158 159 if ((size_t)r == 0) 160 break; 161 } 162 163 return off; 164 } 165 166 /* 167 * Truncate a file. 168 */ 169 int 170 fs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos) 171 { 172 struct inode *node; 173 174 if ((node = find_inode(ino_nr)) == NULL) 175 return EINVAL; 176 177 if (!S_ISREG(node->i_stat.mode)) 178 return EINVAL; 179 180 if (is_inode_deleted(node) || vtreefs_hooks->trunc_hook == NULL) 181 return EACCES; 182 183 /* TODO: translate this case into all-zeroes write callbacks. */ 184 if (end_pos != 0) 185 return EINVAL; 186 187 return vtreefs_hooks->trunc_hook(node, start_pos, 188 get_inode_cbdata(node)); 189 } 190 191 /* 192 * Retrieve directory entries. 193 */ 194 ssize_t 195 fs_getdents(ino_t ino_nr, struct fsdriver_data * data, size_t bytes, 196 off_t * posp) 197 { 198 struct fsdriver_dentry fsdentry; 199 struct inode *node, *child; 200 const char *name; 201 off_t pos; 202 int r, skip, get_next, indexed; 203 204 if (*posp >= ULONG_MAX) 205 return EIO; 206 207 if ((node = find_inode(ino_nr)) == NULL) 208 return EINVAL; 209 210 indexed = node->i_indexed; 211 get_next = FALSE; 212 child = NULL; 213 214 /* Call the getdents hook, if any, to "refresh" the directory. */ 215 if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) { 216 r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node)); 217 if (r != OK) 218 return r; 219 } 220 221 assert(buf != NULL); 222 assert(bufsize > 0); 223 224 fsdriver_dentry_init(&fsdentry, data, bytes, buf, bufsize); 225 226 for (;;) { 227 /* Determine which inode and name to use for this entry. */ 228 pos = (*posp)++; 229 230 if (pos == 0) { 231 /* The "." entry. */ 232 child = node; 233 name = "."; 234 } else if (pos == 1) { 235 /* The ".." entry. */ 236 child = get_parent_inode(node); 237 if (child == NULL) 238 child = node; 239 name = ".."; 240 } else if (pos - 2 < indexed) { 241 /* All indexed entries. */ 242 child = get_inode_by_index(node, pos - 2); 243 244 /* 245 * If there is no inode with this particular index, 246 * continue with the next index number. 247 */ 248 if (child == NULL) continue; 249 250 name = child->i_name; 251 } else { 252 /* All non-indexed entries. */ 253 /* 254 * If this is the first loop iteration, first get to 255 * the non-indexed child identified by the current 256 * position. 257 */ 258 if (get_next == FALSE) { 259 skip = pos - indexed - 2; 260 child = get_first_inode(node); 261 262 /* Skip indexed children. */ 263 while (child != NULL && 264 child->i_index != NO_INDEX) 265 child = get_next_inode(child); 266 267 /* Skip to the right position. */ 268 while (child != NULL && skip-- > 0) 269 child = get_next_inode(child); 270 271 get_next = TRUE; 272 } else 273 child = get_next_inode(child); 274 275 /* No more children? Then stop. */ 276 if (child == NULL) 277 break; 278 279 assert(!is_inode_deleted(child)); 280 281 name = child->i_name; 282 } 283 284 /* Add the directory entry to the output. */ 285 r = fsdriver_dentry_add(&fsdentry, 286 (ino_t)get_inode_number(child), name, strlen(name), 287 IFTODT(child->i_stat.mode)); 288 if (r < 0) 289 return r; 290 if (r == 0) 291 break; 292 } 293 294 return fsdriver_dentry_finish(&fsdentry); 295 } 296