12

Is there a way to shrink a directory entry?

My dovecot maildir directories have increased in size from the default 4096 to several megs, and it is messing with disk quotas.

The only way that I have found, is to delete and recreate the directory. I am hoping there is a magical function somewhere - I am open to anything, except for hand-coded assembler.

Edit: For posterity, to find dirents>4096:

 find / -type d -size +4k -printf "%s\t%p\t%i\n" | sort -nr
mikewaters
  • 1,135
  • 1
  • 14
  • 26

3 Answers3

6

See also rm on a directory with millions of files; tangentially related, but we discuss it there.

As far as I'm aware, at least on ext2/3, no, there's no (online) way to shrink them short of delete + recreate. Unmounted, several sources suggest e2fsck -D might work, though I can claim no personal experience with that option.

Some references for further reading:

BMDan
  • 7,129
  • 2
  • 22
  • 34
  • 1
    The first link was especially helpful: it addressed my problem specifically (mail servers), and the fact that Ted Tso weighed in on the dialogue is pretty cool ;) – mikewaters Apr 28 '11 at 13:13
4

I'd do it like this.

cp -aloldirnewdir&& mvolddirtempname&& mvnewdirolddir&& rm -rtempname

where olddir is the directory you want to shrink newdir and tempname are temporary filenames

cp -al creates links to the content of olddir in newdir and the next three commands swap them with newdir and clean up.

user313114
  • 578
  • 3
  • 7
3

I recently had to delete and recreate an NFS directory to shrink its size after it had previously bloated. In the process, I found a fairly efficient way to do this using rsync and hard links. This method avoids copying the actual files, while still accomplishing the goal of recreating the directory with the same contents.

Since we needed to take downtime to do this operation during our maintenance window, making the rebuilt directory available as soon as possible was important for us. If simplicity is more important to you, change the first move command to an 'rm -rf' on the source directory, and skip the other 'rm -rf' command.

I extracted these commands from a larger process, and abstracted the directory names, so apologies if I've made a mistake in that translation.

mkdir /tmp/holding_dir/
rsync -ai --delete --link-dest=/path/to/source_dir/ /path/to/source_dir/ /tmp/holding_dir/
mv /path/to/source_dir/ /tmp/deleteme/
mv /tmp/holding_dir/ /path/to/source_dir/

Then later, we remove the directory. Depending on size, you may need to use a more sophisticated technique to do this. For example, using rsync with the --delete flag to sync an empty directory into this directory may be more efficient.

rm -rf /tmp/deleteme/