[syslinux:pathbased] fs: move to a chdir()-based mechanism for managing cwd

syslinux-bot for H. Peter Anvin hpa at zytor.com
Sat Feb 13 23:48:05 PST 2010


Commit-ID:  3399f97a70cdca0cb05129ea3fb5e9dd42c35d9c
Gitweb:     http://syslinux.zytor.com/commit/3399f97a70cdca0cb05129ea3fb5e9dd42c35d9c
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sat, 13 Feb 2010 23:42:59 -0800
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Sat, 13 Feb 2010 23:45:02 -0800

fs: move to a chdir()-based mechanism for managing cwd

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 at zytor.com>


---
 core/chdir.c              |   66 ++++++++
 core/comboot.inc          |    4 +-
 core/extern.inc           |    7 +-
 core/fs.c                 |  104 ++++++-------
 core/fs/ext2/ext2.c       |   18 +--
 core/fs/fat/fat.c         |   55 +++----
 core/fs/iso9660/iso9660.c |   30 ++--
 core/fs/lib/loadconfig.c  |   21 +++
 core/fs/pxe/dhcp_option.c |    2 +-
 core/fs/pxe/dnsresolv.c   |    1 +
 core/fs/pxe/pxe.c         |  382 ++++++++++++++++++++++-----------------------
 core/getc.inc             |    2 +-
 core/include/fs.h         |   53 +++++--
 core/parseconfig.inc      |    2 +-
 core/pxelinux.asm         |    2 +-
 core/{dir.c => readdir.c} |    2 +-
 core/runkernel.inc        |    2 +-
 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 0000000..c5c4d58
--- /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 03507c8..e271b9e 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 95a9c88..da6c675 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
diff --git a/core/fs.c b/core/fs.c
index ecd5257..6acfd3b 100644
--- a/core/fs.c
+++ b/core/fs.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 9cdbb3b..c794300 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(&regs, 0, sizeof regs);
-    snprintf(ConfigName, FILENAME_MAX, "%s/extlinux.conf", CurrentDirName);
-    regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
-    call16(core_open, &regs, &regs);
-
-    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 c388dba..97dd715 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(&regs, 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, &regs, &regs);
-	return regs.eflags.l & EFLAGS_ZF;
-    }
-    /* installed by syslinux */
-    for (; i < 3; i++) {
-        strcpy(ConfigName, syslinux_cfg[i]);
-        call16(core_open, &regs, &regs);
+    /* 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(&regs, 0, sizeof regs);
+	    snprintf(ConfigName, FILENAME_MAX, "%s/syslinux.cfg",
+		     search_directories[i]);
+	    regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+	    call16(core_open, &regs, &regs);
+	    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 e691164..06caedd 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(&regs, 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, &regs, &regs);
 	    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 0000000..da2ba50
--- /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(&regs, 0, sizeof regs);
+    snprintf(ConfigName, FILENAME_MAX, "%s/extlinux.conf", CurrentDirName);
+    regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+    call16(core_open, &regs, &regs);
+
+    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 0787078..150290a 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 1556044..18cfc48 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 ea26ef6..56f8ee4 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 48b9f77..47dca1e 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 2477e4e..124a816 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 af7d514..ad89f58 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 58f76ce..8884e03 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
similarity index 97%
rename from core/dir.c
rename to core/readdir.c
index 4013280..7a58eda 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 74f23c4..f07c70f 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 ec9190a..827710f 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



More information about the Syslinux-commits mailing list