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.

14
  • 46
    Don't mess with /dev/null.
    – devnull
    Commented Mar 3, 2014 at 17:15
  • 8
    so says @devnull
    – Braiam
    Commented Mar 3, 2014 at 17:19
  • 3
    The proper way to delete files is the rm command.
    – casey
    Commented Mar 3, 2014 at 17:20
  • 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
    Commented Mar 3, 2014 at 17:58
  • 3
    @Gregg, mv can only be atomic on the same filesystem.
    – Graeme
    Commented Mar 3, 2014 at 18:59

3 Answers 3

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.

1
  • 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. Commented Mar 7, 2014 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.

3
  • See my comments to terdon. Commented Mar 3, 2014 at 17:25
  • Understood, the -d was unneeded, that is just a bad habit however the file should still show up in the ls output, it should not show as nonexistent. Commented Mar 3, 2014 at 17:33
  • @Graeme - welcome to 3K, nice job!
    – slm
    Commented Mar 5, 2014 at 1:28
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
4
  • 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. Commented Mar 3, 2014 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. Commented Mar 3, 2014 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. Commented Mar 3, 2014 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
    Commented Mar 3, 2014 at 17:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .