Initial code release
[external/syslinux.git] / core / fs / chdir.c
1 #include <stdio.h>
2 #include <stdbool.h>
3 #include <string.h>
4 #include "fs.h"
5 #include "cache.h"
6
7 /*
8  * Convert a relative pathname to an absolute pathname
9  * In the future this might also resolve symlinks...
10  */
11 void pm_realpath(com32sys_t *regs)
12 {
13     const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
14     char       *dst = MK_PTR(regs->es, regs->edi.w[0]);
15
16     realpath(dst, src, FILENAME_MAX);
17 }
18
19 #define EMIT(x)                         \
20 do {                                    \
21     if (++n < bufsize)                  \
22         *q++ = (x);                     \
23 } while (0)
24
25 static size_t join_paths(char *dst, size_t bufsize,
26                          const char *s1, const char *s2)
27 {
28     const char *list[2];
29     int i;
30     char c;
31     const char *p;
32     char *q  = dst;
33     size_t n = 0;
34     bool slash = false;
35     
36     list[0] = s1;
37     list[1] = s2;
38
39     for (i = 0; i < 2; i++) {
40         p = list[i];
41
42         while ((c = *p++)) {
43             if (c == '/') {
44                 if (!slash)
45                     EMIT(c);
46                 slash = true;
47             } else {
48                 EMIT(c);
49                 slash = false;
50             }
51         }
52     }
53
54     if (bufsize)
55         *q = '\0';
56
57     return n;
58 }
59
60 size_t realpath(char *dst, const char *src, size_t bufsize)
61 {
62     if (this_fs->fs_ops->realpath) {
63         return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
64     } else {
65         /* Filesystems with "common" pathname resolution */
66         return join_paths(dst, bufsize, 
67                           src[0] == '/' ? "" : this_fs->cwd_name,
68                           src);
69     }
70 }
71
72 int chdir(const char *src)
73 {
74     int rv;
75     struct file *file;
76     char cwd_buf[CURRENTDIR_MAX];
77
78     if (this_fs->fs_ops->chdir)
79         return this_fs->fs_ops->chdir(this_fs, src);
80
81     /* Otherwise it is a "conventional filesystem" */
82     rv = searchdir(src);
83     if (rv < 0)
84         return rv;
85
86     file = handle_to_file(rv);
87     if (file->inode->mode != DT_DIR) {
88         _close_file(file);
89         return -1;
90     }
91
92     put_inode(this_fs->cwd);
93     this_fs->cwd = get_inode(file->inode);
94     _close_file(file);
95
96     /* Save the current working directory */
97     realpath(cwd_buf, src, CURRENTDIR_MAX);
98
99     /* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */
100     join_paths(this_fs->cwd_name, CURRENTDIR_MAX, cwd_buf, "/");
101
102     return 0;
103 }