🌐
Fabian Lee
fabianlee.org β€Ί 2020 β€Ί 01 β€Ί 13 β€Ί linux-using-xfs-project-quotas-to-limit-capacity-within-a-subdirectory
Linux: Using xfs project quotas to limit capacity within a subdirectory | Fabian Lee : Software Engineer
January 14, 2020 - XFS is a journaled filesystem that has excellent parallel performance, and is licensed under the GPL which means it has been included in many Linux distributions. One of the features of XFS is the ability to enforce quotas based on user, group, and project. In this article, I will show how to assign filesize quotas ...
🌐
Linux Man Pages
man7.org β€Ί linux β€Ί man-pages β€Ί man8 β€Ί xfs_quota.8.html
xfs_quota(8) - Linux manual page
# rm -f /etc/projects /etc/projid # mount -o prjquota /dev/xvm/var /var # xfs_quota -x -c 'project -s -p /var/log 42' /var # xfs_quota -x -c 'limit -p bhard=1g 42' /var
🌐
Linux Man Pages
linux.die.net β€Ί man β€Ί 8 β€Ί xfs_quota
xfs_quota(8) - Linux man page
# rm -f /etc/projects /etc/projid # mount -o prjquota /dev/xvm/var /var # xfs_quota -x -c 'project -s -p /var/log 42' /var # xfs_quota -x -c 'limit -p bhard=1g 42' /var
🌐
JuiceFS
juicefs.com β€Ί storage quota
Storage Quota | JuiceFS Document Center
You can use juicefs quota set $METAURL --path $DIR --capacity $N to set the directory capacity limit in GiB.
🌐
Ubuntu Manpages
manpages.ubuntu.com β€Ί jammy β€Ί man(8)
Ubuntu Manpage: xfs_quota - manage use of quota on XFS filesystems
# rm -f /etc/projects /etc/projid # mount -o prjquota /dev/xvm/var /var # xfs_quota -x -c 'project -s -p /var/log 42' /var # xfs_quota -x -c 'limit -p bhard=1g 42' /var
🌐
Medium
juicefs.medium.com β€Ί a-deep-dive-into-the-design-of-directory-quotas-in-juicefs-8eba6295a937
A Deep Dive into the Design of Directory Quotas in JuiceFS | by JuiceFS | Medium
October 27, 2023 - $ gluster volume quota <VOLNAME> limit-usage <DIR> <HARD_LIMIT> Using the existing Linux tool but with specific fields. For example, CephFS manages quotas as a special extended attribute: $ setfattr -n ceph.quota.max_bytes -v 100000000 /some/dir # 100 MB Β· JuiceFS opted for the first approach, with the command form as follows: $ juicefs quota set <METAURL> --path <PATH> --capacity <LIMIT> --inodes <LIMIT>
Find elsewhere
Top answer
1 of 1
5

I believe you are missing a

chattr +P -p 51 /test/first

/etc/projects seems only used by XFS tools. Also /etc/projid is only there for pretty printing.

fyi this is the procedure I came up with:

(step 0 to actually create the block device:

dd if=/dev/zero of=/tmp/fs bs=1024 count=80000
losetup -f /tmp/fs
losetup -l

)

  1. create a file system with big enough inodes:
