This question was actually fun for learning more about
mount - and about the kernel code involved.  =:c)

Fred Crowson wrote on Mon, Oct 02, 2006 at 10:31:24AM +0200:
> Karel Kulhavy wrote:

>> I tried to mount a CD-ROM twice:
>> [EMAIL PROTECTED]:~$ mount /dev/cd0c /mnt/cdrom
>> mount_ffs: /dev/cd0c on /mnt/cdrom: \
>>   specified device does not match mounted device
>> [EMAIL PROTECTED]:~$ mount /dev/cd0c /mnt/cdrom

If i understand correctly, the second mount command succeeded.

>> In first time I got an error message which doesn't make sense -
>> there was only specified device, no mounted device (when you
>> invoke the mount with intention to mount a device, the device
>> is not mounted yet).

Yes, *hopefully*.

On the other hand, *if* you invoke mount(8) on a device which is
already mounted, specifying the -u (change status) flag, you *do*
get the same errno(2) in case you give the wrong mount point:

  [EMAIL PROTECTED] # mount | grep ftp 
  /dev/wd0l on /ftp type ffs (local, nodev, noexec)
  [EMAIL PROTECTED] # mount /dev/wd0l /mnt
  mount_ffs: /dev/wd0l on /mnt: Device busy
  [EMAIL PROTECTED] # mount -u /dev/wd0l /mnt
  mount_ffs: /dev/wd0l on /mnt: \
    specified device does not match mounted device

>> Man mount doesn't mention the error message.

When citing man pages, please do not omit the section number.
The error message is not explained in mount(8), but in mount(2):
  "[EINVAL] An argument given was invalid."
OK, i admit this is not at all obvious.

>> I suggest the error message to be added and explained in the
>> mount manual page.

Maybe the mount_ffs(8) man page could be improved; i will perhaps
think about it...

>> Could you please also tell me what the error
>> message means and why I got it once and not second time?

> The error message is explicit mount_ffs (mount a Berkeley Fast
> File System) does not match the file format on the CD-ROM -
> if you had used the -t switch to mount(8) or used mount_cd9660(8)
> you would have not received that error message.

In one respect, this is almost certainly correct - clearly, if
mount_ffs(8) is invoked on a cd9660 file system, it will fail
just like Karel reported.

But the point is - why did mount(8) invoke mount_ffs(8) at all?
Usually, mount(8) is able to autodetect cd9660 file systems.
It uses readlabelfs(3) from /usr/src/lib/libutil/readlabel.c
to accomplish that.  Thus, why did readlabelfs(3) fail the
first time, and why did it succeed the second time?

To find out, i just tried the following: I opened my CD drive,
put a CD into the tray, typed the command below, hit return and
only *then* pressed the "close tray" button on the CD drive.
The first time i tried, the message "specified device does not
match mounted device" did NOT show up.  So i unmounted and
removed the CD and started over.

Now look at the result i got from the second try:

  [EMAIL PROTECTED] # \                                            
  > ( while true; do
  >     mount /dev/cd0c /mnt && echo DONE && break
  >   done;
  >   mount /dev/cd0c /mnt
  > ) > mount.out 2>&1; \
  > tail -n 6 mount.out
  mount_ffs: /dev/cd0c on /mnt: Operation not supported by device
  mount_ffs: /dev/cd0c on /mnt: Operation not supported by device
  mount_ffs: /dev/cd0c on /mnt: Operation not supported by device
  mount_ffs: /dev/cd0c on /mnt: \
    specified device does not match mounted device
  DONE
  mount_cd9660: /dev/cd0c on /mnt: Device busy

I read this as follows: Before the CD is accessible,
readlabelfs(3) will fail, so mount(8) will use its default
which happens to be "-t ffs".  As soon as the CD becomes
accessible, readlabelfs(3) will of course return the correct
value "cd9660".

Before the CD is accessible, mount(2) called by mount_ffs(8)
will fail with ENODEV, translated to "Operation not supported by
device" by strerror(3).  As soon as the CD becomes accessible,
mount(2) will notice the file system type mismatch, returning
EINVAL, translated to "specified device does not match mounted
device" by mount_ffs(8).

