diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-02-13 23:42:59 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-02-13 23:45:02 -0800 |
commit | 3399f97a70cdca0cb05129ea3fb5e9dd42c35d9c (patch) | |
tree | 529fd2403efa2dc6177495a827a64c776518d1de | |
parent | 3db9073fdd604c253ab0ea0ce83373dc89f633f3 (diff) | |
download | syslinux-3399f97a70cdca0cb05129ea3fb5e9dd42c35d9c.tar.gz |
fs: move to a chdir()-based mechanism for managing cwdsyslinux-4.00-pre19
Introduce a chdir() system and a way to obtain absolute pathnames.
This should allow us to set the current base directory (filename
prefix for PXE) without breaking access to the configuration file.
As a side benefit, for the "normal" filesystems we no longer need
magic hacks to figure out where we should set our current working
directory.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | core/chdir.c | 66 | ||||
-rw-r--r-- | core/comboot.inc | 4 | ||||
-rw-r--r-- | core/extern.inc | 7 | ||||
-rw-r--r-- | core/fs.c | 104 | ||||
-rw-r--r-- | core/fs/ext2/ext2.c | 18 | ||||
-rw-r--r-- | core/fs/fat/fat.c | 55 | ||||
-rw-r--r-- | core/fs/iso9660/iso9660.c | 30 | ||||
-rw-r--r-- | core/fs/lib/loadconfig.c | 21 | ||||
-rw-r--r-- | core/fs/pxe/dhcp_option.c | 2 | ||||
-rw-r--r-- | core/fs/pxe/dnsresolv.c | 1 | ||||
-rw-r--r-- | core/fs/pxe/pxe.c | 382 | ||||
-rw-r--r-- | core/getc.inc | 2 | ||||
-rw-r--r-- | core/include/fs.h | 53 | ||||
-rw-r--r-- | core/parseconfig.inc | 2 | ||||
-rw-r--r-- | core/pxelinux.asm | 2 | ||||
-rw-r--r-- | core/readdir.c (renamed from core/dir.c) | 2 | ||||
-rw-r--r-- | core/runkernel.inc | 2 | ||||
-rw-r--r-- | core/ui.inc | 12 |
18 files changed, 412 insertions, 353 deletions
diff --git a/core/chdir.c b/core/chdir.c new file mode 100644 index 00000000..c5c4d589 --- /dev/null +++ b/core/chdir.c @@ -0,0 +1,66 @@ +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include "fs.h" +#include "cache.h" + +/* + * Convert a relative pathname to an absolute pathname + * In the future this might also resolve symlinks... + */ +void pm_realpath(com32sys_t *regs) +{ + const char *src = MK_PTR(regs->ds, regs->esi.w[0]); + char *dst = MK_PTR(regs->es, regs->edi.w[0]); + + realpath(dst, src, FILENAME_MAX); +} + +size_t realpath(char *dst, const char *src, size_t bufsize) +{ + if (this_fs->fs_ops->realpath) { + return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize); + } else { + /* Filesystems with "common" pathname resolution */ + return snprintf(dst, bufsize, "%s%s", + src[0] == '/' ? "" : this_fs->cwd_name, + src); + } +} + +int chdir(const char *src) +{ + int rv; + struct file *file; + char *p; + + if (this_fs->fs_ops->chdir) + return this_fs->fs_ops->chdir(this_fs, src); + + /* Otherwise it is a "conventional filesystem" */ + rv = searchdir(src); + if (rv < 0) + return rv; + + file = handle_to_file(rv); + if (file->inode->mode != I_DIR) { + _close_file(file); + return -1; + } + + this_fs->cwd = file->inode; + file->inode = NULL; /* "Steal" the inode */ + _close_file(file); + + /* Save the current working directory */ + realpath(this_fs->cwd_name, src, CURRENTDIR_MAX); + p = strchr(this_fs->cwd_name, '\0'); + + /* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */ + if (p < this_fs->cwd_name + CURRENTDIR_MAX - 1 && + (p == this_fs->cwd_name || p[1] != '/')) { + p[0] = '/'; + p[1] = '\0'; + } + return 0; +} diff --git a/core/comboot.inc b/core/comboot.inc index 03507c89..e271b9ec 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -519,7 +519,7 @@ comapi_open: mov di,InitRD pm_call mangle_name pop ds - pm_call searchdir + pm_call pm_searchdir jz comapi_err mov P_EAX,eax mov P_CX,SECTOR_SIZE @@ -751,7 +751,7 @@ comapi_runkernel: mov di,KernelName pm_call mangle_name pop ds - pm_call searchdir + pm_call pm_searchdir jz comapi_err ; The kernel image was found, so we can load it... diff --git a/core/extern.inc b/core/extern.inc index 95a9c88d..da6c675b 100644 --- a/core/extern.inc +++ b/core/extern.inc @@ -13,10 +13,13 @@ extern hello ; fs.c - extern fs_init, searchdir, getfssec, mangle_name, load_config + extern fs_init, pm_searchdir, getfssec, mangle_name, load_config extern unmangle_name, close_file - ; dir.c + ; chdir.c + extern pm_realpath + + ; readdir.c extern opendir, readdir, closedir %if IS_PXELINUX ; pxe.c @@ -1,23 +1,17 @@ #include <stdio.h> #include <stdbool.h> #include <string.h> -#include <fs.h> -#include <cache.h> +#include "fs.h" +#include "cache.h" /* The currently mounted filesystem */ struct fs_info *this_fs = NULL; /* Root filesystem */ -static struct fs_info fs; static struct inode *this_inode = NULL; /* Current working directory */ /* Actual file structures (we don't have malloc yet...) */ struct file files[MAX_OPEN]; /* - * Set to FS_THISIND during the execution of load_config. - */ -enum fs_flags is_load_config = 0; - -/* * Get a new inode structure */ struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data) @@ -65,26 +59,15 @@ void _close_file(struct file *file) /* * Convert between a 16-bit file handle and a file structure */ -inline uint16_t file_to_handle(struct file *file) -{ - return file ? (file - files)+1 : 0; -} -inline struct file *handle_to_file(uint16_t handle) -{ - return handle ? &files[handle-1] : NULL; -} void load_config(void) { int err; - is_load_config = FS_THISIND; err = this_fs->fs_ops->load_config(); - is_load_config = 0; -#if 0 - printf("Loading config file %s\n", err ? "failed" : "successed"); -#endif + if (err) + printf("ERROR: No configuration file found\n"); } void mangle_name(com32sys_t *regs) @@ -107,7 +90,6 @@ void unmangle_name(com32sys_t *regs) regs->edi.w[0] = OFFS_WRT(dst, regs->es); } - void getfssec(com32sys_t *regs) { int sectors; @@ -137,16 +119,32 @@ void getfssec(com32sys_t *regs) regs->ecx.l = bytes_read; } - -void searchdir(com32sys_t *regs) +void pm_searchdir(com32sys_t *regs) { char *name = MK_PTR(regs->ds, regs->edi.w[0]); + int rv; + + rv = searchdir(name); + if (rv < 0) { + regs->esi.w[0] = 0; + regs->eax.l = 0; + regs->eflags.l |= EFLAGS_ZF; + } else { + regs->esi.w[0] = rv; + regs->eax.l = handle_to_file(rv)->file_len; + regs->eflags.l &= ~EFLAGS_ZF; + } +} + +int searchdir(const char *name) +{ struct inode *inode; struct inode *parent; struct file *file; char part[256]; char *p; int symlink_count = 6; + bool got_parent; #if 0 printf("filename: %s\n", name); @@ -160,26 +158,22 @@ void searchdir(com32sys_t *regs) if (file->fs->fs_ops->searchdir) { file->fs->fs_ops->searchdir(name, file); - if (file->open_file) { - regs->esi.w[0] = file_to_handle(file); - regs->eax.l = file->file_len; - regs->eflags.l &= ~EFLAGS_ZF; - return; - } - - goto err; + if (file->open_file) + return file_to_handle(file); + else + goto err; } - /* else, try the generic-path-lookup method */ if (*name == '/') { inode = this_fs->fs_ops->iget_root(this_fs); - while(*name == '/') + while (*name == '/') name++; } else { - inode = this_inode; + inode = this_fs->cwd; } parent = inode; + got_parent = false; while (*name) { p = part; @@ -199,20 +193,10 @@ void searchdir(com32sys_t *regs) free_inode(inode); continue; } - - /* - * For the relative path searching used in FAT and ISO fs. - */ - if ((this_fs->fs_ops->fs_flags & is_load_config) && - (this_inode != parent)){ - if (this_inode) - free_inode(this_inode); - this_inode = parent; - } - - if (parent != this_inode) + if (got_parent) free_inode(parent); parent = inode; + got_parent = true; } if (!*name) break; @@ -220,20 +204,19 @@ void searchdir(com32sys_t *regs) name++; } + if (got_parent) + free_inode(parent); + file->inode = inode; file->offset = 0; + file->file_len = inode->size; - regs->esi.w[0] = file_to_handle(file); - regs->eax.l = inode->size; - regs->eflags.l &= ~EFLAGS_ZF; - return; + return file_to_handle(file); err: _close_file(file); -err_no_close: - regs->esi.w[0] = 0; - regs->eax.l = 0; - regs->eflags.l |= EFLAGS_ZF; +err_no_close: + return -1; } void close_file(com32sys_t *regs) @@ -259,6 +242,7 @@ void close_file(com32sys_t *regs) */ void fs_init(com32sys_t *regs) { + static struct fs_info fs; /* The actual filesystem buffer */ uint8_t disk_devno = regs->edx.b[0]; uint8_t disk_cdrom = regs->edx.b[1]; sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32); @@ -271,6 +255,9 @@ void fs_init(com32sys_t *regs) /* Initialize malloc() */ mem_init(); + + /* Default name for the root directory */ + fs.cwd_name[0] = '/'; while ((blk_shift < 0) && *ops) { /* set up the fs stucture */ @@ -303,8 +290,7 @@ void fs_init(com32sys_t *regs) if (fs.fs_dev && fs.fs_dev->cache_data) cache_init(fs.fs_dev, blk_shift); - if (fs.fs_ops->iget_current) - this_inode = fs.fs_ops->iget_current(&fs); - else - this_inode = fs.fs_ops->iget_root(&fs); /* Will be set later */ + /* start out in the root directory */ + if (fs.fs_ops->iget_root) + fs.cwd = fs.fs_ops->iget_root(&fs); } diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c index 9cdbb3b7..c794300b 100644 --- a/core/fs/ext2/ext2.c +++ b/core/fs/ext2/ext2.c @@ -386,21 +386,6 @@ static struct dirent * ext2_readdir(struct file *file) return dirent; } -/* Load the config file, return 1 if failed, or 0 */ -static int ext2_load_config(void) -{ - char *config_name = "extlinux.conf"; - com32sys_t regs; - - memset(®s, 0, sizeof regs); - snprintf(ConfigName, FILENAME_MAX, "%s/extlinux.conf", CurrentDirName); - regs.edi.w[0] = OFFS_WRT(ConfigName, 0); - call16(core_open, ®s, ®s); - - return !!(regs.eflags.l & EFLAGS_ZF); -} - - /* * init. the fs meta data, return the block size bits. */ @@ -458,9 +443,8 @@ const struct fs_ops ext2_fs_ops = { .close_file = ext2_close_file, .mangle_name = generic_mangle_name, .unmangle_name = generic_unmangle_name, - .load_config = ext2_load_config, + .load_config = generic_load_config, .iget_root = ext2_iget_root, - .iget_current = NULL, .iget = ext2_iget, .follow_symlink = ext2_follow_symlink, .readdir = ext2_readdir diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c index c388dba7..97dd715c 100644 --- a/core/fs/fat/fat.c +++ b/core/fs/fat/fat.c @@ -741,45 +741,33 @@ got: /* Load the config file, return 1 if failed, or 0 */ static int vfat_load_config(void) { - const char * const syslinux_cfg[] = { - "/boot/syslinux/syslinux.cfg", - "/syslinux/syslinux.cfg", - "/syslinux.cfg" + const char *search_directories[] = { + "/boot/syslinux", + "/syslinux", + "/", + NULL }; com32sys_t regs; - char *p; - int i = 0; + int i; - /* - * we use the ConfigName to pass the config path because - * it is under the address 0xffff - */ - memset(®s, 0, sizeof regs); - regs.edi.w[0] = OFFS_WRT(ConfigName, 0); - if (*CurrentDirName) { /* installed by extlinux not syslinux */ - sprintf(ConfigName, "%s/extlinux.conf", CurrentDirName); - call16(core_open, ®s, ®s); - return regs.eflags.l & EFLAGS_ZF; - } - /* installed by syslinux */ - for (; i < 3; i++) { - strcpy(ConfigName, syslinux_cfg[i]); - call16(core_open, ®s, ®s); + /* If installed by extlinux, try the extlinux filename */ + if (*CurrentDirName && !generic_load_config()) + return 0; - /* if zf flag set, then failed; try another */ - if (! (regs.eflags.l & EFLAGS_ZF)) - break; - } - if (i == 3) { - printf("no config file found\n"); - return 1; /* no config file */ + for (i = 0; search_directories[i]; i++) { + memset(®s, 0, sizeof regs); + snprintf(ConfigName, FILENAME_MAX, "%s/syslinux.cfg", + search_directories[i]); + regs.edi.w[0] = OFFS_WRT(ConfigName, 0); + call16(core_open, ®s, ®s); + if (!(regs.eflags.l & EFLAGS_ZF)) + break; } + if (!search_directories[i]) + return -1; - strcpy(ConfigName, "syslinux.cfg"); - strcpy(CurrentDirName, syslinux_cfg[i]); - p = strrchr(CurrentDirName, '/'); - *(p + 1) = '\0'; /* In case we met '/syslinux.cfg' */ - + /* Set the current working directory */ + chdir(search_directories[i]); return 0; } @@ -864,6 +852,5 @@ const struct fs_ops vfat_fs_ops = { .load_config = vfat_load_config, .readdir = vfat_readdir, .iget_root = vfat_iget_root, - .iget_current = NULL, .iget = vfat_iget, }; diff --git a/core/fs/iso9660/iso9660.c b/core/fs/iso9660/iso9660.c index e6911644..06caedd9 100644 --- a/core/fs/iso9660/iso9660.c +++ b/core/fs/iso9660/iso9660.c @@ -382,32 +382,29 @@ static struct dirent *iso_readdir(struct file *file) /* Load the config file, return 1 if failed, or 0 */ static int iso_load_config(void) { - const char *config_file[] = { - "/boot/isolinux/isolinux.cfg", - "/isolinux/isolinux.cfg" + const char *search_directories[] = { + "/boot/isolinux", + "/isolinux", + "/", + NULL }; com32sys_t regs; - int i = 0; - char *p; + int i; - for (; i < 2; i++) { + for (i = 0; search_directories[i]; i++) { memset(®s, 0, sizeof regs); - strcpy(ConfigName, config_file[i]); + snprintf(ConfigName, FILENAME_MAX, "%s/isolinux.cfg", + search_directories[i]); regs.edi.w[0] = OFFS_WRT(ConfigName, 0); call16(core_open, ®s, ®s); if (!(regs.eflags.l & EFLAGS_ZF)) break; } - if (i == 2) { - printf("No config file found\n"); - return 1; - } - - strcpy(ConfigName, "isolinux.cfg"); - strcpy(CurrentDirName, config_file[i]); - p = strrchr(CurrentDirName, '/'); - *p = '\0'; + if (!search_directories[i]) + return -1; + /* Set the current working directory */ + chdir(search_directories[i]); return 0; } @@ -446,7 +443,6 @@ const struct fs_ops iso_fs_ops = { .unmangle_name = generic_unmangle_name, .load_config = iso_load_config, .iget_root = iso_iget_root, - .iget_current = NULL, .iget = iso_iget, .readdir = iso_readdir }; diff --git a/core/fs/lib/loadconfig.c b/core/fs/lib/loadconfig.c new file mode 100644 index 00000000..da2ba50c --- /dev/null +++ b/core/fs/lib/loadconfig.c @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <string.h> +#include <core.h> +#include <fs.h> + +/* + * Standard version of load_config for extlinux-installed filesystems + */ +int generic_load_config(void) +{ + com32sys_t regs; + + chdir(CurrentDirName); + + memset(®s, 0, sizeof regs); + snprintf(ConfigName, FILENAME_MAX, "%s/extlinux.conf", CurrentDirName); + regs.edi.w[0] = OFFS_WRT(ConfigName, 0); + call16(core_open, ®s, ®s); + + return (regs.eflags.l & EFLAGS_ZF) ? -1 : 0; +} diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c index 0787078a..150290ae 100644 --- a/core/fs/pxe/dhcp_option.c +++ b/core/fs/pxe/dhcp_option.c @@ -130,7 +130,7 @@ static void pxelinux_configfile(void *data, int opt_len) ConfigName[opt_len] = 0; } -static void pxelinux_pathprefix(void *data,int opt_len) +static void pxelinux_pathprefix(void *data, int opt_len) { DHCPMagic |= 4; strncpy(path_prefix, data, opt_len); diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c index 15560447..18cfc486 100644 --- a/core/fs/pxe/dnsresolv.c +++ b/core/fs/pxe/dnsresolv.c @@ -168,6 +168,7 @@ static char *dns_skiplabel(char *label) * and returns the ip addr in _ip_ if it exists and can be found. * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated * + * XXX: probably need some caching here. */ uint32_t dns_resolv(const char *name) { diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index ea26ef61..56f8ee41 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -19,8 +19,8 @@ char MAC[MAC_MAX + 1]; /* Actual MAC address */ uint8_t MAC_len; /* MAC address len */ uint8_t MAC_type; /* MAC address type */ -char boot_file[256]; -char path_prefix[256]; +char boot_file[256]; /* From DHCP */ +char path_prefix[256]; /* From DHCP */ char dot_quad_buf[16]; static struct open_file_t Files[MAX_OPEN]; @@ -229,11 +229,11 @@ static int gendotquad(char *dst, uint32_t ip) static const char *parse_dotquad(const char *ip_str, uint32_t *res) { const char *p = ip_str; - int i = 0; uint8_t part = 0; - uint32_t ip = 0; + uint32_t ip = 0; + int i; - for (; i < 4; i++) { + for (i = 0; i < 4; i++) { while (is_digit(*p)) { part = part * 10 + *p - '0'; p++; @@ -245,7 +245,7 @@ static const char *parse_dotquad(const char *ip_str, uint32_t *res) part = 0; p++; } - p --; + p--; *res = ip; return p; @@ -365,42 +365,50 @@ static int pxe_get_cached_info(int type) } - -#if GPXE - /* - * Return true if and only if the buffer pointed to by - * url is a URL -- it must contain :// and it must be the - * first colon. + * Return the type of pathname passed. */ -static inline bool is_url(const char *url) -{ - const char *p = strchr(url, ':'); - - return p && p[1] == '/' && p[2] == '/'; -} - +enum pxe_path_type { + PXE_RELATIVE, /* No :: or URL */ + PXE_HOMESERVER, /* Starting with :: */ + PXE_TFTP, /* host:: */ + PXE_URL, /* Absolute URL syntax */ +}; -/* - * Return CF=0 if and only if the buffer pointed to by DS:SI is a URL - * (contains ://) *and* the gPXE extensions API is available. No - * registers modified. - */ -static bool is_gpxe(const char *url) +static enum pxe_path_type pxe_path_type(const char *str) { - static bool already; - - if (!is_url(url)) - return false; - - if (!has_gpxe && !already) { - fputs("URL syntax, but gPXE extensions not detected, tring plain TFTP...\n", stdout); - already = true; + const char *p; + + p = str; + while (1) { + switch (*p) { + case ':': + if (p[1] == ':') { + if (p == str) + return PXE_HOMESERVER; + else + return PXE_TFTP; + } else if (p > str && p[1] == '/' && p[2] == '/') + return PXE_URL; + + /* else fall through */ + case '/': case '!': case '@': case '#': case '%': + case '^': case '&': case '*': case '(': case ')': + case '[': case ']': case '{': case '}': case '\\': + case '|': case '=': case '`': case '~': case '\'': + case '\"': case ';': case '>': case '<': case '?': + case '\0': + /* Any of these characters terminate the colon search */ + return PXE_RELATIVE; + default: + break; + } + p++; } - - return has_gpxe; } +#if GPXE + /** * Get a fresh packet from a gPXE socket * @param: file -> socket structure @@ -446,85 +454,15 @@ static void get_packet_gpxe(struct open_file_t *file) * mangle a filename pointed to by _src_ into a buffer pointed * to by _dst_; ends on encountering any whitespace. * - * The first four bytes of the manged name is the IP address of - * the download host, 0 for no host, or -1 for a gPXE URL. - * */ static void pxe_mangle_name(char *dst, const char *src) { - const char *p = src; - uint32_t ip = server_ip; - int i = 0; - -#if GPXE - if (is_url(src)) { - ip = -1; - goto store; - } -#endif - - if (*p == 0 || !(p = strstr(src, "::"))) { - /* seems no ip, so make ip to 0 */ - p = src; - ip = 0; - } else if (p == src) { - /* skip the first two-colon */ - p += 2; - } else { - /* - * we have a :: prefix of some sort, it could be either a DNS - * name or dot-quad IP address. Try the dot-quad first. - */ - p = src; - if ((p = parse_dotquad(p, &ip)) && !strncmp(p, "::", 2)) { - p += 2; - } else { - ip = dns_resolv(p); - if (ip && (p = strchr(p, ':')) && p[1] == ':') { - p += 2; - } else { - /* no ip, too */ - p = src; - ip = 0; - } - } - } - - store: - *(uint32_t *)dst = ip; - dst += 4; - i = FILENAME_MAX - 5; - - do { - if (!not_whitespace(*p)) - break; - *dst++ = *p++; - } while (i--); + size_t len = FILENAME_MAX-1; - i++; - while (i) { - *dst++ = 0; - i--; - } -} - - -/* - * Does the opposite of mangle_name; converts a DOS-mangled - * filename to the conventional representation. This is - * needed for the BOOT_IMAGE= parameter for the kernel. - */ -static char *pxe_unmangle_name(char *dst, const char *src) -{ - uint32_t ip = *(uint32_t *)src; - int ip_len = 0; + while (len-- && not_whitespace(*src)) + *dst++ = *src++; - if (ip != 0 && ip != -1) { - ip_len = gendotquad(dst, *(uint32_t *)src); - dst += ip_len; - } - src += 4; - return stpcpy(dst, src); + *dst = '\0'; } /* @@ -552,7 +490,6 @@ static void fill_buffer(struct open_file_t *file) } #endif - /* * Start by ACKing the previous packet; this should cause * the next packet to be sent. @@ -683,22 +620,8 @@ static uint32_t pxe_getfssec(struct file *gfile, char *buf, } return bytes_read; - } - - - -/* - * Fill the packet tail with the tftp informations then retures the lenght - */ -static int fill_tail(char *dst) -{ - static const char tail[] = "octet\0""tsize\0""0\0""blksize\0""1408"; - - memcpy(dst, tail, sizeof tail); - return sizeof tail; } - /** * Open a TFTP connection to the server * @@ -708,16 +631,20 @@ static int fill_tail(char *dst) * @ouT: the lenght of this file, stores in file->file_len * */ -static void pxe_searchdir(char *filename, struct file *file) +static void pxe_searchdir(const char *filename, struct file *file) { - char *buf = packet_buf; - char *p = filename; + struct fs_info *fs = file->fs; + char *buf; + const char *np; + char *p; char *options; char *data; struct open_file_t *open_file; static __lowmem struct s_PXENV_UDP_WRITE udp_write; static __lowmem struct s_PXENV_UDP_READ udp_read; static __lowmem struct s_PXENV_FILE_OPEN file_open; + static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408"; + static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail]; const struct tftp_options *tftp_opt; int i = 0; int err; @@ -728,68 +655,102 @@ static void pxe_searchdir(char *filename, struct file *file) uint16_t tid; uint16_t opcode; uint16_t blk_num; - uint32_t ip; + uint32_t ip = 0; uint32_t opdata, *opdata_ptr; + enum pxe_path_type path_type; + char fullpath[2*FILENAME_MAX]; - open_file = allocate_socket(); - if (!open_file) { - file->file_len = 0; - file->open_file = NULL; - return; + file->file_len = 0; + file->open_file = NULL; + + buf = rrq_packet_buf; + *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ + buf += 2; + + path_type = pxe_path_type(filename); + if (path_type == PXE_RELATIVE) { + snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename); + path_type = pxe_path_type(filename = fullpath); } - timeout_ptr = TimeoutTable; /* Reset timeout */ + switch (path_type) { + case PXE_RELATIVE: /* Really shouldn't happen... */ + case PXE_URL: + buf = stpcpy(buf, filename); + ip = server_ip; /* Default server */ + break; - sendreq: - udp_write.buffer.offs = OFFS_WRT(buf, 0); - udp_write.buffer.seg = 0; - *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */ - buf += 2; + case PXE_HOMESERVER: + buf = stpcpy(buf, filename+2); + ip = server_ip; + break; - ip = *(uint32_t *)p; /* ip <- server override (if any) */ - p += 4; - if (ip == 0) { - /* Have prefix */ - strcpy(buf, path_prefix); - buf += strlen(path_prefix); - ip = server_ip; /* Get the default server */ + case PXE_TFTP: + np = strchr(filename, ':'); + buf = stpcpy(buf, np+2); + if (parse_dotquad(filename, &ip) != np) + ip = dns_resolv(filename); + break; } - strcpy(buf, p); /* Copy the filename */ - buf += strlen(p) + 1; /* advance the pointer, null char included */ + if (!ip) + return; /* No server */ + buf++; /* Point *past* the final NULL */ + memcpy(buf, rrq_tail, sizeof rrq_tail); + buf += sizeof rrq_tail; + + open_file = allocate_socket(); + if (!open_file) + return; /* Allocation failure */ + + timeout_ptr = TimeoutTable; /* Reset timeout */ + + sendreq: #if GPXE - if (is_gpxe(packet_buf + 2)) { - file_open.Status = PXENV_STATUS_BAD_FUNC; - file_open.FileName.offs = OFFS(packet_buf + 2); - file_open.FileName.seg = SEG(packet_buf + 2); - err = pxe_call(PXENV_FILE_OPEN, &file_open); - if (err) - goto done; - - open_file->tftp_localport = -1; - open_file->tftp_remoteport = file_open.FileHandle; - open_file->tftp_filesize = -1; - goto done; + if (path_type == PXE_URL) { + if (has_gpxe) { + file_open.Status = PXENV_STATUS_BAD_FUNC; + file_open.FileName.offs = OFFS(rrq_packet_buf + 2); + file_open.FileName.seg = SEG(rrq_packet_buf + 2); + err = pxe_call(PXENV_FILE_OPEN, &file_open); + if (err) + goto done; + + open_file->tftp_localport = -1; + open_file->tftp_remoteport = file_open.FileHandle; + open_file->tftp_filesize = -1; + goto done; + } else { + static bool already = false; + if (!already) { + fputs("URL syntax, but gPXE extensions not detected, " + "tryng plain TFTP...\n", stdout); + already = true; + } + } } #endif /* GPXE */ open_file->tftp_remoteip = ip; tid = open_file->tftp_localport; /* TID(local port No) */ + udp_write.buffer.offs = OFFS(rrq_packet_buf); + udp_write.buffer.seg = SEG(rrq_packet_buf); udp_write.ip = ip; udp_write.gw = ((udp_write.ip ^ MyIP) & net_mask) ? gate_way : 0; udp_write.src_port = tid; udp_write.dst_port = server_port; - buf += fill_tail(buf); - udp_write.buffer_size = buf - packet_buf; + udp_write.buffer_size = buf - rrq_packet_buf; err = pxe_call(PXENV_UDP_WRITE, &udp_write); - if (err || udp_write.status != 0) - goto failure; /* - * In fact, the 'failure' target will not do - * a failure thing; it will move on to the - * next timeout, then tries again until - * _real_ time out - */ + if (err || udp_write.status != 0) { + /* + * In fact, the 'failure' target will not do + * a failure thing; it will move on to the + * next timeout, then tries again until + * _real_ time out + */ + goto failure; + } /* * Danger, Will Robinson! We need to support tiemout @@ -888,7 +849,7 @@ static void pxe_searchdir(char *filename, struct file *file) p = options; while (buffersize) { - char *opt = p; + const char *opt = p; /* * If we find an option which starts with a NUL byte, @@ -957,8 +918,6 @@ static void pxe_searchdir(char *filename, struct file *file) done: if (!open_file->tftp_filesize) { free_socket(open_file); - file->file_len = 0; - file->open_file = NULL; return; } file->open_file = (void *)open_file; @@ -987,32 +946,61 @@ static void get_prefix(void) char *p; char c; - if (DHCPMagic & 0x04) /* Did we get a path prefix option */ - goto got_prefix; + if (!(DHCPMagic & 0x04)) { + /* No path prefix option, derive from boot file */ + + strlcpy(path_prefix, boot_file, sizeof path_prefix); + len = strlen(path_prefix); + p = &path_prefix[len - 1]; + + while (len--) { + c = *p--; + c |= 0x20; + + c = (c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c == '.' || c == '-'); + if (!c) + break; + }; + + if (len < 0) + p --; + + *(p + 2) = 0; /* Zero-terminate after delimiter */ + } - strcpy(path_prefix, boot_file); - len = strlen(path_prefix); - p = &path_prefix[len - 1]; + printf("TFTP prefix: %s\n", path_prefix); + chdir(path_prefix); +} - while (len--) { - c = *p--; - c |= 0x20; +/* + * realpath for PXE + */ +static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src, + size_t bufsize) +{ + enum pxe_path_type path_type = pxe_path_type(src); - c = (c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - (c == '.' || c == '-'); - if (!c) - break; - }; + return snprintf(dst, bufsize, "%s%s", + path_type == PXE_RELATIVE ? fs->cwd_name : "", src); +} - if (len < 0) - p --; +/* + * chdir for PXE + */ +static int pxe_chdir(struct fs_info *fs, const char *src) +{ + /* The cwd for PXE is just a text prefix */ + enum pxe_path_type path_type = pxe_path_type(src); - *(p + 2) = 0; /* Zero-terminate after delimiter */ + if (path_type == PXE_RELATIVE) + strlcat(fs->cwd_name, src, sizeof fs->cwd_name); + else + strlcpy(fs->cwd_name, src, sizeof fs->cwd_name); - got_prefix: - printf("TFTP prefix: %s\n", path_prefix); - strcpy(CurrentDirName, path_prefix); + printf("cwd = \"%s\"\n", fs->cwd_name); + return 0; } /* @@ -1501,6 +1489,9 @@ static int pxe_fs_init(struct fs_info *fs) /* Initialize network-card-specific idle handling */ pxe_idle_init(); + /* Our name for the root */ + strcpy(fs->cwd_name, "::"); + return 0; } @@ -1645,17 +1636,16 @@ cant_free: return; } - - const struct fs_ops pxe_fs_ops = { .fs_name = "pxe", .fs_flags = FS_NODEV, .fs_init = pxe_fs_init, .searchdir = pxe_searchdir, + .chdir = pxe_chdir, + .realpath = pxe_realpath, .getfssec = pxe_getfssec, .close_file = pxe_close_file, .mangle_name = pxe_mangle_name, - .unmangle_name = pxe_unmangle_name, + .unmangle_name = stpcpy, .load_config = pxe_load_config, - .iget_current = NULL }; diff --git a/core/getc.inc b/core/getc.inc index 48b9f774..47dca1e6 100644 --- a/core/getc.inc +++ b/core/getc.inc @@ -62,7 +62,7 @@ getc_file_lg2 equ 4 ; Size of getc_file as a power of 2 ; global core_open core_open: - pm_call searchdir + pm_call pm_searchdir jz openfd.ret openfd: push bx diff --git a/core/include/fs.h b/core/include/fs.h index 2477e4e3..124a8161 100644 --- a/core/include/fs.h +++ b/core/include/fs.h @@ -21,6 +21,8 @@ #define FILENAME_MAX_LG2 8 #define FILENAME_MAX (1 << FILENAME_MAX_LG2) +#define CURRENTDIR_MAX FILENAME_MAX + #define BLOCK_SIZE(fs) ((fs)->block_size) #define BLOCK_SHIFT(fs) ((fs)->block_shift) #define SECTOR_SIZE(fs) ((fs)->sector_size) @@ -32,6 +34,8 @@ struct fs_info { void *fs_info; /* The fs-specific information */ int sector_shift, sector_size; int block_shift, block_size; + struct inode *cwd; /* Current directory */ + char cwd_name[CURRENTDIR_MAX]; /* Current directory by name */ }; extern struct fs_info *this_fs; @@ -50,15 +54,16 @@ struct fs_ops { enum fs_flags fs_flags; int (*fs_init)(struct fs_info *); - void (*searchdir)(char *, struct file *); + void (*searchdir)(const char *, struct file *); uint32_t (*getfssec)(struct file *, char *, int, bool *); void (*close_file)(struct file *); void (*mangle_name)(char *, const char *); char * (*unmangle_name)(char *, const char *); + size_t (*realpath)(struct fs_info *, char *, const char *, size_t); + int (*chdir)(struct fs_info *, const char *); int (*load_config)(void); struct inode * (*iget_root)(struct fs_info *); - struct inode * (*iget_current)(struct fs_info *); struct inode * (*iget)(char *, struct inode *); char * (*follow_symlink)(struct inode *, const char *); @@ -90,6 +95,7 @@ struct open_file_t; struct file { struct fs_info *fs; + uint32_t file_len; union { /* For the new universal-path_lookup */ struct { @@ -100,7 +106,6 @@ struct file { /* For the old searhdir method */ struct { struct open_file_t *open_file;/* The fs-specific open file struct */ - uint32_t file_len; }; }; }; @@ -109,12 +114,6 @@ struct file { enum dev_type {CHS, EDD}; /* - * Generic functions that filesystem drivers may choose to use - */ -void generic_mangle_name(char *, const char *); -#define generic_unmangle_name stpcpy - -/* * Struct device contains: * the pointer points to the disk structure, * the cache stuff. @@ -153,13 +152,39 @@ static inline void malloc_error(char *obj) kaboom(); } -/* - * functions +/* + * File handle conversion functions */ +extern struct file files[]; +static inline uint16_t file_to_handle(struct file *file) +{ + return file ? (file - files)+1 : 0; +} +static inline struct file *handle_to_file(uint16_t handle) +{ + return handle ? &files[handle-1] : NULL; +} + +/* fs.c */ void mangle_name(com32sys_t *); -void searchdir(com32sys_t *); +void pm_searchdir(com32sys_t *); +int searchdir(const char *name); void _close_file(struct file *); -inline uint16_t file_to_handle(struct file *); -inline struct file *handle_to_file(uint16_t); + +/* chdir.c */ +void pm_realpath(com32sys_t *regs); +size_t realpath(char *dst, const char *src, size_t bufsize); +int chdir(const char *src); + +/* + * Generic functions that filesystem drivers may choose to use + */ + +/* mangle.c */ +void generic_mangle_name(char *, const char *); +#define generic_unmangle_name stpcpy + +/* loadconfig.c */ +int generic_load_config(void); #endif /* FS_H */ diff --git a/core/parseconfig.inc b/core/parseconfig.inc index af7d514f..ad89f58d 100644 --- a/core/parseconfig.inc +++ b/core/parseconfig.inc @@ -148,7 +148,7 @@ pc_filecmd: push ax ; Function to tailcall call pc_getline mov di,MNameBuf pm_call mangle_name - pm_call searchdir + pm_call pm_searchdir jnz .ok pop ax ; Drop the successor function .ok: ret ; Tailcall if OK, error return diff --git a/core/pxelinux.asm b/core/pxelinux.asm index 58f76ce9..8884e030 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -30,7 +30,7 @@ ; my_id equ pxelinux_id NULLFILE equ 0 ; Zero byte == null file name -NULLOFFSET equ 4 ; Position in which to look +NULLOFFSET equ 0 ; Position in which to look REBOOT_TIME equ 5*60 ; If failure, time until full reset %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block) diff --git a/core/dir.c b/core/readdir.c index 4013280a..7a58edac 100644 --- a/core/dir.c +++ b/core/readdir.c @@ -12,7 +12,7 @@ void opendir(com32sys_t *regs) char *src = MK_PTR(regs->es, regs->esi.w[0]); char *dst = MK_PTR(regs->ds, regs->edi.w[0]); strcpy(dst, src); - searchdir(regs); + pm_searchdir(regs); regs->eax.l = (uint32_t)handle_to_file(regs->esi.w[0]); } diff --git a/core/runkernel.inc b/core/runkernel.inc index 74f23c45..f07c70f6 100644 --- a/core/runkernel.inc +++ b/core/runkernel.inc @@ -597,7 +597,7 @@ loadinitrd: sub di,InitRDCName mov [InitRDCNameLen],di mov di,InitRD - pm_call searchdir ; Look for it in directory + pm_call pm_searchdir ; Look for it in directory pop edi jz .notthere diff --git a/core/ui.inc b/core/ui.inc index ec9190a9..827710f2 100644 --- a/core/ui.inc +++ b/core/ui.inc @@ -451,7 +451,7 @@ vk_check: ; Find the kernel on disk ; get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/extension - mov di,KernelName+4*IS_PXELINUX + mov di,KernelName cmp byte [di],' ' jbe bad_kernel ; Missing kernel name xor al,al @@ -463,7 +463,7 @@ get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/e mov bx,exten_table .search_loop: push bx mov di,KernelName ; Search on disk - pm_call searchdir + pm_call pm_searchdir pop bx jnz kernel_good mov eax,[bx] ; Try a different extension @@ -628,7 +628,7 @@ kernel_good: push di push ax - mov di,KernelName+4*IS_PXELINUX + mov di,KernelName xor al,al mov cx,FILENAME_MAX repne scasb @@ -674,11 +674,11 @@ is_unknown_filetype: jmp is_linux_kernel is_config_file: - pusha + push si mov si,KernelCName ; Save the config file name, for posterity mov di,ConfigName - call strcpy - popa + pm_call pm_realpath + pop si call openfd call reset_config jmp load_config_file |