Hello,

Here is a fix for the issue. Independent on what fs partition
contains, ufs_ls() was called. Because of ext2 and ufs similarity it
worked successfully in some cases.

netbsd_boot2_ls_fix.diff
    Fix ls command used in second stage bootloader.

    Currently only ufs_ls is supported. But if ls is called on ext2
    partition, it hangs bootloader, because ufs_ls() is called.
    ls command should always call fs-dependent XXX_ls().

netbsd_boot2_ext2_ls
    Add ls support to libsa/ext2fs (used by boot2).

Could someone please review and commit it?


On Thu, Dec 8, 2011 at 7:35 PM, Izumi Tsutsui <tsut...@ceres.dti.ne.jp> wrote:
>> If my ext2 partition is hd2e then when I do
>>   dev hd2e:
>>   ls
>> Boot loader hangs.
>>
>> >From man page it's not clear if it should support ext2 (as any other
>> FS, because there are dosboot, cdboot, etc), but there is some code
>> and SUPPORT_EXT2FS for boot2.
>
> The bootloader can load files from ext2fs, but
> currently ls command supports only ufs.
> /boot calls src/sys/lib/libsa/ufs_ls.c for ls command:
> http://nxr.NetBSD.org/xref/src/sys/arch/i386/stand/boot/boot2.c#406
>
>> But it shouldn't hang anyway.
>
> Yes, please file a PR.
>
> ---
> Izumi Tsutsui



-- 
Evgeniy Ivanov
diff --git a/sys/arch/i386/stand/boot/boot2.c b/sys/arch/i386/stand/boot/boot2.c
index 587990e..dc50808 100644
--- a/sys/arch/i386/stand/boot/boot2.c
+++ b/sys/arch/i386/stand/boot/boot2.c
@@ -409,7 +409,7 @@ command_ls(char *arg)
 	const char *save = default_filename;
 
 	default_filename = "/";
-	ufs_ls(arg);
+	ls(arg);
 	default_filename = save;
 }
 
diff --git a/sys/arch/i386/stand/bootxx/Makefile.bootxx b/sys/arch/i386/stand/bootxx/Makefile.bootxx
index ab80745..fd8508c 100644
--- a/sys/arch/i386/stand/bootxx/Makefile.bootxx
+++ b/sys/arch/i386/stand/bootxx/Makefile.bootxx
@@ -87,7 +87,8 @@ CPPFLAGS+= -DLIBSA_SINGLE_FILESYSTEM=xxfs \
 		-D"blkdevioctl(x,y,z)=EINVAL" \
 		-D"blkdevclose(f)=0" \
 		-D"devopen(f,n,fl)=(*(fl)=(void *)n,0)" \
-		-DLIBSA_NO_DISKLABEL_MSGS
+		-DLIBSA_NO_DISKLABEL_MSGS \
+		-DLIBSA_NO_LS_OP
 
 # -DLIBSA_FS_SINGLECOMPONENT 
 
diff --git a/sys/arch/i386/stand/dosboot/main.c b/sys/arch/i386/stand/dosboot/main.c
index 87054b0..9aa3d2a 100644
--- a/sys/arch/i386/stand/dosboot/main.c
+++ b/sys/arch/i386/stand/dosboot/main.c
@@ -328,7 +328,7 @@ command_ls(char *arg)
 		return;
 	}
 	default_filename = "/";
-	ufs_ls(arg);
+	ls(arg);
 	default_filename = help;
 }
 
diff --git a/sys/arch/i386/stand/libsa/nfs.c b/sys/arch/i386/stand/libsa/nfs.c
index 18d1bd1..03244d6 100644
--- a/sys/arch/i386/stand/libsa/nfs.c
+++ b/sys/arch/i386/stand/libsa/nfs.c
@@ -630,3 +630,12 @@ nfs_stat(struct open_file *f, struct stat *sb)
 
 	return (0);
 }
+
+__compactcall void
+nfs_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by nfs\n");
+#endif
+	return;
+}
diff --git a/sys/lib/libsa/Makefile b/sys/lib/libsa/Makefile
index da59909..557887f 100644
--- a/sys/lib/libsa/Makefile
+++ b/sys/lib/libsa/Makefile
@@ -40,7 +40,7 @@ SRCS+=	bcopy.c bzero.c	# Remove me eventually.
 
 # io routines
 SRCS+=	closeall.c dev.c disklabel.c dkcksum.c ioctl.c nullfs.c stat.c fstat.c