Apparently, there is a race condition.  Consider the following
timeline:

 1. the user starts mount(8)
 2. mount(8) calls readlabelfs(3)
     *** ASSUME THE CD IS STILL UNAVAILABLE AT THIS TIME ***
 3. readlabelfs(3) returns NULL to mount(8)
    (note: if the CD would already be readable now, the return
     value would be "cd9660" and the mount would succeed)
 4. mount(8) does fork(2) and exec(3) to start mount_ffs(8)
 5. mount_ffs(8) calls mount(2) with option MOUNT_FFS
 6. switch to kernel space, sys_mount from kern/vfs_syscalls.c
 7. sys_mount uses copyinstr(9) to get fstypename from user space
 8. sys_mount calls ffs_mount from ufs/ffs/ffs_vfsops.c
 9. ffs_mount uses copyinstr(9) to get mount point and device name
10. ffs_mount calls ffs_mountfs from ufs/ffs/ffs_vfsops.c
     *** ASSUME THE CD HAS NOW BECOME AVAILABLE ***
11. ffs_mountfs successfully opens the cd(4) device for reading
    by calling VOP_OPEN from kern/vnode_if.c = cdopen from scsi/cd.c
12. ffs_mountfs searches for an ffs superblock on the CD -
    of course, there is none, so it returns EINVAL
13. EINVAL is handed up the chain ffs_mount -> sys_mount ->
    mount(2) -> mount_ffs(8); the latter fails saying
    "specified device does not match mounted device"

You see, there is quite a bit of work to be done between the time
when mount(8) checks the disklabel to autodetect the file system
type and the time when mount(2) can actually start accessing the
device.  In case the CD becomes readable during that time interval,
mount(8) was indeed out of luck.

By the way, mount(2) uses the EINVAL errno(2) in a catch-all
style, and the details may even differ for different mount_*(8)
backends.  EINVAL from mount(2) can mean a lot of different
things.  And concerning ENODEV, the situation is not much
better.

Yet, it does not appear to be an easy task to implement error
messages to distinguish more precisely among various reasons
for mount(8) to fail.  The point is, mount(2) receives failure
information from various different file system drivers and from
various different device drivers, and they all adhere to their
own standards for reporting errors.  Thus, it is hard for
mount(2) to make head or tail from that jungle of information
in the first place.  Still worse, even if mount(2) does indeed
unstand the gory details about what went wrong, it has no way
of telling mount(8) - the only way to pass error information
from mount(2) to mount(8) is via errno(2)...   :-(

While i was about it, i wondered where that other error
message, "Operation not supported by device", comes from.
In case the CD is still not readable at step 11 above,
this is what happens:

12. cdopen calls scsi_test_unit_ready from scsi/scsi_base.c
13. scsi_test_unit_ready calls scsi_scsi_cmd
14. scsi_scsi_cmd calls scsi_execute_xs
15. scsi_execute_xs actually asks the CD drive whether it is ready
16. scsi_execute_xs passes the answer to sc_err1
17. sc_err1 calls scsi_interpret_sense
18. scsi_interpret_sense finds the sense key
    SKEY_NOT_READY subcode 0x3a /* Medium not present */
    and *explicitely* decides to translate this into ENODEV

This is a typical example that the medium-level device
driver code, in this case the the SCSI base system, can indeed
tell *exactly* what went wrong.  But it cannot tell mount(2),
because mount(2) obviously cannot and should not handle SCSI
error codes - and even if sys_mount could handle the information,
it would have a hard time to pass it out of the kernel space
such that mount(8) could print it...

It would be nice if mount(8) could print
  "Medium not present"
in place of
  "Operation not supported by device"
and
  "No FFS superblock found"
in place of
  "specified device does not match mounted device".

The information is in there, deep inside the kernel,
but it cannot get out.

-- 
"Drums, drums in the deep.  They are coming.  We cannot get out!"
 -- From the Book of Mazarbul, found by Frodo in Moria 23rd hall

Reply via email to