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
    Mar 3, 2014 at 17:15
  • 8
    so says @devnull
    – Braiam
    Mar 3, 2014 at 17:19
  • 3
    The proper way to delete files is the rm command.
    – casey
    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
    Mar 3, 2014 at 17:58
  • 3
    @Gregg, mv can only be atomic on the same filesystem.
    – Graeme
    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. 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. 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. Mar 3, 2014 at 17:33
  • @Graeme - welcome to 3K, nice job!
    – slm
    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. 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. 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. 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
    Mar 3, 2014 at 17:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.