Merging upstream version 5.01+dfsg.
[syslinux-debian/hramrach.git] / core / fs / fs.c
blob2c10fe95903d66ad232980441b080e98aae42f7a
1 #include <sys/file.h>
2 #include <stdio.h>
3 #include <stdbool.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <dprintf.h>
7 #include "core.h"
8 #include "dev.h"
9 #include "fs.h"
10 #include "cache.h"
12 __export char *PATH;
14 /* The currently mounted filesystem */
15 __export struct fs_info *this_fs = NULL; /* Root filesystem */
17 /* Actual file structures (we don't have malloc yet...) */
18 __export struct file files[MAX_OPEN];
20 /* Symlink hard limits */
21 #define MAX_SYMLINK_CNT 20
22 #define MAX_SYMLINK_BUF 4096
25 * Get a new inode structure
27 struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data)
29 struct inode *inode = zalloc(sizeof(struct inode) + data);
30 if (inode) {
31 inode->fs = fs;
32 inode->ino = ino;
33 inode->refcnt = 1;
35 return inode;
39 * Free a refcounted inode
41 void put_inode(struct inode *inode)
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);
57 * Get an empty file structure
59 static struct file *alloc_file(void)
61 int i;
62 struct file *file = files;
64 for (i = 0; i < MAX_OPEN; i++) {
65 if (!file->fs)
66 return file;
67 file++;
70 return NULL;
74 * Close and free a file structure
76 static inline void free_file(struct file *file)
78 memset(file, 0, sizeof *file);
81 __export void _close_file(struct file *file)
83 if (file->fs)
84 file->fs->fs_ops->close_file(file);
85 free_file(file);
89 * Find and open the configuration file
91 __export int open_config(void)
93 int fd, handle;
94 struct file_info *fp;
96 fd = opendev(&__file_dev, NULL, O_RDONLY);
97 if (fd < 0)
98 return -1;
100 fp = &__file_info[fd];
102 handle = this_fs->fs_ops->open_config(&fp->i.fd);
103 if (handle < 0) {
104 close(fd);
105 errno = ENOENT;
106 return -1;
109 fp->i.offset = 0;
110 fp->i.nbytes = 0;
112 return fd;
115 __export void mangle_name(char *dst, const char *src)
117 this_fs->fs_ops->mangle_name(dst, src);
120 size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
122 bool have_more;
123 size_t bytes_read;
124 struct file *file;
126 file = handle_to_file(*handle);
127 bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
130 * If we reach EOF, the filesystem driver will have already closed
131 * the underlying file... this really should be cleaner.
133 if (!have_more) {
134 _close_file(file);
135 *handle = 0;
138 return bytes_read;
141 int searchdir(const char *name)
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;
149 dprintf("searchdir: %s root: %p cwd: %p\n",
150 name, this_fs->root, this_fs->cwd);
152 if (!(file = alloc_file()))
153 goto err_no_close;
154 file->fs = this_fs;
156 /* if we have ->searchdir method, call it */
157 if (file->fs->fs_ops->searchdir) {
158 file->fs->fs_ops->searchdir(name, file);
160 if (file->inode)
161 return file_to_handle(file);
162 else
163 goto err;
166 /* else, try the generic-path-lookup method */
168 /* Copy the path */
169 path = strdup(name);
170 if (!path) {
171 dprintf("searchdir: Couldn't copy path\n");
172 goto err_path;
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;
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';
195 if (next_inode_name) {
196 /* Advance beyond redundant slashes */
197 while (*next_inode_name == '/')
198 next_inode_name++;
200 /* Check if we're at the end */
201 if (*next_inode_name == '\0')
202 next_inode_name = NULL;
204 dprintf("searchdir: inode_name: %s\n", inode_name);
205 if (next_inode_name)
206 dprintf("searchdir: Remaining: %s\n", next_inode_name);
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;
216 /* Current directory? */
217 if (!strncmp(inode_name, ".", sizeof "."))
218 continue;
220 /* Parent directory? */
221 if (!strncmp(inode_name, "..", sizeof "..")) {
222 /* If there is no parent, just ignore it */
223 if (!inode->parent)
224 continue;
226 /* Add a reference to the parent so we can release the child */
227 tmp = get_inode(inode->parent);
229 /* Releasing the child will drop the parent back down to 1 */
230 put_inode(inode);
232 inode = tmp;
233 continue;
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;
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;
253 inode->parent = tmp;
254 inode->name = strdup(inode_name);
255 dprintf("searchdir: path component: %s\n", inode->name);
257 /* Symlink handling */
258 if (inode->mode == DT_LNK) {
259 char *new_path;
260 int new_len, copied;
262 /* target path + NUL */
263 new_len = inode->size + 1;
265 if (next_inode_name) {
266 /* target path + slash + remaining + NUL */
267 new_len += strlen(next_inode_name) + 1;
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;
276 new_path = malloc(new_len);
277 if (!new_path)
278 goto err_new_path;
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);
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);
292 free(path);
293 path = next_inode_name = new_path;
295 /* Add a reference to the parent so we can release the child */
296 tmp = get_inode(inode->parent);
298 /* Releasing the child will drop the parent back down to 1 */
299 put_inode(inode);
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;
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;
320 err_curdir:
321 free(path);
322 err_path:
323 if (!inode) {
324 dprintf("searchdir: Not found\n");
325 goto err;
328 file->inode = inode;
329 file->offset = 0;
331 return file_to_handle(file);
333 err:
334 _close_file(file);
335 err_no_close:
336 return -1;
339 __export int open_file(const char *name, struct com32_filedata *filedata)
341 int rv;
342 struct file *file;
343 char mangled_name[FILENAME_MAX];
345 dprintf("open_file %s\n", name);
347 mangle_name(mangled_name, name);
348 rv = searchdir(mangled_name);
350 if (rv < 0)
351 return rv;
353 file = handle_to_file(rv);
355 if (file->inode->mode != DT_REG) {
356 _close_file(file);
357 return -1;
360 filedata->size = file->inode->size;
361 filedata->blocklg2 = SECTOR_SHIFT(file->fs);
362 filedata->handle = rv;
364 return rv;
367 __export void close_file(uint16_t handle)
369 struct file *file;
371 if (handle) {
372 file = handle_to_file(handle);
373 _close_file(file);
378 * it will do:
379 * initialize the memory management function;
380 * set up the vfs fs structure;
381 * initialize the device structure;
382 * invoke the fs-specific init function;
383 * initialize the cache if we need one;
384 * finally, get the current inode for relative path looking.
386 __bss16 uint16_t SectorSize, SectorShift;
388 void fs_init(com32sys_t *regs)
390 static struct fs_info fs; /* The actual filesystem buffer */
391 uint8_t disk_devno = regs->edx.b[0];
392 uint8_t disk_cdrom = regs->edx.b[1];
393 sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
394 uint16_t disk_heads = regs->esi.w[0];
395 uint16_t disk_sectors = regs->edi.w[0];
396 uint32_t maxtransfer = regs->ebp.l;
397 int blk_shift = -1;
398 struct device *dev = NULL;
399 /* ops is a ptr list for several fs_ops */
400 const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
402 /* Initialize malloc() */
403 mem_init();
405 /* Default name for the root directory */
406 fs.cwd_name[0] = '/';
408 while ((blk_shift < 0) && *ops) {
409 /* set up the fs stucture */
410 fs.fs_ops = *ops;
413 * This boldly assumes that we don't mix FS_NODEV filesystems
414 * with FS_DEV filesystems...
416 if (fs.fs_ops->fs_flags & FS_NODEV) {
417 fs.fs_dev = NULL;
418 } else {
419 if (!dev)
420 dev = device_init(disk_devno, disk_cdrom, disk_offset,
421 disk_heads, disk_sectors, maxtransfer);
422 fs.fs_dev = dev;
424 /* invoke the fs-specific init code */
425 blk_shift = fs.fs_ops->fs_init(&fs);
426 ops++;
428 if (blk_shift < 0) {
429 printf("No valid file system found!\n");
430 while (1)
433 this_fs = &fs;
435 /* initialize the cache */
436 if (fs.fs_dev && fs.fs_dev->cache_data)
437 cache_init(fs.fs_dev, blk_shift);
439 /* start out in the root directory */
440 if (fs.fs_ops->iget_root) {
441 fs.root = fs.fs_ops->iget_root(&fs);
442 fs.cwd = get_inode(fs.root);
443 dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd);
446 if (fs.fs_ops->chdir_start) {
447 if (fs.fs_ops->chdir_start() < 0)
448 printf("Failed to chdir to start directory\n");
451 SectorShift = fs.sector_shift;
452 SectorSize = fs.sector_size;