kern/92040: mmap/cp fails on small file in UDF
Giorgos Keramidas
keramida at freebsd.org
Sun Jan 22 19:20:14 PST 2006
The following reply was made to PR kern/92040; it has been noted by GNATS.
From: Giorgos Keramidas <keramida at freebsd.org>
To: Nate Eldredge <nge at cs.hmc.edu>
Cc: bug-followup at FreeBSD.org
Subject: Re: kern/92040: mmap/cp fails on small file in UDF
Date: Mon, 23 Jan 2006 05:15:47 +0200
On 2006-01-19 22:34, Nate Eldredge <nge at cs.hmc.edu> wrote:
> If you try to copy a small file from a UDF filesystem, cp fails with EINVAL.
>
> The underlying cause is that cp attempts to copy small files using mmap,
> and mmap fails in this case. I don't know whether cp is calling it wrong,
> or mmap is simply unimplemented for udf, or if there is some other reason
> for the failure.
>
> In any case, it is probably a good idea for cp to fall back to read() if
> mmap doesn't work. It might also be useful to be able to explicitly
> tell it to use read(). For example, I don't know what the mmap approach
> will do in the presence of I/O errors. I'm worried that it might silently
> make a corrupt copy of the file.
>
> This is on amd64.
>
> Perhaps this bug should be cross-filed in category bin, if that is possible.
> I don't know how to do that.
>
> >How-To-Repeat:
> nate at vulcan:~/bugs/udf$ mkdir dir
> nate at vulcan:~/bugs/udf$ cat >dir/hello
> hello world
> nate at vulcan:~/bugs/udf$ mkisofs -o img -udf dir/
> ...
> vulcan# mdconfig -a -t vnode -f img
> md0
> vulcan# mount_udf /dev/md0 /mnt/md0
> vulcan# ls -l /mnt/md0
> total 0
> -r--r--r-- 1 root wheel 12 Jan 20 22:10 hello
> vulcan# cat /mnt/md0/hello
> hello world
> vulcan# cp /mnt/md0/hello .
> cp: /mnt/md0/hello: Invalid argument
The following patch adds a `retry' variable in utils.c, which is set by
default to true. If mmap() succeeds, then `retry' is reset to false, to
avoid copying some blocks with mmap() and others with read/write.
It seems to work fine here...
$ mount | grep /mnt
/dev/md1 on /mnt (udf, local, read-only)
$ ls -l /mnt
total 0
-r--r--r-- 1 root wheel - 12 Jan 24 05:03 hello
$ ./cp /mnt/hello .
$ ls -ld hello
-r--r--r-- 1 keramida keramida - 12 Jan 23 05:12 hello
$ cmp /mnt/hello hello ; echo $?
0
$
Removing the need for rval = 1 when mmap() fails, means we can revert
the condition of the following if statement and reduce the indentation
level too:
%%%
Index: utils.c
===================================================================
RCS file: /home/ncvs/src/bin/cp/utils.c,v
retrieving revision 1.46
diff -u -r1.46 utils.c
--- utils.c 5 Sep 2005 04:36:08 -0000 1.46
+++ utils.c 23 Jan 2006 03:09:33 -0000
@@ -61,7 +61,7 @@
{
static char buf[MAXBSIZE];
struct stat *fs;
- int ch, checkch, from_fd, rcount, rval, to_fd;
+ int ch, checkch, from_fd, rcount, retry, rval, to_fd;
ssize_t wcount;
size_t wresid;
size_t wtotal;
@@ -125,6 +125,7 @@
}
rval = 0;
+ retry = 1; /* Retry copying if mmap() fails. */
/*
* Mmap and write if less than 8M (the limit is so we don't totally
@@ -133,41 +134,37 @@
*/
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
- fs->st_size <= 8 * 1048576) {
- if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
- MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
+ fs->st_size <= 8 * 1048576 &&
+ (p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
+ MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
+ retry = 0;
+ wtotal = 0;
+ for (bufp = p, wresid = fs->st_size; ;
+ bufp += wcount, wresid -= (size_t)wcount) {
+ wcount = write(to_fd, bufp, wresid);
+ wtotal += wcount;
+ if (info) {
+ info = 0;
+ (void)fprintf(stderr,
+ "%s -> %s %3d%%\n",
+ entp->fts_path, to.p_path,
+ cp_pct(wtotal, fs->st_size));
+ }
+ if (wcount >= (ssize_t)wresid || wcount <= 0)
+ break;
+ }
+ if (wcount != (ssize_t)wresid) {
+ warn("%s", to.p_path);
+ rval = 1;
+ }
+ /* Some systems don't unmap on close(2). */
+ if (munmap(p, fs->st_size) < 0) {
warn("%s", entp->fts_path);
rval = 1;
- } else {
- wtotal = 0;
- for (bufp = p, wresid = fs->st_size; ;
- bufp += wcount, wresid -= (size_t)wcount) {
- wcount = write(to_fd, bufp, wresid);
- wtotal += wcount;
- if (info) {
- info = 0;
- (void)fprintf(stderr,
- "%s -> %s %3d%%\n",
- entp->fts_path, to.p_path,
- cp_pct(wtotal, fs->st_size));
-
- }
- if (wcount >= (ssize_t)wresid || wcount <= 0)
- break;
- }
- if (wcount != (ssize_t)wresid) {
- warn("%s", to.p_path);
- rval = 1;
- }
- /* Some systems don't unmap on close(2). */
- if (munmap(p, fs->st_size) < 0) {
- warn("%s", entp->fts_path);
- rval = 1;
- }
}
- } else
+ }
#endif
- {
+ if (retry != 0) {
wtotal = 0;
while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
for (bufp = buf, wresid = rcount; ;
%%%
More information about the freebsd-bugs
mailing list