24

If I do: touch file; mv file /dev/null as root, /dev/null disappears. ls -lad /dev/null results in no such file or directory. This breaks applications which depend on /dev/null like SSH and can be resolved by doing mknod /dev/null c 1 3; chmod 666 /dev/null. Why does moving a regular file to this special file result in the disappearance of /dev/null?

To clarify, this was for testing purposes, and I understand how the mv command works. What I am curious about is why ls -la /dev/null before replacing it with a regular file shows the expected output, but afterwards it shows that /dev/null does not exist even though a file was allegedly created via the original mv command and the file command shows ASCII Text. I think this must be a combination of the ls command behavior in conjunction with devfs when a non special file replaces a character/special file. This is on Mac OS X, behaviors may vary on other OS's.

Jonathon Reinhart
  • 1,821
  • 1
  • 16
  • 20
Gregg Leventhal
  • 7,480
  • 19
  • 65
  • 100
  • 46
    Don't mess with `/dev/null`. – devnull Mar 03 '14 at 17:15
  • 8
    so says @devnull – Braiam Mar 03 '14 at 17:19
  • 3
    The proper way to delete files is the `rm` command. – casey Mar 03 '14 at 17:20
  • I am not trying to delete anything, this was an experiment. The thing I am curious about is that replacing the file results in the file being missing when ls -la /dev/null is run. I am guessing this is due to the nature of devfs. – Gregg Leventhal Mar 03 '14 at 17:35
  • @Gregg, does a Mac use `devfs`? – Graeme Mar 03 '14 at 17:38
  • Yes df -h Greggs-MacBook-Air1:~ gleventhal$ df -h /dev Filesystem Size Used Avail Capacity iused ifree %iused Mounted on devfs 191Ki 191Ki 0Bi 100% 660 0 100% /dev – Gregg Leventhal Mar 03 '14 at 17:38
  • @Gregg, can you also create normal files on the OSX `/dev`, eg `touch /dev/testfile`. – Graeme Mar 03 '14 at 17:40
  • Just to make sure it's not specific to `ls`, run `find /dev -n name null` to test whether `find` can see it. I really doubt it will but may as well. – terdon Mar 03 '14 at 17:42
  • On a Mac, as root touch /dev/test results in operation not supported. – Gregg Leventhal Mar 03 '14 at 17:54
  • 1
    Seems like that is the root of your problem, OSX `devfs` is funny about normal files. Strange you didn't get an error from `mv` though. How about this way: `touch testfile; mv testfile /dev`? – Graeme Mar 03 '14 at 17:58
  • 1
    touch testfile; mv testfile /dev results in operation not supported too, so I am going to assume when you mv file /dev/null on a Mac you are "unlinking" /dev/null and then the mv fails, but this seems weird since mv is supposed to be atomic. – Gregg Leventhal Mar 03 '14 at 18:07
  • @GreggLeventhal So it was pointed out to me. I deleted my comment, but too late to stop you answering. :-) In the interest of reducing clutter, if you delete your comment, I'll delete mine. Just ping me on chat, and sorry about the noise. – Faheem Mitha Mar 03 '14 at 18:11
  • 3
    @Gregg, `mv` can only be atomic on the same filesystem. – Graeme Mar 03 '14 at 18:59
  • 1
    Maybe [this](https://www.usenix.org/legacy/events/bsdcon/full_papers/kamp/kamp_html/) @DE_WHITEOUT flag? –  Mar 04 '14 at 00:29

3 Answers3

17

Looking at the source code for mv, http://www.opensource.apple.com/source/file_cmds/file_cmds-220.7/mv/mv.c :

/*
 * If rename fails because we're trying to cross devices, and
 * it's a regular file, do the copy internally; otherwise, use
 * cp and rm.
 */
if (lstat(from, &sb)) {
    warn("%s", from);
    return (1);
}
return (S_ISREG(sb.st_mode) ?
    fastcopy(from, to, &sb) : copy(from, to));

...

int
fastcopy(char *from, char *to, struct stat *sbp)
{
...
while ((to_fd =
    open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) {
        if (errno == EEXIST && unlink(to) == 0)
            continue;
        warn("%s", to);
        (void)close(from_fd);
        return (1);
}

In the first pass through the while loop, open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0) will fail with EEXIST. Then /dev/null will be unlinked, and the loop repeated. But as you pointed out in your comment, regular files can't be created in /dev, so on the next pass through the loop, open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0) is still going to fail.

I'd file a bug report with Apple. The mv source code is mostly unchanged from the FreeBSD version, but because OSX's devfs has that non-POSIX behavior with regular files, Apple should fix their mv.

Mark Plotnick
  • 24,913
  • 2
  • 59
  • 81
  • 1
    I am giving this best answer right now for providing source code and acknowledging this as a bug, which is what I was driving at. – Gregg Leventhal Mar 07 '14 at 16:45
12

Moving a file to the location of an already existing file replaces the existing file. In this case the /dev/null device file is replaced, just as any normal file would be. To avoid this use the -i (interactive, warns before overwriting) or -n (no clober) option for mv.

/dev/null only performs its special function as a bit-bucket then the device is opened as is. Eg, when the > shell operator is used, the file is opened then truncated (not removed an replaced, which may be what you expected). As mentioned by casey, the correct way to remove a file is with rm or even with unlink.

Graeme
  • 33,607
  • 8
  • 85
  • 110
10

Umm, because you overwrite the special file with normal one? What did you expect to happen? dev/null is not a directory, it is a file pointing to a null device. When you mv something to it, you delete the original and replace it with whatever you moved:

$ file /dev/null 
/dev/null: character special 
$ sudo mv file /dev/null 
$ file /dev/null 
/dev/null: ASCII text
terdon
  • 234,489
  • 66
  • 447
  • 667
  • 2
    But I am saying /dev/null was showing as missing when running an ls -lad /dev/null. This must be something specific to the devfs, that it what I wanted to know about. – Gregg Leventhal Mar 03 '14 at 17:24
  • If I mv file /dev/null then /dev/null should contain what file contained but still exist. I want to know why this caused /dev/null to not be found in an ls. – Gregg Leventhal Mar 03 '14 at 17:25
  • Granted, I am doing this on a Mac, so it might be slightly different, but I did not expect the file to show as missing, I only expected it to show as changed to the source file. – Gregg Leventhal Mar 03 '14 at 17:31
  • @GreggLeventhal yes, it must be an OSX or BSD thing (please edit your Q and specify your OS). On my Linux I can still see `/dev/null`, it's just become the file I moved. – terdon Mar 03 '14 at 17:37