ProFTPD mini-HOWTO - Quotas

## ProFTPD Quotas

A busy FTP server handles hundreds to thousands of files belonging to hundreds to thousands of users. There is not an infinite capacity on the disks of the server, however, and eventually those files will take up too much space. Thus many sites have a pressing need to set limits on just how much can be stored on the server by their users.

Most Unix systems have support for OS- or fileystem-level quotas. These have the advantage of being transparent to applications like `proftpd`, which means that the applications need not worry about maintaining and enforcing quotas. The kernel/filesystem will handle that. One disadvantage these OS-level quotas have, though, is that they are strictly tied to OS and/or filesystem, and not all Unix kernels and filesystems are the same. They also rely on each user having their own separate user ID. In the case of virtual users for `proftpd`, it is possible for many users to have the same user ID, which causes problems for these traditional Unix quotas.

For these reasons, the `mod_quotatab` module was developed for ProFTPD. This module, being part of the application, applies to all the Unix kernels and filesystems which support ProFTPD, and easily handles virtual users. The `mod_quotatab` documentation covers how to configure `proftpd` for quotas.

Note: This howto is a work-in-progress. Please contact me with suggestions, questions, requests, etc for what you would like to see covered here. Thanks!

Example Configuration
Here is an example `mod_quotatab` configuration for supporting quotas via file tables and SQL tables. This is an example only.

      <IfModule mod_quotatab.c>
        QuotaEngine on
        QuotaLog /var/log/ftpd/quota.log

        # For more information on using files for storing the limit and tally
        # table quota data, please see the mod_quotatab_file documentation:
        #
        #   http://www.castaglia.org/proftpd/modules/mod_quotatab_file.html
        #
        <IfModule mod_quotatab_file.c>
          QuotaLimitTable file:/etc/ftpd/ftpquota.limittab
          QuotaTallyTable file:/etc/ftpd/ftpquota.tallytab
        </IfModule>

        # For more information on using a SQL database for storing the limit and
        # tally table quota data, please see the mod_quotatab_file documentation:
        #
        #   http://www.castaglia.org/proftpd/modules/mod_quotatab_sql.html
        #
        <IfModule mod_quotatab_sql.c>
          SQLNamedQuery get-quota-limit SELECT "* FROM quotalimits WHERE name = '%{0}' AND quota_type = '%{1}'"
          SQLNamedQuery get-quota-tally SELECT "* FROM quotatallies WHERE name = '%{0}' AND quota_type = '%{1}'"
          SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name = '%{6}' AND quota_type = '%{7}'" quotatallies
          SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" quotatallies

          QuotaLock /var/lock/ftpd.quotatab.lock
          QuotaLimitTable sql:/get-quota-limit
          QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally
        </IfModule>
      </IfModule>

Frequently Asked Questions

Question: How do I set the disk space limit for a user?
Answer: The short answer is to use the "upload bytes limit" setting.

It's true that the various limits maintained by `mod_quotatab`, for either bytes or files (or both), are confusing. When designing the module, I anticipated administrators wanting to limit download as well as upload transfers; in reality, most administrators wish to limit the disk space for their users. The bytes uploaded limit doubles as the disk space limit, for there is no effective difference between them; any bytes uploaded via FTP are automatically stored on disk.

Question: `mod_quotatab` only tracks changes made using `proftpd`, and my users can add/remove files other ways. What am I supposed to do?
Answer: As the `mod_quotatab` documentation states, the module was designed only to track changes done via FTP; it made implementation of quotas simpler.

This question is also often posed as "Why can't mod_quotatab just calculate the amount of space used by a user?" The module could do this, but there are some considerations with this kind of approach.

The primary consideration is the time cost of having to recurse a directory. If the directory is relatively small, the time needed is small. For very large/deep directories, however, the time needed to do the scan could be long, possibly long enough for users to notice and complain. Another complication is the disk space used by a given user is not confined to their home directory. Many sites have home directories for users, and have a shared directory that all users can use. Somehow `mod_quotatab` would need to know to scan these shared directories.

As of `proftpd-1.3.1rc1`, the `mod_quotatab` supports this feature via the `ScanOnLogin` parameter of the `QuotaOptions` directive.

Question: If I use this `ScanOnLogin` option, how do I tell it to ignore certain files? Answer: Use the new `QuotaExcludeFilter` directive. For example, if you wanted to exclude all files that ended in ".log" from the automatic quota calculation, you might use:

      <IfModule mod_quotatab.c>
         ...

         # Automatically calculate the used quota on login
         QuotaOptions ScanOnLogin

         # Do not include files ending in ".log" in the automatically
         # calculated quota
         QuotaExludeFilter \.log$
      </IfModule>

Question: If `mod_quotatab` does not automatically scan for disk space usage, how will it know about my existing users?
Answer: By default, `mod_quotatab` indeed knows nothing about what disk space is already occupied by your users; the tally table starts out blank.

To help address this, there is a `diskuse.pl` Perl script:

      http://www.castaglia.org/proftpd/contrib/diskuse

which will display the number of bytes owned by a given user (or group) within a list of directories. The parameters needed for running `diskuse.pl` are documented here.