-SRCS+=	close.c lseek.c open.c read.c write.c
+SRCS+=	close.c ls.c lseek.c open.c read.c write.c
 .if (${SA_USE_CREAD} == "yes")
 CPPFLAGS+= -D__INTERNAL_LIBSA_CREAD
 SRCS+=	cread.c
@@ -65,7 +65,7 @@ SRCS+=	bootp.c rarp.c bootparam.c
 SRCS+=	nfs.c tftp.c
 .endif
 
-SRCS+=	ffsv1.c ffsv2.c ufs_ls.c
+SRCS+=	ffsv1.c ffsv2.c
 SRCS+=	lfsv1.c lfsv2.c
 SRCS+=	cd9660.c
 SRCS+=	ustarfs.c
diff --git a/sys/lib/libsa/cd9660.c b/sys/lib/libsa/cd9660.c
index 4ebb91a..aab3411 100644
--- a/sys/lib/libsa/cd9660.c
+++ b/sys/lib/libsa/cd9660.c
@@ -396,3 +396,12 @@ cd9660_stat(struct open_file *f, struct stat *sb)
 	sb->st_size = fp->size;
 	return 0;
 }
+
+__compactcall void
+cd9660_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by cd9660\n");
+#endif
+	return;
+}
diff --git a/sys/lib/libsa/dosfs.c b/sys/lib/libsa/dosfs.c
index 2dab9be..86b4635 100644
--- a/sys/lib/libsa/dosfs.c
+++ b/sys/lib/libsa/dosfs.c
@@ -405,6 +405,15 @@ dosfs_stat(struct open_file *fd, struct stat *sb)
 	return 0;
 }
 
+__compactcall void
+dosfs_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by dosfs\n");
+#endif
+	return;
+}
+
 /*
  * Parse DOS boot sector
  */
diff --git a/sys/lib/libsa/ext2fs.c b/sys/lib/libsa/ext2fs.c
index 3f08190..1e06f31 100644
--- a/sys/lib/libsa/ext2fs.c
+++ b/sys/lib/libsa/ext2fs.c
@@ -146,6 +146,59 @@ struct file {
 	daddr_t		f_buf_blkno;	/* block number of data block */
 };
 
+#if !defined(LIBSA_NO_LS_OP)
+
+#define NELEM(x) (sizeof (x) / sizeof(*x))
+
+typedef struct entry_t entry_t;
+struct entry_t {
+	entry_t	*e_next;
+	ino32_t	e_ino;
+	uint8_t	e_type;
+	char	e_name[1];
+};
+
+static const char    *const typestr[] = {
+	"unknown",
+	"REG",
+	"DIR",
+	"CHR",
+	"BLK",
+	"FIFO",
+	"SOCK",
+	"LNK"
+};
+
+static int
+fn_match(const char *fname, const char *pattern)
+{
+	char fc, pc;
+
+	do {
+		fc = *fname++;
+		pc = *pattern++;
+		if (!fc && !pc)
+			return 1;
+		if (pc == '?' && fc)
+			pc = fc;
+	} while (fc == pc);
+
+	if (pc != '*')
+		return 0;
+	/*
+	 * Too hard (and unnecessary really) too check for "*?name" etc....
+	 * "**" will look for a '*' and "*?" a '?'
+	 */
+	pc = *pattern++;
+	if (!pc)
+		return 1;
+	while ((fname = strchr(fname, pc)))
+		if (fn_match(++fname, pattern))
+			return 1;
+	return 0;
+}
+#endif /* LIBSA_NO_LS_OP */
+
 static int read_inode(ino32_t, struct open_file *);
 static int block_map(struct open_file *, indp_t, indp_t *);
 static int buf_read_file(struct open_file *, char **, size_t *);
@@ -803,6 +856,97 @@ ext2fs_stat(struct open_file *f, struct stat *sb)
 	return 0;
 }
 
+__compactcall void
+ext2fs_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	struct file *fp = (struct file *)f->f_fsdata;
+	size_t block_size = fp->f_fs->e2fs_bsize;
+	char *buf;
+	size_t buf_size;
+	entry_t	*names = 0, *n, **np;
+
+	fp->f_seekp = 0;
+	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
+		struct ext2fs_direct  *dp, *edp;
+		int rc = buf_read_file(f, &buf, &buf_size);
+		if (rc)
+			goto out;
+		if (buf_size != block_size || buf_size == 0)
+			goto out;
+
+		dp = (struct ext2fs_direct *)buf;
+		edp = (struct ext2fs_direct *)(buf + buf_size);
+
+		for (; dp < edp;
+		     dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
+			const char *t;
+
+			if (fs2h16(dp->e2d_reclen) <= 0)
+				goto out;
+
+			if (fs2h32(dp->e2d_ino) == 0)
+				continue;
+
+			if (dp->e2d_type >= NELEM(typestr) ||
+			    !(t = typestr[dp->e2d_type])) {
+				/*
+				 * This does not handle "old"
+				 * filesystems properly. On little
+				 * endian machines, we get a bogus
+				 * type name if the namlen matches a
+				 * valid type identifier. We could
+				 * check if we read namlen "0" and
+				 * handle this case specially, if
+				 * there were a pressing need...
+				 */
+				printf("bad dir entry\n");
+				goto out;
+			}
+			if (pattern && !fn_match(dp->e2d_name, pattern))
+				continue;
+			n = alloc(sizeof *n + strlen(dp->e2d_name));
+			if (!n) {
+				printf("%d: %s (%s)\n",
+					fs2h32(dp->e2d_ino), dp->e2d_name, t);
+				continue;
+			}
+			n->e_ino = fs2h32(dp->e2d_ino);
+			n->e_type = dp->e2d_type;
+			strcpy(n->e_name, dp->e2d_name);
+			for (np = &names; *np; np = &(*np)->e_next) {
+				if (strcmp(n->e_name, (*np)->e_name) < 0)
+					break;
+			}
+			n->e_next = *np;
+			*np = n;
+		}
+		fp->f_seekp += buf_size;
+	}
+
+	if (names) {
+		entry_t *p_names = names;
+		do {
+			n = p_names;
+			printf("%d: %s (%s)\n",
+				n->e_ino, n->e_name, typestr[n->e_type]);
+			p_names = n->e_next;
+		} while (p_names);
+	} else {
+		printf("not found\n");
+	}
+out:
+	if (names) {
+		do {
+			n = names;
+			names = n->e_next;
+			dealloc(n, 0);
+		} while (names);
+	}
+#endif
+	return;
+}
+
 /*
  * byte swap functions for big endian machines
  * (ext2fs is always little endian)
diff --git a/sys/lib/libsa/ffsv1.c b/sys/lib/libsa/ffsv1.c
index 182c997..ea4300f 100644
--- a/sys/lib/libsa/ffsv1.c
+++ b/sys/lib/libsa/ffsv1.c
@@ -8,6 +8,7 @@
 #define ufs_write	ffsv1_write
 #define ufs_seek	ffsv1_seek
 #define ufs_stat	ffsv1_stat
+#define ufs_ls		ffsv1_ls
 
 #define ufs_dinode	ufs1_dinode
 #define indp_t		int32_t
diff --git a/sys/lib/libsa/ffsv2.c b/sys/lib/libsa/ffsv2.c
index 2f712f5..589ecf3 100644
--- a/sys/lib/libsa/ffsv2.c
+++ b/sys/lib/libsa/ffsv2.c
@@ -8,6 +8,7 @@
 #define ufs_write	ffsv2_write
 #define ufs_seek	ffsv2_seek
 #define ufs_stat	ffsv2_stat
+#define ufs_ls		ffsv2_ls
 
 #define ufs_dinode	ufs2_dinode
 #define indp_t		int64_t
diff --git a/sys/lib/libsa/lfsv1.c b/sys/lib/libsa/lfsv1.c
index 69ed855..e6f811e 100644
--- a/sys/lib/libsa/lfsv1.c
+++ b/sys/lib/libsa/lfsv1.c
@@ -9,6 +9,7 @@
 #define	ufs_write		lfsv1_write
 #define	ufs_seek		lfsv1_seek
 #define	ufs_stat		lfsv1_stat
+#define	ufs_ls			lfsv1_ls
 
 #define	fs_bsize		lfs_ibsize
 #define	IFILE_Vx		IFILE_V1
diff --git a/sys/lib/libsa/lfsv2.c b/sys/lib/libsa/lfsv2.c
index fe28990..dfbbd0b 100644
--- a/sys/lib/libsa/lfsv2.c
+++ b/sys/lib/libsa/lfsv2.c
@@ -9,6 +9,7 @@
 #define	ufs_write		lfsv2_write
 #define	ufs_seek		lfsv2_seek
 #define	ufs_stat		lfsv2_stat
+#define	ufs_ls			lfsv2_ls
 
 #define	fs_bsize		lfs_bsize
 #define	IFILE_Vx		IFILE
diff --git a/sys/lib/libsa/ls.c b/sys/lib/libsa/ls.c
new file mode 100644
index 0000000..542848f
--- /dev/null
+++ b/sys/lib/libsa/ls.c
@@ -0,0 +1,157 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2011
+ *      The NetBSD Foundation, Inc. All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1996
+ *	Matthias Drochner.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "stand.h"
+#include <sys/stat.h>
+#include <lib/libkern/libkern.h>
+
+void
+ls(const char *path)
+{
+	int             fd;
+	struct stat     sb;
+	size_t          size;
+	const char	*fname = 0;
+	char		*p;
+	struct open_file *f;
+
+	if ((fd = open(path, 0)) < 0
+	    || fstat(fd, &sb) < 0
+	    || (sb.st_mode & S_IFMT) != S_IFDIR) {
+		/* Path supplied isn't a directory, open parent
+		   directory and list matching files. */
+		if (fd >= 0)
+			close(fd);
+		fname = strrchr(path, '/');
+		if (fname) {
+			size = fname - path;
+			p = alloc(size + 1);
+			if (!p)
+				goto out;
+			memcpy(p, path, size);
+			p[size] = 0;
+			fd = open(p, 0);
+			dealloc(p, size + 1);
+		} else {
+			fd = open("", 0);
+			fname = path;
+		}
+
+		if (fd < 0) {
+			printf("ls: %s\n", strerror(errno));
+			return;
+		}
+		if (fstat(fd, &sb) < 0) {
+			printf("stat: %s\n", strerror(errno));
+			goto out;
+		}
+		if ((sb.st_mode & S_IFMT) != S_IFDIR) {
+			printf("%s: %s\n", path, strerror(ENOTDIR));
+			goto out;
+		}
+	}
+
+	f = &files[fd];
+
+#if !defined(LIBSA_NO_FD_CHECKING)
+	if ((unsigned int)fd >= SOPEN_MAX || f->f_flags == 0) {
+		errno = EBADF;
+		goto out;
+	}
+#endif
+
+#if !defined(LIBSA_NO_RAW_ACCESS)
+	/* operation not defined on raw devices */
+	if (f->f_flags & F_RAW) {
+		errno = EOPNOTSUPP;
+		goto out;
+	}
+#endif
+
+	FS_LS(f->f_ops)(f, fname);
+
+out:
+	close(fd);
+}
diff --git a/sys/lib/libsa/nfs.c b/sys/lib/libsa/nfs.c
index a3935ff..7964bfd 100644
--- a/sys/lib/libsa/nfs.c
+++ b/sys/lib/libsa/nfs.c
@@ -655,3 +655,12 @@ nfs_stat(struct open_file *f, struct stat *sb)
 
 	return 0;
 }
+
+__compactcall void
+nfs_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by nfs\n");
+#endif
+	return;
+}
diff --git a/sys/lib/libsa/nullfs.c b/sys/lib/libsa/nullfs.c
index bc23260..c29a79f 100644
--- a/sys/lib/libsa/nullfs.c
+++ b/sys/lib/libsa/nullfs.c
@@ -113,3 +113,12 @@ null_stat(struct open_file *f, struct stat *sb)
 
 	return EIO;
 }
+
+__compactcall void
+null_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by nullfs\n");
+#endif
+	return;
+}
diff --git a/sys/lib/libsa/stand.h b/sys/lib/libsa/stand.h
index f1af4ce..7d218c6 100644
--- a/sys/lib/libsa/stand.h
+++ b/sys/lib/libsa/stand.h
@@ -95,7 +95,8 @@ struct open_file;
 	extern __compactcall int	__CONCAT(fs,_write)(struct open_file *, void *, \
 						size_t, size_t *); \
 	extern __compactcall off_t	__CONCAT(fs,_seek)(struct open_file *, off_t, int); \
-	extern __compactcall int	__CONCAT(fs,_stat)(struct open_file *, struct stat *)
+	extern __compactcall int	__CONCAT(fs,_stat)(struct open_file *, struct stat *); \
+	extern __compactcall void	__CONCAT(fs,_ls)(struct open_file *, const char *);
 
 /*
  * This structure is used to define file system operations in a file system
@@ -112,6 +113,7 @@ struct fs_ops {
 	__compactcall int	(*write)(struct open_file *, void *, size_t size, size_t *);
 	__compactcall off_t	(*seek)(struct open_file *, off_t, int);
 	__compactcall int	(*stat)(struct open_file *, struct stat *);
+	__compactcall void	(*ls)(struct open_file *, const char *);
 };
 
 extern struct fs_ops file_system[];
@@ -123,7 +125,8 @@ extern int nfsys;
 	__CONCAT(fs,_read), \
 	__CONCAT(fs,_write), \
 	__CONCAT(fs,_seek), \
-	__CONCAT(fs,_stat) }
+	__CONCAT(fs,_stat), \
+	__CONCAT(fs,_ls) }
 
 #define	FS_OPEN(fs)		((fs)->open)
 #define	FS_CLOSE(fs)		((fs)->close)
@@ -131,6 +134,7 @@ extern int nfsys;
 #define	FS_WRITE(fs)		((fs)->write)
 #define	FS_SEEK(fs)		((fs)->seek)
 #define	FS_STAT(fs)		((fs)->stat)
+#define	FS_LS(fs)		((fs)->ls)
 
 #else
 
@@ -140,6 +144,7 @@ extern int nfsys;
 #define	FS_WRITE(fs)		___CONCAT(LIBSA_SINGLE_FILESYSTEM,_write)
 #define	FS_SEEK(fs)		___CONCAT(LIBSA_SINGLE_FILESYSTEM,_seek)
 #define	FS_STAT(fs)		___CONCAT(LIBSA_SINGLE_FILESYSTEM,_stat)
+#define	FS_LS(fs)		___CONCAT(LIBSA_SINGLE_FILESYSTEM,_ls)
 
 FS_DEF(LIBSA_SINGLE_FILESYSTEM);
 
@@ -256,6 +261,7 @@ off_t	lseek(int, off_t, int);
 int	ioctl(int, u_long, char *);
 int	stat(const char *, struct stat *);
 int	fstat(int, struct stat *);
+void	ls(const char *);
 
 typedef int cmp_t(const void *, const void *);
 void	qsort(void *, size_t, size_t, cmp_t *);
diff --git a/sys/lib/libsa/tftp.c b/sys/lib/libsa/tftp.c
index 17bad29..7902647 100644
--- a/sys/lib/libsa/tftp.c
+++ b/sys/lib/libsa/tftp.c
@@ -429,6 +429,15 @@ tftp_stat(struct open_file *f, struct stat *sb)
 	return 0;
 }
 
+__compactcall void
+tftp_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by tftp\n");
+#endif
+	return;
+}
+
 __compactcall off_t
 tftp_seek(struct open_file *f, off_t offset, int where)
 {
diff --git a/sys/lib/libsa/ufs.c b/sys/lib/libsa/ufs.c
index 473100d..22c1493 100644
--- a/sys/lib/libsa/ufs.c
+++ b/sys/lib/libsa/ufs.c
@@ -179,6 +179,66 @@ static void ffs_oldfscompat(struct fs *);
 static int ffs_find_superblock(struct open_file *, struct fs *);
 #endif
 
+#if !defined(LIBSA_NO_LS_OP)
+
+#define NELEM(x) (sizeof (x) / sizeof(*x))
+
+typedef struct entry_t entry_t;
+struct entry_t {
+	entry_t	*e_next;
+	ino32_t	e_ino;
+	uint8_t	e_type;
+	char	e_name[1];
+};
+
+static const char    *const typestr[] = {
+	"unknown",
+	"FIFO",
+	"CHR",
+	0,
+	"DIR",
+	0,
+	"BLK",
+	0,
+	"REG",
+	0,
+	"LNK",
+	0,
+	"SOCK",
+	0,
+	"WHT"
+};
+
+static int
+fn_match(const char *fname, const char *pattern)
+{
+	char fc, pc;
+
+	do {
+		fc = *fname++;
+		pc = *pattern++;
+		if (!fc && !pc)
+			return 1;
+		if (pc == '?' && fc)
+			pc = fc;
+	} while (fc == pc);
+
+	if (pc != '*')
+		return 0;
+	/*
+	 * Too hard (and unnecessary really) too check for "*?name" etc....
+	 * "**" will look for a '*' and "*?" a '?'
+	 */
+	pc = *pattern++;
+	if (!pc)
+		return 1;
+	while ((fname = strchr(fname, pc)))
+		if (fn_match(++fname, pattern))
+			return 1;
+	return 0;
+}
+#endif /* LIBSA_NO_LS_OP */
+
 #ifdef LIBSA_LFS
 /*
  * Find an inode's block.  Look it up in the ifile.  Whee!
@@ -852,6 +912,90 @@ ufs_stat(struct open_file *f, struct stat *sb)
 	return 0;
 }
 
+__compactcall void
+ufs_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	struct file *fp = (struct file *)f->f_fsdata;
+	char *buf;
+	size_t buf_size;
+	entry_t	*names = 0, *n, **np;
+
+	fp->f_seekp = 0;
+	while (fp->f_seekp < (off_t)fp->f_di.di_size) {
+		struct direct  *dp, *edp;
+		int rc = buf_read_file(f, &buf, &buf_size);
+		if (rc)
+			goto out;
+		if (buf_size != DIRBLKSIZ || buf_size == 0)
+			goto out;
+
+		dp = (struct direct *)buf;
+		edp = (struct direct *)(buf + buf_size);
+
+		for (; dp < edp; dp = (void *)((char *)dp + dp->d_reclen)) {
+			const char *t;
+			if (dp->d_ino ==  0)
+				continue;
+
+			if (dp->d_type >= NELEM(typestr) ||
+			    !(t = typestr[dp->d_type])) {
+				/*
+				 * This does not handle "old"
+				 * filesystems properly. On little
+				 * endian machines, we get a bogus
+				 * type name if the namlen matches a
+				 * valid type identifier. We could
+				 * check if we read namlen "0" and
+				 * handle this case specially, if
+				 * there were a pressing need...
+				 */
+				printf("bad dir entry\n");
+				goto out;
+			}
+			if (pattern && !fn_match(dp->d_name, pattern))
+				continue;
+			n = alloc(sizeof *n + strlen(dp->d_name));
+			if (!n) {
+				printf("%d: %s (%s)\n",
+					dp->d_ino, dp->d_name, t);
+				continue;
+			}
+			n->e_ino = dp->d_ino;
+			n->e_type = dp->d_type;
+			strcpy(n->e_name, dp->d_name);
+			for (np = &names; *np; np = &(*np)->e_next) {
+				if (strcmp(n->e_name, (*np)->e_name) < 0)
+					break;
+			}
+			n->e_next = *np;
+			*np = n;
+		}
+		fp->f_seekp += buf_size;
+	}
+
+	if (names) {
+		entry_t *p_names = names;
+		do {
+			n = p_names;
+			printf("%d: %s (%s)\n",
+				n->e_ino, n->e_name, typestr[n->e_type]);
+			p_names = n->e_next;
+		} while (p_names);
+	} else {
+		printf("not found\n");
+	}
+out:
+	if (names) {
+		do {
+			n = names;
+			names = n->e_next;
+			dealloc(n, 0);
+		} while (names);
+	}
+#endif /* LIBSA_NO_LS_OP */
+}
+
 #ifdef LIBSA_FFSv1
 /*
  * Sanity checks for old file systems.
diff --git a/sys/lib/libsa/ufs.h b/sys/lib/libsa/ufs.h
index aa458bf..4340f08 100644
--- a/sys/lib/libsa/ufs.h
+++ b/sys/lib/libsa/ufs.h
@@ -34,5 +34,3 @@
 FS_DEF(ufs);
 FS_DEF(ffsv1);
 FS_DEF(ffsv2);
-
-void ufs_ls(const char *);
diff --git a/sys/lib/libsa/ufs_ls.c b/sys/lib/libsa/ufs_ls.c
deleted file mode 100644
index 55e5b78..0000000
--- a/sys/lib/libsa/ufs_ls.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*	$NetBSD: ufs_ls.c,v 1.14 2007/11/24 13:20:58 isaki Exp $	 */
-
-/*
- * Copyright (c) 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Copyright (c) 1996
- *	Matthias Drochner.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-#include <sys/param.h>
-#include <lib/libkern/libkern.h>
-#include <ufs/ufs/dinode.h>
-#include <ufs/ufs/dir.h>
-
-#include "stand.h"
-#include "ufs.h"
-
-#define NELEM(x) (sizeof (x) / sizeof(*x))
-
-
-typedef uint32_t ino32_t;
-typedef struct entry_t entry_t;
-struct entry_t {
-	entry_t	*e_next;
-	ino32_t	e_ino;
-	uint8_t	e_type;
-	char	e_name[1];
-};
-
-static const char    *const typestr[] = {
-	"unknown",
-	"FIFO",
-	"CHR",
-	0,
-	"DIR",
-	0,
-	"BLK",
-	0,
-	"REG",
-	0,
-	"LNK",
-	0,
-	"SOCK",
-	0,
-	"WHT"
-};
-
-static int
-fn_match(const char *fname, const char *pattern)
-{
-	char fc, pc;
-
-	do {
-		fc = *fname++;
-		pc = *pattern++;
-		if (!fc && !pc)
-			return 1;
-		if (pc == '?' && fc)
-			pc = fc;
-	} while (fc == pc);
-
-	if (pc != '*')
-		return 0;
-	/*
-	 * Too hard (and unnecessary really) too check for "*?name" etc....
-	 * "**" will look for a '*' and "*?" a '?'
-	 */
-	pc = *pattern++;
-	if (!pc)
-		return 1;
-	while ((fname = strchr(fname, pc)))
-		if (fn_match(++fname, pattern))
-			return 1;
-	return 0;
-}
-
-void
-ufs_ls(const char *path)
-{
-	int             fd;
-	struct stat     sb;
-	size_t          size;
-	char            dirbuf[DIRBLKSIZ];
-	const char	*fname = 0;
-	char		*p;
-	entry_t		*names = 0, *n, **np;
-
-	if ((fd = open(path, 0)) < 0
-	    || fstat(fd, &sb) < 0
-	    || (sb.st_mode & IFMT) != IFDIR) {
-		/* Path supplied isn't a directory, open parent
-		   directory and list matching files. */
-		if (fd >= 0)
-			close(fd);
-		fname = strrchr(path, '/');
-		if (fname) {
-			size = fname - path;
-			p = alloc(size + 1);
-			if (!p)
-				goto out;
-			memcpy(p, path, size);
-			p[size] = 0;
-			fd = open(p, 0);
-			dealloc(p, size + 1);
-		} else {
-			fd = open("", 0);
-			fname = path;
-		}
-
-		if (fd < 0) {
-			printf("ls: %s\n", strerror(errno));
-			return;
-		}
-		if (fstat(fd, &sb) < 0) {
-			printf("stat: %s\n", strerror(errno));
-			goto out;
-		}
-		if ((sb.st_mode & IFMT) != IFDIR) {
-			printf("%s: %s\n", path, strerror(ENOTDIR));
-			goto out;
-		}
-	}
-
-	while ((size = read(fd, dirbuf, DIRBLKSIZ)) == DIRBLKSIZ) {
-		struct direct  *dp, *edp;
-
-		dp = (struct direct *)dirbuf;
-		edp = (struct direct *)(dirbuf + size);
-
-		for (; dp < edp; dp = (void *)((char *)dp + dp->d_reclen)) {
-			const char *t;
-			if (dp->d_ino ==  0)
-				continue;
-
-			if (dp->d_type >= NELEM(typestr) ||
-			    !(t = typestr[dp->d_type])) {
-				/*
-				 * This does not handle "old"
-				 * filesystems properly. On little
-				 * endian machines, we get a bogus
-				 * type name if the namlen matches a
-				 * valid type identifier. We could
-				 * check if we read namlen "0" and
-				 * handle this case specially, if
-				 * there were a pressing need...
-				 */
-				printf("bad dir entry\n");
-				goto out;
-			}
-			if (fname && !fn_match(dp->d_name, fname))
-				continue;
-			n = alloc(sizeof *n + strlen(dp->d_name));
-			if (!n) {
-				printf("%d: %s (%s)\n",
-					dp->d_ino, dp->d_name, t);
-				continue;
-			}
-			n->e_ino = dp->d_ino;
-			n->e_type = dp->d_type;
-			strcpy(n->e_name, dp->d_name);
-			for (np = &names; *np; np = &(*np)->e_next) {
-				if (strcmp(n->e_name, (*np)->e_name) < 0)
-					break;
-			}
-			n->e_next = *np;
-			*np = n;
-		}
-	}
-
-	if (names) {
-		do {
-			n = names;
-			printf("%d: %s (%s)\n",
-				n->e_ino, n->e_name, typestr[n->e_type]);
-			names = n->e_next;
-			dealloc(n, 0);
-		} while (names);
-	} else {
-		printf( "%s not found\n", path );
-	}
-out:
-	close(fd);
-}
diff --git a/sys/lib/libsa/ustarfs.c b/sys/lib/libsa/ustarfs.c
index cd41bd2..c066ad1 100644
--- a/sys/lib/libsa/ustarfs.c
+++ b/sys/lib/libsa/ustarfs.c
@@ -537,6 +537,16 @@ ustarfs_stat(struct open_file *f, struct stat *sb)
 	return 0;
 }
 
+
+__compactcall void
+ustarfs_ls(struct open_file *f, const char *pattern)
+{
+#if !defined(LIBSA_NO_LS_OP)
+	printf("Currently ls command is unsupported by ustarfs\n");
+#endif
+	return;
+}
+
 #ifndef LIBSA_NO_FS_CLOSE
 __compactcall int
 ustarfs_close(struct open_file *f)
diff --git a/sys/lib/libsa/ext2fs.c b/sys/lib/libsa/ext2fs.c
index e267d2b..1e06f31 100644
--- a/sys/lib/libsa/ext2fs.c
+++ b/sys/lib/libsa/ext2fs.c
@@ -146,6 +146,59 @@ struct file {
 	daddr_t		f_buf_blkno;	/* block number of data block */
 };
 
+#if !defined(LIBSA_NO_LS_OP)
+
+#define NELEM(x) (sizeof (x) / sizeof(*x))
+
+typedef struct entry_t entry_t;
+struct entry_t {
+	entry_t	*e_next;
+	ino32_t	e_ino;
+	uint8_t	e_type;
+	char	e_name[1];
+};
+
+static const char    *const typestr[] = {
+	"unknown",
+	"REG",
+	"DIR",
+	"CHR",
+	"BLK",
+	"FIFO",
+	"SOCK",
+	"LNK"
+};
+
+static int
+fn_match(const char *fname, const char *pattern)
+{
+	char fc, pc;
+
+	do {
+		fc = *fname++;
+		pc = *pattern++;
+		if (!fc && !pc)
+			return 1;
+		if (pc == '?' && fc)
+			pc = fc;
+	} while (fc == pc);
+
+	if (pc != '*')
+		return 0;
+	/*
+	 * Too hard (and unnecessary really) too check for "*?name" etc....
+	 * "**" will look for a '*' and "*?" a '?'
+	 */
+	pc = *pattern++;
+	if (!pc)
+		return 1;
+	while ((fname = strchr(fname, pc)))
+		if (fn_match(++fname, pattern))
+			return 1;
+	return 0;
+}
+#endif /* LIBSA_NO_LS_OP */
+
 static int read_inode(ino32_t, struct open_file *);
 static int block_map(struct open_file *, indp_t, indp_t *);
 static int buf_read_file(struct open_file *, char **, size_t *);
@@ -807,7 +860,89 @@ __compactcall void
 ext2fs_ls(struct open_file *f, const char *pattern)
 {
 #if !defined(LIBSA_NO_LS_OP)
-	printf("Currently ls command is unsupported by ext2fs\n");
+	struct file *fp = (struct file *)f->f_fsdata;
+	size_t block_size = fp->f_fs->e2fs_bsize;
+	char *buf;
+	size_t buf_size;
+	entry_t	*names = 0, *n, **np;
+
+	fp->f_seekp = 0;
+	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
+		struct ext2fs_direct  *dp, *edp;
+		int rc = buf_read_file(f, &buf, &buf_size);
+		if (rc)
+			goto out;
+		if (buf_size != block_size || buf_size == 0)
+			goto out;
+
+		dp = (struct ext2fs_direct *)buf;
+		edp = (struct ext2fs_direct *)(buf + buf_size);
+
+		for (; dp < edp;
+		     dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
+			const char *t;
+
+			if (fs2h16(dp->e2d_reclen) <= 0)
+				goto out;
+
+			if (fs2h32(dp->e2d_ino) == 0)
+				continue;
+
+			if (dp->e2d_type >= NELEM(typestr) ||
+			    !(t = typestr[dp->e2d_type])) {
+				/*
+				 * This does not handle "old"
+				 * filesystems properly. On little
+				 * endian machines, we get a bogus
+				 * type name if the namlen matches a
+				 * valid type identifier. We could
+				 * check if we read namlen "0" and
+				 * handle this case specially, if
+				 * there were a pressing need...
+				 */
+				printf("bad dir entry\n");
+				goto out;
+			}
+			if (pattern && !fn_match(dp->e2d_name, pattern))
+				continue;
+			n = alloc(sizeof *n + strlen(dp->e2d_name));
+			if (!n) {
+				printf("%d: %s (%s)\n",
+					fs2h32(dp->e2d_ino), dp->e2d_name, t);
+				continue;
+			}
+			n->e_ino = fs2h32(dp->e2d_ino);
+			n->e_type = dp->e2d_type;
+			strcpy(n->e_name, dp->e2d_name);
+			for (np = &names; *np; np = &(*np)->e_next) {
+				if (strcmp(n->e_name, (*np)->e_name) < 0)
+					break;
+			}
+			n->e_next = *np;
+			*np = n;
+		}
+		fp->f_seekp += buf_size;
+	}
+
+	if (names) {
+		entry_t *p_names = names;
+		do {
+			n = p_names;
+			printf("%d: %s (%s)\n",
+				n->e_ino, n->e_name, typestr[n->e_type]);
+			p_names = n->e_next;
+		} while (p_names);
+	} else {
+		printf("not found\n");
+	}
+out:
+	if (names) {
+		do {
+			n = names;
+			names = n->e_next;
+			dealloc(n, 0);
+		} while (names);
+	}
 #endif
 	return;
 }

Reply via email to