1 #include <sys/file.h> 2 #include <stdio.h> 3 #include <stdbool.h> 4 #include <string.h> 5 #include <unistd.h> 6 #include <fcntl.h> 7 #include <dprintf.h> 8 #include <syslinux/sysappend.h> 9 #include "core.h" 10 #include "dev.h" 11 #include "fs.h" 12 #include "cache.h" 13 14 /* The currently mounted filesystem */ 15 __export struct fs_info *this_fs = NULL; /* Root filesystem */ 16 17 /* Actual file structures (we don't have malloc yet...) */ 18 __export struct file files[MAX_OPEN]; 19 20 /* Symlink hard limits */ 21 #define MAX_SYMLINK_CNT 20 22 #define MAX_SYMLINK_BUF 4096 23 24 /* 25 * Get a new inode structure 26 */ 27 struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data) 28 { 29 struct inode *inode = zalloc(sizeof(struct inode) + data); 30 if (inode) { 31 inode->fs = fs; 32 inode->ino = ino; 33 inode->refcnt = 1; 34 } 35 return inode; 36 } 37 38 /* 39 * Free a refcounted inode 40 */ 41 void put_inode(struct inode *inode) 42 { 43 while (inode) { 44 struct inode *dead = inode; 45 int refcnt = --(dead->refcnt); 46 dprintf("put_inode %p name %s refcnt %u\n", dead, dead->name, refcnt); 47 if (refcnt) 48 break; /* We still have references */ 49 inode = dead->parent; 50 if (dead->name) 51 free((char *)dead->name); 52 free(dead); 53 } 54 } 55 56 /* 57 * Get an empty file structure 58 */ 59 static struct file *alloc_file(void) 60 { 61 int i; 62 struct file *file = files; 63 64 for (i = 0; i < MAX_OPEN; i++) { 65 if (!file->fs) 66 return file; 67 file++; 68 } 69 70 return NULL; 71 } 72 73 /* 74 * Close and free a file structure 75 */ 76 static inline void free_file(struct file *file) 77 { 78 memset(file, 0, sizeof *file); 79 } 80 81 __export void _close_file(struct file *file) 82 { 83 if (file->fs) 84 file->fs->fs_ops->close_file(file); 85 free_file(file); 86 } 87 88 /* 89 * Find and open the configuration file 90 */ 91 __export int open_config(void) 92 { 93 int fd, handle; 94 struct file_info *fp; 95 96 fd = opendev(&__file_dev, NULL, O_RDONLY); 97 if (fd < 0) 98 return -1; 99 100 fp = &__file_info[fd]; 101 102 handle = this_fs->fs_ops->open_config(&fp->i.fd); 103 if (handle < 0) { 104 close(fd); 105 errno = ENOENT; 106 return -1; 107 } 108 109 fp->i.offset = 0; 110 fp->i.nbytes = 0; 111 112 return fd; 113 } 114 115 __export void mangle_name(char *dst, const char *src) 116 { 117 this_fs->fs_ops->mangle_name(dst, src); 118 } 119 120 size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors) 121 { 122 bool have_more; 123 size_t bytes_read; 124 struct file *file; 125 126 file = handle_to_file(*handle); 127 bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more); 128 129 /* 130 * If we reach EOF, the filesystem driver will have already closed 131 * the underlying file... this really should be cleaner. 132 */ 133 if (!have_more) { 134 _close_file(file); 135 *handle = 0; 136 } 137 138 return bytes_read; 139 } 140 141 int searchdir(const char *name, int flags) 142 { 143 static char root_name[] = "/"; 144 struct file *file; 145 char *path, *inode_name, *next_inode_name; 146 struct inode *tmp, *inode = NULL; 147 int symlink_count = MAX_SYMLINK_CNT; 148 149 dprintf("searchdir: %s root: %p cwd: %p\n", 150 name, this_fs->root, this_fs->cwd); 151 152 if (!(file = alloc_file())) 153 goto err_no_close; 154 file->fs = this_fs; 155 156 /* if we have ->searchdir method, call it */ 157 if (file->fs->fs_ops->searchdir) { 158 file->fs->fs_ops->searchdir(name, flags, file); 159 160 if (file->inode) 161 return file_to_handle(file); 162 else 163 goto err; 164 } 165 166 /* else, try the generic-path-lookup method */ 167 168 /* Copy the path */ 169 path = strdup(name); 170 if (!path) { 171 dprintf("searchdir: Couldn't copy path\n"); 172 goto err_path; 173 } 174 175 /* Work with the current directory, by default */ 176 inode = get_inode(this_fs->cwd); 177 if (!inode) { 178 dprintf("searchdir: Couldn't use current directory\n"); 179 goto err_curdir; 180 } 181 182 for (inode_name = path; inode_name; inode_name = next_inode_name) { 183 /* Root directory? */ 184 if (inode_name[0] == '/') { 185 next_inode_name = inode_name + 1; 186 inode_name = root_name; 187 } else { 188 /* Find the next inode name */ 189 next_inode_name = strchr(inode_name + 1, '/'); 190 if (next_inode_name) { 191 /* Terminate the current inode name and point to next */ 192 *next_inode_name++ = '\0'; 193 } 194 } 195 if (next_inode_name) { 196 /* Advance beyond redundant slashes */ 197 while (*next_inode_name == '/') 198 next_inode_name++; 199 200 /* Check if we're at the end */ 201 if (*next_inode_name == '\0') 202 next_inode_name = NULL; 203 } 204 dprintf("searchdir: inode_name: %s\n", inode_name); 205 if (next_inode_name) 206 dprintf("searchdir: Remaining: %s\n", next_inode_name); 207 208 /* Root directory? */ 209 if (inode_name[0] == '/') { 210 /* Release any chain that's already been established */ 211 put_inode(inode); 212 inode = get_inode(this_fs->root); 213 continue; 214 } 215 216 /* Current directory? */ 217 if (!strncmp(inode_name, ".", sizeof ".")) 218 continue; 219 220 /* Parent directory? */ 221 if (!strncmp(inode_name, "..", sizeof "..")) { 222 /* If there is no parent, just ignore it */ 223 if (!inode->parent) 224 continue; 225 226 /* Add a reference to the parent so we can release the child */ 227 tmp = get_inode(inode->parent); 228 229 /* Releasing the child will drop the parent back down to 1 */ 230 put_inode(inode); 231 232 inode = tmp; 233 continue; 234 } 235 236 /* Anything else */ 237 tmp = inode; 238 inode = this_fs->fs_ops->iget(inode_name, inode); 239 if (!inode) { 240 /* Failure. Release the chain */ 241 put_inode(tmp); 242 break; 243 } 244 245 /* Sanity-check */ 246 if (inode->parent && inode->parent != tmp) { 247 dprintf("searchdir: iget returned a different parent\n"); 248 put_inode(inode); 249 inode = NULL; 250 put_inode(tmp); 251 break; 252 } 253 inode->parent = tmp; 254 inode->name = strdup(inode_name); 255 dprintf("searchdir: path component: %s\n", inode->name); 256 257 /* Symlink handling */ 258 if (inode->mode == DT_LNK) { 259 char *new_path; 260 int new_len, copied; 261 262 /* target path + NUL */ 263 new_len = inode->size + 1; 264 265 if (next_inode_name) { 266 /* target path + slash + remaining + NUL */ 267 new_len += strlen(next_inode_name) + 1; 268 } 269 270 if (!this_fs->fs_ops->readlink || 271 /* limit checks */ 272 --symlink_count == 0 || 273 new_len > MAX_SYMLINK_BUF) 274 goto err_new_len; 275 276 new_path = malloc(new_len); 277 if (!new_path) 278 goto err_new_path; 279 280 copied = this_fs->fs_ops->readlink(inode, new_path); 281 if (copied <= 0) 282 goto err_copied; 283 new_path[copied] = '\0'; 284 dprintf("searchdir: Symlink: %s\n", new_path); 285 286 if (next_inode_name) { 287 new_path[copied] = '/'; 288 strcpy(new_path + copied + 1, next_inode_name); 289 dprintf("searchdir: New path: %s\n", new_path); 290 } 291 292 free(path); 293 path = next_inode_name = new_path; 294 295 /* Add a reference to the parent so we can release the child */ 296 tmp = get_inode(inode->parent); 297 298 /* Releasing the child will drop the parent back down to 1 */ 299 put_inode(inode); 300 301 inode = tmp; 302 continue; 303 err_copied: 304 free(new_path); 305 err_new_path: 306 err_new_len: 307 put_inode(inode); 308 inode = NULL; 309 break; 310 } 311 312 /* If there's more to process, this should be a directory */ 313 if (next_inode_name && inode->mode != DT_DIR) { 314 dprintf("searchdir: Expected a directory\n"); 315 put_inode(inode); 316 inode = NULL; 317 break; 318 } 319 } 320 err_curdir: 321 free(path); 322 err_path: 323 if (!inode) { 324 dprintf("searchdir: Not found\n"); 325 goto err; 326 } 327 328 file->inode = inode; 329 file->offset = 0; 330 331 return file_to_handle(file); 332 333 err: 334 dprintf("serachdir: error seraching file %s\n", name); 335 _close_file(file); 336 err_no_close: 337 return -1; 338 } 339 340 __export int open_file(const char *name, int flags, struct com32_filedata *filedata) 341 { 342 int rv; 343 struct file *file; 344 char mangled_name[FILENAME_MAX]; 345 346 dprintf("open_file %s\n", name); 347 348 mangle_name(mangled_name, name); 349 rv = searchdir(mangled_name, flags); 350 351 if (rv < 0) 352 return rv; 353 354 file = handle_to_file(rv); 355 356 if (file->inode->mode != DT_REG) { 357 _close_file(file); 358 return -1; 359 } 360 361 filedata->size = file->inode->size; 362 filedata->blocklg2 = SECTOR_SHIFT(file->fs); 363 filedata->handle = rv; 364 365 return rv; 366 } 367 368 __export void close_file(uint16_t handle) 369 { 370 struct file *file; 371 372 if (handle) { 373 file = handle_to_file(handle); 374 _close_file(file); 375 } 376 } 377 378 __export char *fs_uuid(void) 379 { 380 if (!this_fs || !this_fs->fs_ops || !this_fs->fs_ops->fs_uuid) 381 return NULL; 382 return this_fs->fs_ops->fs_uuid(this_fs); 383 } 384 385 /* 386 * it will do: 387 * initialize the memory management function; 388 * set up the vfs fs structure; 389 * initialize the device structure; 390 * invoke the fs-specific init function; 391 * initialize the cache if we need one; 392 * finally, get the current inode for relative path looking. 393 * 394 * ops is a ptr list for several fs_ops 395 */ 396 __bss16 uint16_t SectorSize, SectorShift; 397 398 void fs_init(const struct fs_ops **ops, void *priv) 399 { 400 static struct fs_info fs; /* The actual filesystem buffer */ 401 int blk_shift = -1; 402 struct device *dev = NULL; 403 404 /* Default name for the root directory */ 405 fs.cwd_name[0] = '/'; 406 407 while ((blk_shift < 0) && *ops) { 408 /* set up the fs stucture */ 409 fs.fs_ops = *ops; 410 411 /* 412 * This boldly assumes that we don't mix FS_NODEV filesystems 413 * with FS_DEV filesystems... 414 */ 415 if (fs.fs_ops->fs_flags & FS_NODEV) { 416 fs.fs_dev = NULL; 417 } else { 418 if (!dev) 419 dev = device_init(priv); 420 fs.fs_dev = dev; 421 } 422 /* invoke the fs-specific init code */ 423 blk_shift = fs.fs_ops->fs_init(&fs); 424 ops++; 425 } 426 if (blk_shift < 0) { 427 printf("No valid file system found!\n"); 428 while (1) 429 ; 430 } 431 this_fs = &fs; 432 433 /* initialize the cache only if it wasn't already initialized 434 * by the fs driver */ 435 if (fs.fs_dev && fs.fs_dev->cache_data && !fs.fs_dev->cache_init) 436 cache_init(fs.fs_dev, blk_shift); 437 438 /* start out in the root directory */ 439 if (fs.fs_ops->iget_root) { 440 fs.root = fs.fs_ops->iget_root(&fs); 441 fs.cwd = get_inode(fs.root); 442 dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd); 443 } 444 445 if (fs.fs_ops->chdir_start) { 446 if (fs.fs_ops->chdir_start() < 0) 447 printf("Failed to chdir to start directory\n"); 448 } 449 450 SectorShift = fs.sector_shift; 451 SectorSize = fs.sector_size; 452 453 /* Add FSUUID=... string to cmdline */ 454 sysappend_set_fs_uuid(); 455 456 } 457