Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can create a deeply-nested directory structure that can't be deleted #13451

Closed
DouglasRoyds opened this issue May 25, 2015 · 22 comments
Closed

Comments

@DouglasRoyds
Copy link

While trying to build coreutils inside a Docker container, I found that the coreutils ./configure step left behind a very deep directory structure that could not be deleted:

$ rm -fr confdir3/
rm: cannot remove 'confdir3/confdir3/confdir3/confdir3/.../confdir3/confdir3/confdir3': File name too long

$ while cd confdir3/; do true; done         # Takes some time ...
-bash: cd: confdir3/: File name too long

$ pwd | wc -c
8099

Users could conceivably be affected in other cases in which an application (presumably accidentally) creates an excessively deep directory structure that can not be deleted. It does need to be quite deep: 8000 characters generally represent quite a few directories.

I can reproduce this simply with the following script:

$ while mkdir tempdir3; do cd tempdir3; done
mkdir: cannot create directory 'tempdir3': File name too long

$ pwd | wc -c
8092

$ cd ..
$ rmdir tempdir3/
rmdir: failed to remove 'tempdir3/': File name too long

Initially, I was tempted to blame aufs, but I can run the same experiment outside of a Docker container without hitting the same problem. I generated a directory structure over 10000 directories deep (aufs running on top of ext4), with a path length in excess of 90000 characters, but ran out of patience before finding any upper limit to aufs' path length.

The coreutils configuration runs code from getcwd-path-max.m4 (from the gnulib project), which includes C code to probe the limits of PATH_MAX for getcwd(). It does this by generating a directory structure that runs out to PATH_MAX, and testing that getcwd() remains well-behaved. At the end, it politely attempts to remove this directory structure, but silently fails. This does not have any immediate impact on the configuration of coreutils, though a second attempt to ./configure does have a different result (as mkdir confdir3/ fails). It leaves the user unable to delete the coreutils directory after building, triggering (for instance) this downstream OpenEmbedded/Yocto defect: https://bugzilla.yoctoproject.org/show_bug.cgi?id=7338

Work-arounds:

  1. From the top, manually move the deep structure up by one level, and then delete it, eg:

    $ mv confdir3/confdir3/ fred
    $ rm -fr fred/ confdir3/
    
  2. For the coreutils build, patch getcwd-path-max.m4 (and regenerate the ./configure script).

    --- coreutils-7.2.original/configure    2009-04-01 01:34:13.000000000 +1300
    +++ coreutils-7.2/configure 2015-05-25 12:50:39.616794241 +1200
    @@ -19177,6 +19177,7 @@
     #include <sys/stat.h>
     #include <sys/types.h>
     #include <fcntl.h>
    +#include <stdio.h>
    
     #ifndef AT_FDCWD
     # define AT_FDCWD 0
    @@ -19304,6 +19305,13 @@
    
         /* Try rmdir first, in case the chdir failed.  */
         rmdir (DIR_NAME);
    +    /* Getting rid of the very bottom dirs inside a Docker container is tricky */
    +    if (chdir ("../..") < 0) exit (errno);
    +    rename (DIR_NAME"/"DIR_NAME, "c");
    +    rename (DIR_NAME, "d");
    +    rmdir ("c");
    +    rmdir ("d");
    +    /* Now for the rest */
         for (i = 0; i <= n_chdirs; i++)
           {
        if (chdir ("..") < 0)
    
  3. Build coreutils in a data volume.

  4. Delete the directory structure from the underlying fs (not ideal - requires root privilege).

Docker 1.5.0
Host Ubuntu 15.04
Guest Ubuntu 14.04

@DouglasRoyds
Copy link
Author

docker version:

Client version: 1.5.0
Client API version: 1.17
Go version (client): go1.3.3
Git commit (client): a8a31ef
OS/Arch (client): linux/amd64
Server version: 1.5.0
Server API version: 1.17
Go version (server): go1.3.3
Git commit (server): a8a31ef

docker info:

Containers: 2
Images: 179
Storage Driver: aufs
Root Dir: /home/root/docker/aufs
Backing Filesystem: extfs
Dirs: 183
Execution Driver: native-0.2
Kernel Version: 3.19.0-16-generic
Operating System: Ubuntu 15.04
CPUs: 4
Total Memory: 7.731 GiB
Name: acheron
ID: LT43:PZUM:VHL7:A45V:FJPD:ISYB:5U4U:NNVZ:CSVD:GU7C:JV4T:P46A
WARNING: No swap limit support

uname -a:

Linux acheron 3.19.0-16-generic #16-Ubuntu SMP Thu Apr 30 16:09:58 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

Environment details (AWS, VirtualBox, physical, etc.):

  • Physical

@cpuguy83
Copy link
Member

I'm not sure what's expected here.

@DouglasRoyds
Copy link
Author

Docker allows a directory structure to be created that can not be deleted. This is a bad thing: a defect. It has down-stream effects on other projects (coreutils, OpenEmbedded), which is not ideal either. How serious is it? Not so bad - at least there are some work-arounds available.

So what is expected? A fix perhaps, but it's up to you.

@cpuguy83
Copy link
Member

@DouglasRoyds I don't see how Docker is allowing this, or how it could prevent it.
Are you sure you aren't memory constrained in the container?

@DouglasRoyds
Copy link
Author

According to https://docs.docker.com/reference/run, if "We set nothing about memory, this means the processes in the container can use as much memory and swap memory as they need" - which I hadn't.

As mentioned in my original report (above), I ran exactly the same experiment outside of the Docker container, generating a directory tree 10 times as deep without any problem.

@cpuguy83
Copy link
Member

@DouglasRoyds this is not an entirely accurate statement, as any process started by the docker daemon is going to inherit certain settings (like ridiculously high ulimit values) by default, which you as a user probably do not have set (and as such are comparatively low)... note I'm just using this as an example.

Curious if you tried to make this fail on a different graphdriver?

@DouglasRoyds
Copy link
Author

... uh, no, sorry. I'd have to do quite a lot of work here to set up a separate partition to try btrfs, for instance. You don't happen to have a btrfs (or even vfs) instance available yourself? My test script above is pretty straightforward. Sorry I can't be more helpful.

@aidanhs
Copy link
Contributor

aidanhs commented Aug 5, 2015

Just hit this, annoying. Ubuntu 12.04, kernel 3.13.0-57-generic (pretty sure this is a 14.04 kernel), docker 1.7.1

Devicemapper works:

$ docker run -it --rm ubuntu:14.04.2 bash
root@131a73881a45:/# PS1='# '
# while mkdir tempdir3; do cd tempdir3; done
mkdir: cannot create directory 'tempdir3': File name too long
# pwd | wc -c
8191
# cd ..
# rmdir tempdir3/
# exit

Aufs fails:

$ docker run -it --rm ubuntu:14.04.2 bash
root@c2696be50a3e:/# PS1='# '
# while mkdir tempdir3; do cd tempdir3; done
mkdir: cannot create directory 'tempdir3': File name too long
# pwd | wc -c
8101
# cd ..
# rmdir tempdir3/
rmdir: failed to remove 'tempdir3/': File name too long
# exit

For completeness I also tried AUFS on Ubuntu 14.04 with kernel 3.19.0-25 (i.e. from 15.04). The result is exactly the same as for AUFS on 12.04.

I don't know why you still recommend AUFS! I've been having a streak of bad luck with it recently :(

@thaJeztah
Copy link
Member

@aidanhs the main reason for recommending AUFS is that at this moment it's the most stable solution out of the box (compare the number of issues labeled devicemapper with aufs). Devicemapper certainly can be a good driver, but requires manual configuration to be useful in production (the default setting certainly are not recommended for that)

In the long term, we hope to make overlay the default driver, as it is part of recent kernels. However, there are some show-stoppers there, making this not possible currently.

@aidanhs
Copy link
Contributor

aidanhs commented Aug 10, 2015

Yeah, you're between a rock and a hard place really. I'm sure if I were on devicemapper I would be asking why aufs wasn't the default!

@DouglasRoyds
Copy link
Author

Just found this line in /var/log/kern.log, for what it's worth. The word-wrap is mine, it was all on one line in the logfile:

Aug 12 03:36:30 acheron kernel: [6707185.374190] 
audit: type=1400 audit(1439294610.591:272): apparmor="DENIED" operation="mkdir" info="Failed name lookup - name too long" error=-36 profile="docker-default" name="//docker/aufs/diff/4ac9924ecd213a357801ff98e9b40067fc33afa7fafc43d8f0ffa3cc3a54b86c/tmp/tait.dbs/build/tmp/work/x86_64-linux/coreutils-native-7.2-r1/coreutils-7.2/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdi
audit: type=1400 audit(1439294610.591:273): apparmor="DENIED" operation="rename_dest" info="Failed name lookup - name too long" error=-36 profile="docker-default" name="/hom/aufs/diff/4ac9924ecd213a357801ff98e9b40067fc33afa7fafc43d8f0ffa3cc3a54b86c/tmp/tait.dbs/build/tmp/work/x86_64-linux/coreutils-native-7.2-r1/coreutils-7.2/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/conf
audit: type=1400 audit(1439294633.936:274): apparmor="DENIED" operation="rename_dest" info="Failed name lookup - name too long" error=-36 profile="docker-default" name="/hom/aufs/diff/4ac9924ecd213a357801ff98e9b40067fc33afa7fafc43d8f0ffa3cc3a54b86c/tmp/tait.dbs/build/tmp/work/x86_64-linux/coreutils-native-7.2-r1/coreutils-7.2/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/confdir3/conf

I'm using the first workaround from the list in my original posting, so I assume the second and third complaints are something to do with the mv confdir3/confdir3/ fred command.

I have no familiarity with apparmor, so I'm open to suggestions for experiments.

@cpuguy83
Copy link
Member

ping @ewindisch #13451 (comment)

@thaJeztah
Copy link
Member

ping @ewindisch, can you have a look here?

@vote539
Copy link

vote539 commented Jan 5, 2016

This problem with being unable to remove deeply nested confdir3 directories happens when building GNU Octave also. @DouglasRoyds's patch for getcwd-path-max.m4 is a good workaround. I was also able to run the following line at the bash terminal.

while [[ -e confdir3/confdir3 ]]; do mv confdir3/confdir3 confdir3a; rmdir confdir3; mv confdir3a confdir3; done; rmdir confdir3

@yuseitahara
Copy link

I solved this problem. Try this.

echo 9000 > /sys/module/apparmor/parameters/path_max

http://wiki.apparmor.net/index.php/FAQ#Failed_name_lookup_-_name_too_long

@thaJeztah
Copy link
Member

Thanks @yuseitahara

@DouglasRoyds
Copy link
Author

Changing the apparmor path_max just moves the goalposts.

Outside the container (ie. on the host machine):

$ sudo cat  /sys/module/apparmor/parameters/path_max
8192
$ echo 9000 | sudo tee /sys/module/apparmor/parameters/path_max

Inside the container:

$ while mkdir tempdir3; do cd tempdir3; done
$ pwd | wc -c
8902
$ mkdir temp && cd temp && pwd | wc -c
8907
$ cd ..
$ rmdir temp/
rmdir: failed to remove ‘temp/’: File name too long

So we just added some 800 bytes to the too-long-to-delete directory path.

On the other hand, if I create the overly-long directory path first, and then bump up the path_max (outside the container), I can subsequently delete the directory.

The longest path I can delete with the default path_max = 8192 is 8086 characters long, ie. 106 characters shorter than the path_max. The longest path I can create is 8099 characters, ie. 93 shorter than path_max. Sure enough, if I then increase path_max by 13 characters (over the default 8192), I can delete the directory from inside the container:

$ echo 8205 | sudo tee /sys/module/apparmor/parameters/path_max

We can create a directory path that is 13 characters longer than we can delete.

Interestingly, if I create a file deep down in this (deep) directory tree, I can delete the longest filename that I am able to create, eg:

$ touch 1234567890123456
touch: cannot touch ‘1234567890123456’: File name too long
$ touch 123456789012345
$ rm 123456789012345 

@justincormack
Copy link
Contributor

I cannot replicate this on a recent aufs (4.4 kernel series), it accepts paths of at least 44128 characters without issues, so it looks like this was resolved upstream.

@thaJeztah
Copy link
Member

if this is an aufs issue outside of docker, I don't think there's much we can do here indeed. I'll close this issue, but feel free to continue the discussion

@DouglasRoyds
Copy link
Author

Problem still applies under 4.4 kernel:

Server Version: 1.11.1
Storage Driver: aufs
 Root Dir: /home/root/docker/aufs
 Backing Filesystem: extfs
 Dirs: 113
 Dirperm1 Supported: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: host bridge null
Kernel Version: 4.4.0-22-generic
Operating System: Ubuntu 16.04 LTS
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 7.73 GiB

Just to be clear, this is an interaction problem between aufs and docker, as opposed to just an aufs problem: the defect does not happen outside of a docker container (directly in an aufs mount).

@staticfloat
Copy link

I just ran into this as well while trying to ./configure a tar installation, quite annoying. I can confirm this is still happening with Server Version: 1.13.0-rc4.

@ksopyla
Copy link

ksopyla commented Jun 10, 2017

I have the same problem while building octave 4.0.0 docker image

RUN apt-get build-dep -y octave
RUN apt-get install -y -q  curl
RUN curl -O ftp://ftp.gnu.org/gnu/octave/octave-4.0.0.tar.gz && tar xf octave-4.0.0.tar.gz && cd octave-4.0.0 && ./configure --without-qt && make -j4 && make install && cd / && rm /octave-4.0.0.tar.gz && rm -r /octave-4.0.0

For me workaround was to change docker storage driver from aufs to overlay2

System information:

  • ubuntu 16.04, kernel 4.4
  • docker 17.05

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants