easy way to determine if a stream or fd is seekable

Alexander Best arundel at freebsd.org
Wed Nov 16 10:31:44 UTC 2011


On Wed Nov 16 11, Alexander Best wrote:
> On Tue Nov 15 11, Brandon Gooch wrote:
> > On Nov 15, 2011 2:25 PM, "Alexander Best" <arundel at freebsd.org> wrote:
> > >
> > > hi there,
> > >
> > > one of the things i'm missing is an easy way to determine, whether a
> > stream or
> > > fd is seekable. i checked the dd(1) and hd(1) sources and those tools are
> > > performing so much stuff just to find out if this is the case, and they
> > still
> > > are doing a very poor job.
> > >
> > > dd(1) e.g. identifies /dev/zero as non-seekable, even though it is. the
> > result:
> > >
> > > `dd if=/dev/zero bs=1m count=1 skip=100000`:
> > >
> > > on freebsd:
> > > 57,41 real         0,05 user        43,21 sys
> > >
> > > on openbsd:
> > > 0,88 real         0,00 user         0,07 sys
> > >
> > > on freebsd dd(1) is very easy fixable (1 line diff). the point however is:
> > >
> > > there doesn't seem to exist a unified way of checking seekable == TRUE. so
> > > every userspace application seems to do it differently and most of them
> > (dd(1)
> > > and hd(1) e.g) aren't doing it right. hd(1) e.g. believes that only
> > regular
> > > files are seekable. this means that hd(1) fired at /dev/ada* with a high
> > skip
> > > value takes ages! dd(1) is a bit smarter in this case, but still not
> > correct.
> > >
> > > idealy there would be something like stat.st_mode (see stat(2)) where one
> > > could simply do a S_ISSEEK(m). so far the best and easiest solution i've
> > seen
> > > is:
> > >
> > > fd = open(argv[1], O_RDONLY);
> > > if (fd == -1)
> > >   exit(1);
> > > if (lseek(fd, 0, SEEK_CUR) != -1)
> > >    printf ("%d is seekable.\n", fd);
> > > else
> > >    printf ("%d is not seekable.\n", fd);
> > >
> > > seeing an application iterate through a stream or fd via getchar(),
> > although
> > > a simple seek() would work is very frustrating. i think what would be
> > needed
> > > is a simple function or macro that userspace applications can make use of.
> > > maybe such a thing already exists in linux, openbsd, netbsd, dragonflybsd,
> > > solaris or plan9?
> > >
> > > cheers.
> > > alex
> > >
> > > references:
> > > [1]
> > http://www.mavetju.org/mail/view_thread.php?list=freebsd-hackers&id=3290708&thread=yes
> > > [2] http://www.freebsd.org/cgi/query-pr.cgi?pr=152485
> > > [3] http://www.freebsd.org/cgi/query-pr.cgi?pr=86485
> > 
> > So, according to APUE 2nd Edition, seek should be supported on anything
> > that's not a pipe, FIFO, or socket.  In fact, the "test for seekability"
> > you've provided above closely resembles the example given in that text.
> 
> if this really is the case, things could even be easier in a freebsd specific
> manor than the code above:
> 
> !S_ISFIFO(m) && !S_ISSOCK(m)
> 
> this means it would be trivial to write a new macro S_ISSEEK which would test
> stat.st_mode against the according bits.

here's a patch, which also fixes a comment.

cheers.
alex

> 
> cheers.
> alex
> 
> > 
> > Need to think about this more before commenting further...
> > 
> > -Brandon
-------------- next part --------------
diff --git a/sys/sys/stat.h b/sys/sys/stat.h
index 1b03bd2..ab5ae44 100644
--- a/sys/sys/stat.h
+++ b/sys/sys/stat.h
@@ -238,10 +238,11 @@ struct nstat {
 #define	S_ISCHR(m)	(((m) & 0170000) == 0020000)	/* char special */
 #define	S_ISBLK(m)	(((m) & 0170000) == 0060000)	/* block special */
 #define	S_ISREG(m)	(((m) & 0170000) == 0100000)	/* regular file */
-#define	S_ISFIFO(m)	(((m) & 0170000) == 0010000)	/* fifo or socket */
+#define	S_ISFIFO(m)	(((m) & 0170000) == 0010000)	/* pipe or fifo */
 #if __POSIX_VISIBLE >= 200112
 #define	S_ISLNK(m)	(((m) & 0170000) == 0120000)	/* symbolic link */
 #define	S_ISSOCK(m)	(((m) & 0170000) == 0140000)	/* socket */
+#define	S_ISSEEK(m)	(((m) & 0150000) == 0)		/* seekable file */
 #endif
 #if __BSD_VISIBLE
 #define	S_ISWHT(m)	(((m) & 0170000) == 0160000)	/* whiteout */


More information about the freebsd-hackers mailing list