PATH: use a linked list internally
[syslinux.git] / core / fs / fs.c
blobb6ee19c2e108721d33df19aec8c86136f848c6ad
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 "core.h"
9 #include "dev.h"
10 #include "fs.h"
11 #include "cache.h"
13 /* The currently mounted filesystem */
14 __export struct fs_info *this_fs = NULL; /* Root filesystem */
16 /* Actual file structures (we don't have malloc yet...) */
17 __export struct file files[MAX_OPEN];
19 /* Symlink hard limits */
20 #define MAX_SYMLINK_CNT 20
21 #define MAX_SYMLINK_BUF 4096
24 * Get a new inode structure
26 struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data)
28 struct inode *inode = zalloc(sizeof(struct inode) + data);
29 if (inode) {
30 inode->fs = fs;
31 inode->ino = ino;
32 inode->refcnt = 1;
34 return inode;
38 * Free a refcounted inode
40 void put_inode(struct inode *inode)
42 while (inode) {
43 struct inode *dead = inode;
44 int refcnt = --(dead->refcnt);
45 dprintf("put_inode %p name %s refcnt %u\n", dead, dead->name, refcnt);
46 if (refcnt)
47 break; /* We still have references */
48 inode = dead->parent;
49 if (dead->name)
50 free((char *)dead->name);
51 free(dead);
56 * Get an empty file structure
58 static struct file *alloc_file(void)
60 int i;
61 struct file *file = files;
63 for (i = 0; i < MAX_OPEN; i++) {
64 if (!file->fs)
65 return file;
66 file++;
69 return NULL;
73 * Close and free a file structure
75 static inline void free_file(struct file *file)
77 memset(file, 0, sizeof *file);
80 __export void _close_file(struct file *file)
82 if (file->fs)
83 file->fs->fs_ops->close_file(file);
84 free_file(file);
88 * Find and open the configuration file
90 __export int open_config(void)
92 int fd, handle;
93 struct file_info *fp;
95 fd = opendev(&__file_dev, NULL, O_RDONLY);
96 if (fd < 0)
97 return -1;
99 fp = &__file_info[fd];
101 handle = this_fs->fs_ops->open_config(&fp->i.fd);
102 if (handle < 0) {
103 close(fd);
104 errno = ENOENT;
105 return -1;
108 fp->i.offset = 0;
109 fp->i.nbytes = 0;
111 return fd;
114 __export void mangle_name(char *dst, const char *src)
116 this_fs->fs_ops->mangle_name(dst, src);
119 size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
121 bool have_more;
122 size_t bytes_read;
123 struct file *file;
125 file = handle_to_file(*handle);
126 bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
129 * If we reach EOF, the filesystem driver will have already closed
130 * the underlying file... this really should be cleaner.
132 if (!have_more) {
133 _close_file(file);
134 *handle = 0;
137 return bytes_read;
140 int searchdir(const char *name, int flags)
142 static char root_name[] = "/";
143 struct file *file;
144 char *path, *inode_name, *next_inode_name;
145 struct inode *tmp, *inode = NULL;
146 int symlink_count = MAX_SYMLINK_CNT;
148 dprintf("searchdir: %s root: %p cwd: %p\n",
149 name, this_fs->root, this_fs->cwd);
151 if (!(file = alloc_file()))
152 goto err_no_close;
153 file->fs = this_fs;
155 /* if we have ->searchdir method, call it */
156 if (file->fs->fs_ops->searchdir) {
157 file->fs->fs_ops->searchdir(name, flags, file);
159 if (file->inode)
160 return file_to_handle(file);
161 else
162 goto err;
165 /* else, try the generic-path-lookup method */
167 /* Copy the path */
168 path = strdup(name);
169 if (!path) {
170 dprintf("searchdir: Couldn't copy path\n");
171 goto err_path;
174 /* Work with the current directory, by default */
175 inode = get_inode(this_fs->cwd);
176 if (!inode) {
177 dprintf("searchdir: Couldn't use current directory\n");
178 goto err_curdir;
181 for (inode_name = path; inode_name; inode_name = next_inode_name) {
182 /* Root directory? */
183 if (inode_name[0] == '/') {
184 next_inode_name = inode_name + 1;
185 inode_name = root_name;
186 } else {
187 /* Find the next inode name */
188 next_inode_name = strchr(inode_name + 1, '/');
189 if (next_inode_name) {
190 /* Terminate the current inode name and point to next */
191 *next_inode_name++ = '\0';
194 if (next_inode_name) {
195 /* Advance beyond redundant slashes */
196 while (*next_inode_name == '/')
197 next_inode_name++;
199 /* Check if we're at the end */
200 if (*next_inode_name == '\0')
201 next_inode_name = NULL;
203 dprintf("searchdir: inode_name: %s\n", inode_name);
204 if (next_inode_name)
205 dprintf("searchdir: Remaining: %s\n", next_inode_name);
207 /* Root directory? */
208 if (inode_name[0] == '/') {
209 /* Release any chain that's already been established */
210 put_inode(inode);
211 inode = get_inode(this_fs->root);
212 continue;
215 /* Current directory? */
216 if (!strncmp(inode_name, ".", sizeof "."))
217 continue;
219 /* Parent directory? */
220 if (!strncmp(inode_name, "..", sizeof "..")) {
221 /* If there is no parent, just ignore it */
222 if (!inode->parent)
223 continue;
225 /* Add a reference to the parent so we can release the child */
226 tmp = get_inode(inode->parent);
228 /* Releasing the child will drop the parent back down to 1 */
229 put_inode(inode);
231 inode = tmp;
232 continue;
235 /* Anything else */
236 tmp = inode;
237 inode = this_fs->fs_ops->iget(inode_name, inode);
238 if (!inode) {
239 /* Failure. Release the chain */
240 put_inode(tmp);
241 break;
244 /* Sanity-check */
245 if (inode->parent && inode->parent != tmp) {
246 dprintf("searchdir: iget returned a different parent\n");
247 put_inode(inode);
248 inode = NULL;
249 put_inode(tmp);
250 break;
252 inode->parent = tmp;
253 inode->name = strdup(inode_name);
254 dprintf("searchdir: path component: %s\n", inode->name);
256 /* Symlink handling */
257 if (inode->mode == DT_LNK) {
258 char *new_path;
259 int new_len, copied;
261 /* target path + NUL */
262 new_len = inode->size + 1;
264 if (next_inode_name) {
265 /* target path + slash + remaining + NUL */
266 new_len += strlen(next_inode_name) + 1;
269 if (!this_fs->fs_ops->readlink ||
270 /* limit checks */
271 --symlink_count == 0 ||
272 new_len > MAX_SYMLINK_BUF)
273 goto err_new_len;
275 new_path = malloc(new_len);
276 if (!new_path)
277 goto err_new_path;
279 copied = this_fs->fs_ops->readlink(inode, new_path);
280 if (copied <= 0)
281 goto err_copied;
282 new_path[copied] = '\0';
283 dprintf("searchdir: Symlink: %s\n", new_path);
285 if (next_inode_name) {
286 new_path[copied] = '/';
287 strcpy(new_path + copied + 1, next_inode_name);
288 dprintf("searchdir: New path: %s\n", new_path);
291 free(path);
292 path = next_inode_name = new_path;
294 /* Add a reference to the parent so we can release the child */
295 tmp = get_inode(inode->parent);
297 /* Releasing the child will drop the parent back down to 1 */
298 put_inode(inode);
300 inode = tmp;
301 continue;
302 err_copied:
303 free(new_path);
304 err_new_path:
305 err_new_len:
306 put_inode(inode);
307 inode = NULL;
308 break;
311 /* If there's more to process, this should be a directory */
312 if (next_inode_name && inode->mode != DT_DIR) {
313 dprintf("searchdir: Expected a directory\n");
314 put_inode(inode);
315 inode = NULL;
316 break;
319 err_curdir:
320 free(path);
321 err_path:
322 if (!inode) {
323 dprintf("searchdir: Not found\n");
324 goto err;
327 file->inode = inode;
328 file->offset = 0;
330 return file_to_handle(file);
332 err:
333 _close_file(file);
334 err_no_close:
335 return -1;
338 __export int open_file(const char *name, int flags, struct com32_filedata *filedata)
340 int rv;
341 struct file *file;
342 char mangled_name[FILENAME_MAX];
344 dprintf("open_file %s\n", name);
346 mangle_name(mangled_name, name);
347 rv = searchdir(mangled_name, flags);
349 if (rv < 0)
350 return rv;
352 file = handle_to_file(rv);
354 if (file->inode->mode != DT_REG) {
355 _close_file(file);
356 return -1;
359 filedata->size = file->inode->size;
360 filedata->blocklg2 = SECTOR_SHIFT(file->fs);
361 filedata->handle = rv;
363 return rv;
366 __export void close_file(uint16_t handle)
368 struct file *file;
370 if (handle) {
371 file = handle_to_file(handle);
372 _close_file(file);
377 * it will do:
378 * initialize the memory management function;
379 * set up the vfs fs structure;
380 * initialize the device structure;
381 * invoke the fs-specific init function;
382 * initialize the cache if we need one;
383 * finally, get the current inode for relative path looking.
385 __bss16 uint16_t SectorSize, SectorShift;
387 void fs_init(com32sys_t *regs)
389 static struct fs_info fs; /* The actual filesystem buffer */
390 uint8_t disk_devno = regs->edx.b[0];
391 uint8_t disk_cdrom = regs->edx.b[1];
392 sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
393 uint16_t disk_heads = regs->esi.w[0];
394 uint16_t disk_sectors = regs->edi.w[0];
395 uint32_t maxtransfer = regs->ebp.l;
396 int blk_shift = -1;
397 struct device *dev = NULL;
398 /* ops is a ptr list for several fs_ops */
399 const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
401 /* Default name for the root directory */
402 fs.cwd_name[0] = '/';
404 while ((blk_shift < 0) && *ops) {
405 /* set up the fs stucture */
406 fs.fs_ops = *ops;
409 * This boldly assumes that we don't mix FS_NODEV filesystems
410 * with FS_DEV filesystems...
412 if (fs.fs_ops->fs_flags & FS_NODEV) {
413 fs.fs_dev = NULL;
414 } else {
415 if (!dev)
416 dev = device_init(disk_devno, disk_cdrom, disk_offset,
417 disk_heads, disk_sectors, maxtransfer);
418 fs.fs_dev = dev;
420 /* invoke the fs-specific init code */
421 blk_shift = fs.fs_ops->fs_init(&fs);
422 ops++;
424 if (blk_shift < 0) {
425 printf("No valid file system found!\n");
426 while (1)
429 this_fs = &fs;
431 /* initialize the cache */
432 if (fs.fs_dev && fs.fs_dev->cache_data)
433 cache_init(fs.fs_dev, blk_shift);
435 /* start out in the root directory */
436 if (fs.fs_ops->iget_root) {
437 fs.root = fs.fs_ops->iget_root(&fs);
438 fs.cwd = get_inode(fs.root);
439 dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd);
442 if (fs.fs_ops->chdir_start) {
443 if (fs.fs_ops->chdir_start() < 0)
444 printf("Failed to chdir to start directory\n");
447 SectorShift = fs.sector_shift;
448 SectorSize = fs.sector_size;