mkfs.ext4 -I 256 /dev/loop0
  1. enable project quotas and make sure the file system is mounted with it by default (here using extended options with -E, avoiding the mount option in your step3, but also quite sneaky as you don't see it in /proc/mounts as mounted as such)
tune2fs -Q prjquota  /dev/loop0
tune2fs -E mount_opts=prjquota /dev/loop0
  1. mount it
mount /dev/loop0 /mnt/loop/
  1. the quota on command does not seem useful, so skipping this one

  2. set a proj id, but as a pure courtesy for the next sys admin logging in to your box. Not actually required

echo testproj:51 >> /etc/projid
  1. actually make your folder part of the project (which was missing from your list)
mkdir abc
chattr +P -p 51 abc
  1. edit the quota. Let's use the setquota tool, that could be used later in some ansible playbook someday, unlike edquota which runs an interactive editor:
setquota -P testproj 0 1234 0 0 /mnt/loop/
  1. confirm the quota is set
repquota  -P   /mnt/loop/
# in some parsable format, assuming you wrote some simple enough strings in projid, since the xml formatter is pretty basic
repquota -P /mnt/loop/ -O xml
  1. verify it works:

as a normal user:

dd if=/dev/zero of=someoutput oflag=append
loop0: write failed, project block limit reached.
dd: writing to 'someoutput': Disk quota exceeded
2471+0 records in
2470+0 records out
1264640 bytes (1.3 MB, 1.2 MiB) copied, 0.00985608 s, 128 MB/s
  1. verify you can trivially escape it though, as a normal user:
chattr  -p 43 someoutput
dd if=/dev/zero of=someoutput oflag=append
dd: writing to 'someoutput': No space left on device
127427+0 records in
127426+0 records out
65242112 bytes (65 MB, 62 MiB) copied, 0.561987 s, 116 MB/s

here completely filling the file system.

EDIT: more info on the limitations of project quotas Re: Project Quota file owner could change its project ID?, Re: ext4 and project quotas bugs (/ features)

🌐
Linux Man Pages
man7.org β€Ί linux β€Ί man-pages β€Ί man2 β€Ί quotactl.2.html
quotactl(2) - Linux manual page
The id argument is the identification ... QFMT_VFS_V0 The standard VFS v0 quota format, which can handle 32-bit UIDs and GIDs and quota limits up to 2^42 bytes and 2^32 inodes....
Top answer
1 of 6
9

Here's a working example with the python code you cited:

Usage: tree.py -f [file limit] <directory>

If a number is specified for -f [file limit] then ... <additional files> is printed and the other files are skipped. Additional directories should not be skipped however. If the file limit is set to 10000 (default) this acts as no limit

#! /usr/bin/env python
# tree.py
#
# Written by Doug Dahms
# modified by glallen @ StackExchange
#
# Prints the tree structure for the path specified on the command line

from os import listdir, sep
from os.path import abspath, basename, isdir
from sys import argv

def tree(dir, padding, print_files=False, limit=10000):
    print padding[:-1] + '+-' + basename(abspath(dir)) + '/'
    padding = padding + ' '
    limit = int(limit)
    files = []
    if print_files:
        files = listdir(dir)
    else:
        files = [x for x in listdir(dir) if isdir(dir + sep + x)]
    count = 0
    for file in files:
        count += 1
        path = dir + sep + file
        if isdir(path):
            print padding + '|'
            if count == len(files):
                tree(path, padding + ' ', print_files, limit)
            else:
                tree(path, padding + '|', print_files, limit)
        else:
            if limit == 10000:
                print padding + '|'
                print padding + '+-' + file
                continue
            elif limit == 0:
                print padding + '|'
                print padding + '+-' + '... <additional files>'
                limit -= 1
            elif limit <= 0:
                continue
            else:
                print padding + '|'
                print padding + '+-' + file
                limit -= 1

def usage():
    return '''Usage: %s [-f] [file-listing-limit(int)] <PATH>
Print tree structure of path specified.
Options:
-f          Print files as well as directories
-f [limit]  Print files as well as directories up to number limit
PATH        Path to process''' % basename(argv[0])

def main():
    if len(argv) == 1:
        print usage()
    elif len(argv) == 2:
        # print just directories
        path = argv[1]
        if isdir(path):
            tree(path, ' ')
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    elif len(argv) == 3 and argv[1] == '-f':
        # print directories and files
        path = argv[2]
        if isdir(path):
            tree(path, ' ', True)
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    elif len(argv) == 4 and argv[1] == '-f':
        # print directories and files up to max
        path = argv[3]
        if isdir(path):
            tree(path, ' ', True, argv[2])
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    else:
        print usage()

if __name__ == '__main__':
    main()

When run, it should produce output similar to:

user@host /usr/share/doc $ python /tmp/recipe-217212-1.py -f 2 . | head -n 40
+-doc/
  |
  +-libgnuradio-fft3.7.2.1/
  | |
  | +-copyright
  | |
  | +-changelog.Debian.gz
  |
  +-libqt4-script/
  | |
  | +-LGPL_EXCEPTION.txt
  | |
  | +-copyright
  | |
  | +-... <additional files>
  |
  +-xscreensaver-gl/
  | |
  | +-copyright
  | |
  | +-changelog.Debian.gz
  | |
  | +-... <additional files>
2 of 6
12

One can use tree --filelimit=N to limit number of subdirectories/file to display. Unfortunately, this will not open directory which has more than N sub-directories and files.

For simple cases, when you have multiple directories and most have too many(say > 100) files, you can use tree --filelimit=100.

.
β”œβ”€β”€ A1
β”‚   β”œβ”€β”€ A2
β”‚   β”œβ”€β”€ B2
β”‚   β”œβ”€β”€ C2 [369 entries exceeds filelimit, not opening dir]
β”‚   └── D2 [3976 entries exceeds filelimit, not opening dir]
β”œβ”€β”€ B1
β”‚   └── A2
β”‚       β”œβ”€β”€ A3.jpeg
β”‚       └── B3.png
└── C1.sh

Note, if A1/C2 has a sub-directory A3, it will not be shown.

P.S. This is not a complete solution but will be quicker for few.

🌐
Linux Man Pages
man7.org β€Ί linux β€Ί man-pages β€Ί man8 β€Ί setquota.8.html
setquota(8) - Linux manual page
If you specify this option, setquota will always send paths with a leading slash. This can be useful for legacy reasons but be aware that quota over RPC will stop working if you are using new rpc.rquotad. -F, --format=quotaformat Perform setting for specified format (ie. don't perform format autodetection). Possible format names are: vfsold Original quota format with 16-bit UIDs / GIDs, vfsv0 Quota format with 32-bit UIDs / GIDs, 64-bit space usage, 32-bit inode usage and limits, vfsv1 Quota format with 64-bit quota limits and usage, rpc (quota over NFS), xfs (quota on XFS filesystem) -u, --user Set user quotas for named user.
🌐
NERSC
docs.nersc.gov β€Ί filesystems β€Ί quotas
Quotas - NERSC Documentation
NERSC sets quotas on file systems shown in the table below. The table shows space quota, inode quota and Consequence for Exceeding Quota for each file system. An inode quota is a limit on the number of files and directories that can be created on a system, as each file and directory has an ...
🌐
RDR-IT
rdr-it.com β€Ί tutorials β€Ί windows server β€Ί file servers β€Ί fsrm β€Ί windows server : apply quotas on folders
Windows Server : apply quotas on folders - RDR-IT
March 17, 2026 - From the FSRM console, go to Quotas 1, right click in the central area and click on Create a quota 2. In the quota creation window, indicate the path 1 where it is applied, select Create a quota on the path 2, choose the model 3 and click on ...