Once you've run `diskuse.pl` to find out the current usage for your users, you can enter those numbers into your tally table. Unfortunately `diskuse.pl` cannot read your `proftpd.conf` file to know whether you are using a file- or SQL-based tally table, so it cannot automatically update your tally table.

Question: How can I set a default quota for all of my users?
Answer: Unfortunately, there is no way currently to do this. The `mod_quotatab` module was designed such that the administrator would have to explicitly create limits for every user.

However, a `mod_quotatab_default` module could be written to provide default quotas. I simply do not know if this would be desirable enough to users for writing the module.

Question: What is a "tally table"?
Answer: Tally tables, and limit tables, are covered in the `mod_quotatab` documentation.

Question: How do I construct the limit and tally files for file-based quotas?
Answer: There is a Perl script called `ftpquota` which can create the necessary files. This script can also be found under the `contrib/` directory of the `proftpd` source distribution.

Question: Is there a SQL script for the SQL quota tables used by `mod_quotatab_sql`?
Answer: No. However, the `mod_quotatab_sql` documentation contains example schema for the necessary tables.

Question: How do I set a limit on the size of a directory?
Answer: Currently, you cannot.

Traditional Unix quotas are implemented in terms of ownership: the thing that counts is not where a file is located on the filesystem, but which user and/or group owns the file. Asking about directory quotas assumes a different basis for quotas, based on location rather than ownership (such quotas are often called tree-based quotas). The `mod_quotatab` module followed the example of traditional Unix quotas, but I have started designing how location-based quotas might be implemented.

Question: Why isn't `mod_quotatab` updating my tally table?
Answer: It depends.

One possibility is that the per session flag in the limit in effect is set to `true`, which means that the limits will only be applied to this session. When this happens, `mod_quotatab` will not update the tally table.

Another possibility is that your configured limits are "unlimited" (i.e. zero). As it states in the `mod_quotatab` documentation:

      For any quota limit that is set as "unlimited", mod_quotatab will not keep the tally.
      Many site administrators might want this ability, for accounting purposes. However,
      that ability is outside of the intended design of this module; other logging modules
      are much better suited for accounting purposes (e.g. mod_sql's SQLLog directive).

If the above cases are not applicable, then consider looking in the `QuotaLog` file for more information.

Question: Can I use `mod_quotatab` to set monthly quotas?
Answer: Sort of. `mod_quotatab` itself has no concept of time, so this sort of capability is not built-in. However, it can be approximated by having some other process, such as a cron job, periodically (such as once a month) clear the tally table, yielding the effect of monthly quotas.

Question: How can users see their current quota?
Answer: There are two ways to show the current quota to users. There is the `SITE QUOTA` command. And there are certain `Display` variables that are supported by the `mod_quotatab` module.

Question: What if I want to set limits on the size of individual files being transferred?
Answer: For this, you do not need the `mod_quotatab` module. ProFTPD has the `MaxRetrieveFileSize` and `MaxStoreFileSize` directives.

Question: Why do I see the following error?

      QuotaLimitTable: unsupported table source type: 'sql'

Answer: The `mod_quotatab` module acts as a general quota managing front-end; it relies on other backend modules to handle the specifics of storage formats. Every backend module (e.g. `mod_quotatab_file`, `mod_quotatab_sql`, `mod_quotatab_ldap`) registers a supported table type with the main `mod_quotatab` module. The error above indicates that the `mod_quotatab_sql` module has not been compiled/loaded into `proftpd`.

Question: How do group quotas work? If a user is in multiple groups, what happens?
Answer: The `mod_quotatab` module searches for applicable quotas, by type, in the following order:

- user quotas
- group quotas
- class quotas
- the "all" quota

Thus if there is a user-specific quota, it will be used; the search stops there. The `mod_quotatab` module does not combine multiple different quotas.

What happens, then, if a user belongs to multiple different groups, and there are different quotas set for those different groups? First, the primary group of the user is checked; this is the group that is included, for example, in the entry for the user in the `/etc/passwd` file. If no quota for the primary group is found, the module then checks against each of the user's supplemental groups. The order in which these groups are checked is dependent up on the order in which the groups are returned from the auth module providing them, e.g. `mod_sql`, `mod_ldap`, or perhaps `mod_auth_unix`.

Question: Does `mod_quotatab` handle the case where a client might append to/overwrite an existing file, in terms of tracking bytes?
Answer: Yes. The `mod_quotatab` module checks the size of the file before the upload/append starts, and then checks the size of the file again after the upload/append completes. The difference in file sizes is used for tracking the byte-related tallies.

Question: I configured limits for my users such that only the transfer bytes limit is set. On uploads, my transfer bytes tally is incremented properly. But when I delete a file, the transfer bytes tally is not decremented. Is this a bug?
Answer: No, it's not a bug.

Deleting a file does not count as transferred bytes (i.e. it does not pertain to the `bytes_xfer` limit); it counts as uploaded bytes (i.e. the `bytes_in` limit). And since there is only a limit, in this case, on the transferred bytes, the `mod_quotatab` module ends up not changing any tally value for a delete. More details on this can be found in Bug#2897.

$Date: 2009-07-23 15:04:05 $