Hi,
I just figured FreeBSD will happily write data (disk blocks) from byte
#0 even after successful lseek(fd, 17, SEEK_SET) returned 17, at least
on da(4) partitions such as rda0s1f. Tested on 4.9-PRERELEASE checked
out early Sep 5th.
I'd suggest that lseek to a non-sector boundary returns either (off_t)-1
and EINVAL (preferred) or at least returns the actual position (though
few applications will handle that properly), or that write() returns -1
and EINVAL when writing to such a non-aligned location.
Try this DESTRUCTIVE test on a scratch or spare partition that may lose
all its data.
Note that some devices are halfway fine; vn0c lets the seek succeed but
returns EINVAL for the subsequent write, so make sure to try this on a
da partition (comment out your swap partition, reboot, and use your
freshly-deactivated swap partition if need be).
NIT PICK: write(2) EINVAL means "The pointer associated with d was
negative." (d is the file descriptor)
Expected behaviour: either abort with "Invalid argument" on lseek bs/2
or on write or print "OK".
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/disklabel.h>
void barf(const char *t) __attribute__((noreturn));
void barf(const char *t) { perror(t); exit(EXIT_FAILURE); }
int main(int argc, char **argv) {
int bs, fd = open(argc > 1 ? argv[1] : "/dev/rda0s1f",
O_RDWR);
int want = 0x66, t;
struct disklabel dl;
char *x;
if (fd < 0) barf("open");
if (0 == ioctl(fd, DIOCGDINFO, &dl)) {
printf("sector size: %lu\n", (unsigned long)(bs =
dl.d_secsize))
;
} else barf("ioctl DIOCGDINFO");
/* this writes bs = blocksize times the byte 0x66 at offset #0
* then seeks to offset #bs/2 then writes bs times the byte 0x33. */
if (!(x = malloc(bs))) barf("malloc");
memset(x, want, bs);
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) barf("lseek 0");
if (write(fd, x, bs) < bs) barf("write");
memset(x, 0x33, bs);
t = lseek(fd, bs >> 1, SEEK_SET);
if (t == -1) barf("lseek bs/2");
printf("seeked to byte #%d\n", t);
if (write(fd, x, bs) < bs) barf("write");
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) barf("lseek 0");
if (read(fd, x, bs) < bs) barf("read");
if (x[0] == want) puts("OK");
else printf("KERNEL BUG: byte #0 is %x, should be %x\n", x[0],
want);
free(x);
return 0;
}
--
Matthias Andree
Encrypt your mail: my GnuPG key ID is 0x052E7D95