easy way to determine if a stream or fd is seekable
Alexander Best
arundel at freebsd.org
Wed Nov 16 13:17:02 UTC 2011
On Wed Nov 16 11, Alexander Best wrote:
> On Wed Nov 16 11, Alexander Best wrote:
> > 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.
>
> hmmm...the patch doesn't seem to work for directories and symbolic links
> unfortunately. does anybody spot the problem?
>
> i've attached two testing applications. mode.c will do the tests via the S_IS*
> macros and requires the patch i've posted beforehand to stat.h. the other
> application is seekable.c, which will use lseek(2) to test, whether a file
> argument is seekable or not. basically both applications should report the
> same!
grrrr...the attachments got scrubbed!
>
> cheers.
>
> >
> > cheers.
> > alex
> >
> > >
> > > cheers.
> > > alex
> > >
> > > >
> > > > Need to think about this more before commenting further...
> > > >
> > > > -Brandon
>
> > 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 */
>
-------------- next part --------------
#include <sys/types.h>
#include <sys/stat.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int m;
int bflag, iscflag, isdflag, isfflag, islflag, isregflag, issflag, isseflag;
int iswflag;
bflag = iscflag = isdflag = isfflag = islflag = isregflag = isseflag = 0;
issflag = iswflag = 0;
if (argc != 2) {
fprintf(stderr, "usage: mode filename\n");
exit(1);
}
struct stat *test_stat;
test_stat = malloc(sizeof(test_stat));
m = lstat(argv[1], test_stat);
if (m == -1)
err(1, "lstat failed");
if(S_ISBLK(test_stat->st_mode))
bflag = 1;
if(S_ISCHR(test_stat->st_mode))
iscflag = 1;
if(S_ISDIR(test_stat->st_mode))
isdflag = 1;
if(S_ISFIFO(test_stat->st_mode))
isfflag = 1;
if(S_ISLNK(test_stat->st_mode))
islflag = 1;
if(S_ISREG(test_stat->st_mode))
isregflag = 1;
if(S_ISSOCK(test_stat->st_mode))
issflag = 1;
if(S_ISWHT(test_stat->st_mode))
iswflag = 1;
if(S_ISSEEK(test_stat->st_mode))
isseflag = 1;
printf("Block special file: %d\n"
"Character special file: %d\n"
"Directory: %d\n"
"Pipe or FIFO special file: %d\n"
"Symbolic link: %d\n"
"Regular file: %d\n"
"Socket: %d\n"
"Whiteout: %d\n"
"Seekable: %d\n"
"st_mode: %u\n",
bflag, iscflag, isdflag, isfflag, islflag, isregflag, issflag,
iswflag, isseflag, test_stat->st_mode);
exit(0);
}
-------------- next part --------------
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv)
{
int fd;
if (argc != 2) {
fprintf(stderr, "usage: seekable filename\n");
exit(1);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
err(1, "open failed");
if (lseek(fd, 0, SEEK_CUR) != -1)
printf ("%d is seekable.\n", fd);
else
printf ("%d is not seekable.\n", fd);
exit(0);
}
More information about the freebsd-hackers
mailing list