Bug 895905 - guestmount: link() incorrectly returns ENOENT, when it should be EXDEV
Summary: guestmount: link() incorrectly returns ENOENT, when it should be EXDEV
Keywords:
Status: CLOSED UPSTREAM
Alias: None
Product: Virtualization Tools
Classification: Community
Component: libguestfs
Version: unspecified
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Richard W.M. Jones
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks: 892291
TreeView+ depends on / blocked
 
Reported: 2013-01-16 09:06 UTC by Richard W.M. Jones
Modified: 2013-01-21 14:46 UTC (History)
8 users (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of: 892291
Environment:
Last Closed: 2013-01-21 14:46:13 UTC
Embargoed:


Attachments (Terms of Use)
test script (471 bytes, text/plain)
2013-01-21 13:15 UTC, Richard W.M. Jones
no flags Details
API-level link test (729 bytes, text/plain)
2013-01-21 13:22 UTC, Richard W.M. Jones
no flags Details

Description Richard W.M. Jones 2013-01-16 09:06:17 UTC
+++ This bug was initially created as a clone of Bug #892291 +++

Description of problem:

My project ostree uses hardlinks extensively; I have a bit of code which tries link() and falls back to a copy if it gets EXDEV.

Unfortunately, something in the guestmount/fuse/kernel chain is giving me ENOENT, when it should be EXDEV.  I've added a workaround to ostree, but this is suboptimal.

Version-Release number of selected component (if applicable):

libguestfs-1.16.34-2.el6.x86_64
fuse-2.8.3-4.el6.x86_64
kernel-2.6.32-350.el6.x86_64


How reproducible:

Always.

Steps to Reproduce:
1. guestmount --rw -o allow_root --pid-file guestmount.pid -a test.img -m /dev/sda3 -m /dev/sda1:/boot mnt
2. cd mnt
3. touch foo
4. ln foo boot/foo
 
Actual results:

ENOENT

Expected results:

EXDEV

--- Additional comment from RHEL Product and Program Management on 2013-01-06 06:13:09 EST ---

Since this bug report was entered in bugzilla, the release flag has been
set to ? to ensure that it is properly evaluated for this release.

--- Additional comment from RHEL Product and Program Management on 2013-01-11 01:47:31 EST ---

This request was not resolved in time for the current release.
Red Hat invites you to ask your support representative to
propose this request, if still desired, for consideration in
the next release of Red Hat Enterprise Linux.

Comment 1 Richard W.M. Jones 2013-01-16 09:07:26 UTC
Cloning this bug to check whether it happens upstream too.

Comment 2 Richard W.M. Jones 2013-01-21 13:15:11 UTC
Created attachment 684279 [details]
test script

Simple self-contained test script demonstrating the problem.

The output is:

  [...]
  + mkdir -p /tmp/mnt
  + guestmount -a test1.img -m /dev/sda1:/ -m /dev/sda2:/boot /tmp/mnt
  + touch /tmp/mnt/foo
  + cd /tmp/mnt
  + ln foo boot/foo
  ln: failed to create hard link ‘boot/foo’ => ‘foo’: No such file or directory

(The error message should be "Invalid cross-device link")

Comment 3 Richard W.M. Jones 2013-01-21 13:22:50 UTC
Created attachment 684284 [details]
API-level link test

This is a test script to see what the guestfs_ln API
returns.

Note that the current implementation of guestfs_ln runs
the external 'ln' binary.  This has two obvious problems:

(a) it's slow

(b) it doesn't capture errno

There's no obvious reason why we could not replace this
impl with a direct call to link(2).

Comment 4 Richard W.M. Jones 2013-01-21 14:46:13 UTC
Two commits are needed to fix this fully:

https://github.com/libguestfs/libguestfs/commit/533082e28240522df51e6615d99794d726c8e1f0

https://github.com/libguestfs/libguestfs/commit/28e34290ff0dc11e9c5d8b8a1e5992cd0cc941fb

The first commit changes guestfs_ln to use link(2) directly
instead of the external 'ln' program, ensuring that we can
capture the errno.  That commit on its own does fix the whole
bug (including guestmount), but ...

When I looked closely at the code to guestmount to try to work
out why ENOENT was being returned (ENOENT appears nowhere along
any relevant codepaths), I realized there is a second bug in
guestmount which affects lots of things.  The current guestmount
code returns errnos to FUSE by just doing:

  return -guestfs_last_errno(g);

That works, in the cases where the previous guestfs_* API call
captured the errno.  In other cases, guestfs_last_errno returns
0, and thus 0 (no error) is passed back to FUSE.

Ooops.

What was in fact happening in the ln-over-guestmount case was that
the link call was *succeeding*, but a subsequent open was failing
with ENOENT.  The second commit fixes this.


Note You need to log in before you can comment on or make changes to this bug.