Переглянути джерело

remove remainders of quota support

Some features like repository quotas rely on a server-side component
that enforces them (because that shall only be controllable server-side,
not client-side).

So, that can only work, if such a server-side component exists, which is the
case for borg 1.x ssh: repositories (but not for borg 1.x non-ssh: repositories).

For borg2, we currently have:
- fs repos
- sftp: repos
- rclone: repos (enabling many different cloud providers)
- s3/b3: repos
- ssh: repos using client/server rpc code similar as in borg 1.x

So, only for the last method we have a borg server-side process that could enforce some features, but not for any of the other repo types.

For quotas the current idea is that this should not be done within borg,
but enforced by a storage specific quota implementation (like fs quota,
or quota of the cloud storage provider). borg could offer information
about overall repo space used, but would not enforce quotas within borg.
Thomas Waldmann 2 місяців тому
батько
коміт
48b39d878f

+ 0 - 13
docs/deployment/hosting-repositories.rst

@@ -6,7 +6,6 @@ Hosting repositories
 ====================
 
 This sections shows how to provide repository storage securely for users.
-Optionally, each user can have a storage quota.
 
 Repositories are accessed through SSH. Each user of the service should
 have her own login which is only able to access the user's files.
@@ -56,18 +55,6 @@ multiple times to permit access to more than one repository.
 The repository may not exist yet; it can be initialized by the user,
 which allows for encryption.
 
-**Storage quotas** can be enabled by adding the ``--storage-quota`` option
-to the ``borg serve`` command line::
-
-    restrict,command="borg serve --storage-quota 20G ..." ...
-
-The storage quotas of repositories are completely independent. If a
-client is able to access multiple repositories, each repository
-can be filled to the specified quota.
-
-If storage quotas are used, ensure that all deployed Borg releases
-support storage quotas.
-
 **Specificities: Append-only repositories**
 
 Running ``borg init`` via a ``borg serve --append-only`` server will **not**

+ 0 - 2
docs/internals/frontends.rst

@@ -610,8 +610,6 @@ Errors
         The parent path of the repo directory [{}] does not exist.
     Repository.PathAlreadyExists rc: 19 traceback: no
         There is already something at {}.
-    Repository.StorageQuotaExceeded rc: 20 traceback: no
-        The storage quota ({}) has been exceeded ({}). Try deleting some archives.
     Repository.PathPermissionDenied rc: 21 traceback: no
         Permission denied to {}.
 

+ 1 - 1
docs/quickstart.rst

@@ -47,7 +47,7 @@ Also helpful:
   does not have free space any more.
 - if you use LVM: use a LV + a filesystem that you can resize later and have
   some unallocated PEs you can add to the LV.
-- consider using quotas
+- consider using quotas (e.g. fs quota, quota settings of storage provider)
 - use `prune` and `compact` regularly
 
 

+ 0 - 2
scripts/shell_completions/fish/borg.fish

@@ -89,7 +89,6 @@ complete -c borg         -l 'rsh'                   -d 'Use COMMAND instead of s
 set -l encryption_modes "none keyfile keyfile-blake2 repokey repokey-blake2 authenticated authenticated-blake2"
 complete -c borg -f -s e -l 'encryption'            -d 'Encryption key MODE' -a "$encryption_modes" -n "__fish_seen_subcommand_from init"
 complete -c borg -f      -l 'append-only'           -d 'Create an append-only mode repository'      -n "__fish_seen_subcommand_from init"
-complete -c borg -f      -l 'storage-quota'         -d 'Set storage QUOTA of the repository'        -n "__fish_seen_subcommand_from init"
 complete -c borg -f      -l 'make-parent-dirs'      -d 'Create parent directories'                  -n "__fish_seen_subcommand_from init"
 
 # borg create options
@@ -316,7 +315,6 @@ complete -c borg -f      -l 'strip-components'      -d 'Remove NUMBER of leading
 complete -c borg         -l 'restrict-to-path'      -d 'Restrict repository access to PATH'         -n "__fish_seen_subcommand_from serve"
 complete -c borg         -l 'restrict-to-repository' -d 'Restrict repository access at PATH'        -n "__fish_seen_subcommand_from serve"
 complete -c borg -f      -l 'append-only'           -d 'Only allow appending to repository'         -n "__fish_seen_subcommand_from serve"
-complete -c borg -f      -l 'storage-quota'         -d 'Override storage QUOTA of the repository'   -n "__fish_seen_subcommand_from serve"
 
 # borg config
 complete -c borg -f -s c -l 'cache'                 -d 'Get/set/list values in the repo cache'      -n "__fish_seen_subcommand_from config"

+ 1 - 1
src/borg/archiver/__init__.py

@@ -401,7 +401,7 @@ class Archiver(
                 # client is allowed to specify the allowlisted options,
                 # everything else comes from the forced "borg serve" command (or the defaults).
                 # stuff from denylist must never be used from the client.
-                denylist = {"restrict_to_paths", "restrict_to_repositories", "append_only", "storage_quota", "umask"}
+                denylist = {"restrict_to_paths", "restrict_to_repositories", "append_only", "umask"}
                 allowlist = {"debug_topics", "lock_wait", "log_level"}
                 not_present = object()
                 for attr_name in allowlist:

+ 3 - 18
src/borg/archiver/_common.py

@@ -30,7 +30,7 @@ from ..logger import create_logger
 logger = create_logger(__name__)
 
 
-def get_repository(location, *, create, exclusive, lock_wait, lock, append_only, storage_quota, args, v1_or_v2):
+def get_repository(location, *, create, exclusive, lock_wait, lock, append_only, args, v1_or_v2):
     if location.proto in ("ssh", "socket"):
         RemoteRepoCls = LegacyRemoteRepository if v1_or_v2 else RemoteRepository
         repository = RemoteRepoCls(
@@ -45,25 +45,13 @@ def get_repository(location, *, create, exclusive, lock_wait, lock, append_only,
 
     elif location.proto in ("sftp", "file", "rclone") and not v1_or_v2:  # stuff directly supported by borgstore
         repository = Repository(
-            location,
-            create=create,
-            exclusive=exclusive,
-            lock_wait=lock_wait,
-            lock=lock,
-            append_only=append_only,
-            storage_quota=storage_quota,
+            location, create=create, exclusive=exclusive, lock_wait=lock_wait, lock=lock, append_only=append_only
         )
 
     else:
         RepoCls = LegacyRepository if v1_or_v2 else Repository
         repository = RepoCls(
-            location.path,
-            create=create,
-            exclusive=exclusive,
-            lock_wait=lock_wait,
-            lock=lock,
-            append_only=append_only,
-            storage_quota=storage_quota,
+            location.path, create=create, exclusive=exclusive, lock_wait=lock_wait, lock=lock, append_only=append_only
         )
     return repository
 
@@ -127,7 +115,6 @@ def with_repository(
             assert isinstance(exclusive, bool)
             lock = getattr(args, "lock", _lock)
             append_only = getattr(args, "append_only", False)
-            storage_quota = getattr(args, "storage_quota", None)
 
             repository = get_repository(
                 location,
@@ -136,7 +123,6 @@ def with_repository(
                 lock_wait=self.lock_wait,
                 lock=lock,
                 append_only=append_only,
-                storage_quota=storage_quota,
                 args=args,
                 v1_or_v2=False,
             )
@@ -205,7 +191,6 @@ def with_other_repository(manifest=False, cache=False, compatibility=None):
                 lock_wait=self.lock_wait,
                 lock=True,
                 append_only=False,
-                storage_quota=None,
                 args=args,
                 v1_or_v2=v1_or_v2,
             )

+ 0 - 12
src/borg/archiver/repo_create_cmd.py

@@ -6,7 +6,6 @@ from ..constants import *  # NOQA
 from ..crypto.key import key_creator, key_argument_names
 from ..helpers import CancelledByUser, CommandError
 from ..helpers import location_validator, Location
-from ..helpers import parse_storage_quota
 from ..manifest import Manifest
 
 from ..logger import create_logger
@@ -19,8 +18,6 @@ class RepoCreateMixIn:
     @with_other_repository(manifest=True, compatibility=(Manifest.Operation.READ,))
     def do_repo_create(self, args, repository, *, other_repository=None, other_manifest=None):
         """Create a new, empty repository"""
-        if args.storage_quota is not None:
-            raise CommandError("storage-quota is not supported (yet?)")
         if args.append_only:
             raise CommandError("append-only is not supported (yet?)")
         other_key = other_manifest.key if other_manifest is not None else None
@@ -236,15 +233,6 @@ class RepoCreateMixIn:
             "or `prune` will still be allowed. See :ref:`append_only_mode` in "
             "Additional Notes for more details.",
         )
-        subparser.add_argument(
-            "--storage-quota",
-            metavar="QUOTA",
-            dest="storage_quota",
-            default=None,
-            type=parse_storage_quota,
-            action=Highlander,
-            help="Set storage quota of the new repository (e.g. 5G, 1.5T). Default: no quota.",
-        )
         subparser.add_argument(
             "--copy-crypt-key",
             dest="copy_crypt_key",

+ 1 - 10
src/borg/archiver/repo_info_cmd.py

@@ -3,7 +3,7 @@ import textwrap
 
 from ._common import with_repository
 from ..constants import *  # NOQA
-from ..helpers import bin_to_hex, json_print, basic_json_data, format_file_size
+from ..helpers import bin_to_hex, json_print, basic_json_data
 from ..manifest import Manifest
 
 from ..logger import create_logger
@@ -50,15 +50,6 @@ class RepoInfoMixIn:
                 )
             )
 
-            response = repository.info()
-            storage_quota = response["storage_quota"]
-            used = format_file_size(response["storage_quota_use"], iec=args.iec)
-
-            output += f"\nStorage quota: {used} used"
-            if storage_quota:
-                output += f" out of {format_file_size(storage_quota, iec=args.iec)}"
-            output += "\n"
-
             if hasattr(info["cache"], "path"):
                 output += "Cache: {cache.path}\n".format(**info)
             output += "Security dir: {security_dir}\n".format(**info)

+ 1 - 16
src/borg/archiver/serve_cmd.py

@@ -1,8 +1,7 @@
 import argparse
 
-from ._common import Highlander
 from ..constants import *  # NOQA
-from ..helpers import parse_storage_quota, CommandError
+from ..helpers import CommandError
 from ..remote import RepositoryServer
 
 from ..logger import create_logger
@@ -15,13 +14,10 @@ class ServeMixIn:
         """Start in server mode. This command is usually not used manually."""
         if args.append_only:
             raise CommandError("append-only is not supported (yet?)")
-        if args.storage_quota is not None:
-            raise CommandError("storage-quota is not supported (yet?)")
         RepositoryServer(
             restrict_to_paths=args.restrict_to_paths,
             restrict_to_repositories=args.restrict_to_repositories,
             append_only=args.append_only,
-            storage_quota=args.storage_quota,
             use_socket=args.use_socket,
         ).serve()
 
@@ -84,14 +80,3 @@ class ServeMixIn:
             "or `prune` will still be allowed. See :ref:`append_only_mode` in Additional "
             "Notes for more details.",
         )
-        subparser.add_argument(
-            "--storage-quota",
-            metavar="QUOTA",
-            dest="storage_quota",
-            type=parse_storage_quota,
-            default=None,
-            action=Highlander,
-            help="Override storage quota of the repository (e.g. 5G, 1.5T). "
-            "When a new repository is initialized, sets the storage quota on the new "
-            "repository as well. Default: no quota.",
-        )

+ 1 - 1
src/borg/helpers/__init__.py

@@ -30,7 +30,7 @@ from .parseformat import bin_to_hex, hex_to_bin, safe_encode, safe_decode
 from .parseformat import text_to_json, binary_to_json, remove_surrogates, join_cmd
 from .parseformat import eval_escapes, decode_dict, positive_int_validator, interval
 from .parseformat import PathSpec, SortBySpec, ChunkerParams, FilesCacheMode, partial_format, DatetimeWrapper
-from .parseformat import format_file_size, parse_file_size, FileSize, parse_storage_quota
+from .parseformat import format_file_size, parse_file_size, FileSize
 from .parseformat import sizeof_fmt, sizeof_fmt_iec, sizeof_fmt_decimal, Location, text_validator
 from .parseformat import format_line, replace_placeholders, PlaceholderError, relative_time_marker_validator
 from .parseformat import format_archive, parse_stringified_list, clean_lines

+ 0 - 7
src/borg/helpers/parseformat.py

@@ -357,13 +357,6 @@ def parse_file_size(s):
     return int(float(s) * factor)
 
 
-def parse_storage_quota(storage_quota):
-    parsed = parse_file_size(storage_quota)
-    if parsed < parse_file_size("10M"):
-        raise argparse.ArgumentTypeError("quota is too small (%s). At least 10M are required." % storage_quota)
-    return parsed
-
-
 def sizeof_fmt(num, suffix="B", units=None, power=None, sep="", precision=2, sign=False):
     sign = "+" if sign and num > 0 else ""
     fmt = "{0:{1}.{2}f}{3}{4}{5}"

+ 0 - 3
src/borg/legacyremote.py

@@ -409,9 +409,6 @@ class LegacyRemoteRepository:
                     topic = "borg.debug." + topic
                 if "repository" in topic:
                     opts.append("--debug-topic=%s" % topic)
-
-            if "storage_quota" in args and args.storage_quota:
-                opts.append("--storage-quota=%s" % args.storage_quota)
         env_vars = []
         if testing:
             return env_vars + [sys.executable, "-m", "borg", "serve"] + opts + self.extra_test_args

+ 9 - 59
src/borg/legacyrepository.py

@@ -183,10 +183,7 @@ class LegacyRepository:
 
         exit_mcode = 19
 
-    class StorageQuotaExceeded(Error):
-        """The storage quota ({}) has been exceeded ({}). Try deleting some archives."""
-
-        exit_mcode = 20
+    # StorageQuotaExceeded was exit_mcode = 20
 
     class PathPermissionDenied(Error):
         """Permission denied to {}."""
@@ -194,15 +191,7 @@ class LegacyRepository:
         exit_mcode = 21
 
     def __init__(
-        self,
-        path,
-        create=False,
-        exclusive=False,
-        lock_wait=None,
-        lock=True,
-        append_only=False,
-        storage_quota=None,
-        send_log_cb=None,
+        self, path, create=False, exclusive=False, lock_wait=None, lock=True, append_only=False, send_log_cb=None
     ):
         self.path = os.path.abspath(path)
         self._location = Location("file://%s" % self.path)
@@ -230,8 +219,6 @@ class LegacyRepository:
         self.created = False
         self.exclusive = exclusive
         self.append_only = append_only
-        self.storage_quota = storage_quota
-        self.storage_quota_use = 0
         self.transaction_doomed = None
         # v2 is the default repo version for borg 2.0
         # v1 repos must only be used in a read-only way, e.g. for
@@ -290,13 +277,9 @@ class LegacyRepository:
         """
         Raise an exception if a repository already exists at *path* or any parent directory.
 
-        Checking parent directories is done for two reasons:
-        (1) It's just a weird thing to do, and usually not intended. A Borg using the "parent" repository
-            may be confused, or we may accidentally put stuff into the "data/" or "data/<n>/" directories.
-        (2) When implementing repository quotas (which we currently don't), it's important to prohibit
-            folks from creating quota-free repositories. Since no one can create a repository within another
-            repository, user's can only use the quota'd repository, when their --restrict-to-path points
-            at the user's repository.
+        Checking parent directories is done because it's just a weird thing to do, and usually not intended.
+        A Borg using the "parent" repository may be confused, or we may accidentally put stuff into the "data/" or
+        "data/<n>/" directories.
         """
         try:
             st = os.stat(path)
@@ -345,10 +328,6 @@ class LegacyRepository:
         config.set("repository", "segments_per_dir", str(DEFAULT_SEGMENTS_PER_DIR))
         config.set("repository", "max_segment_size", str(DEFAULT_MAX_SEGMENT_SIZE))
         config.set("repository", "append_only", str(int(self.append_only)))
-        if self.storage_quota:
-            config.set("repository", "storage_quota", str(self.storage_quota))
-        else:
-            config.set("repository", "storage_quota", "0")
         config.set("repository", "additional_free_space", "0")
         config.set("repository", "id", bin_to_hex(os.urandom(32)))
         self.save_config(path, config)
@@ -492,9 +471,6 @@ class LegacyRepository:
         # append_only can be set in the constructor
         # it shouldn't be overridden (True -> False) here
         self.append_only = self.append_only or self.config.getboolean("repository", "append_only", fallback=False)
-        if self.storage_quota is None:
-            # self.storage_quota is None => no explicit storage_quota was specified, use repository setting.
-            self.storage_quota = parse_file_size(self.config.get("repository", "storage_quota", fallback=0))
         self.id = hex_to_bin(self.config.get("repository", "id").strip(), length=32)
         self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
 
@@ -504,15 +480,12 @@ class LegacyRepository:
             return
         hints = self._unpack_hints(transaction_id)
         self.version = hints["version"]
-        self.storage_quota_use = hints["storage_quota_use"]
         self.shadow_index = hints["shadow_index"]
 
     def info(self):
         """return some infos about the repo (must be opened first)"""
         info = dict(id=self.id, version=self.version, append_only=self.append_only)
         self._load_hints()
-        info["storage_quota"] = self.storage_quota
-        info["storage_quota_use"] = self.storage_quota_use
         return info
 
     def close(self):
@@ -604,7 +577,6 @@ class LegacyRepository:
         if transaction_id is None:
             self.segments = {}  # XXX bad name: usage_count_of_segment_x = self.segments[x]
             self.compact = FreeSpace()  # XXX bad name: freeable_space_of_segment_x = self.compact[x]
-            self.storage_quota_use = 0
             self.shadow_index.clear()
         else:
             if do_cleanup:
@@ -626,7 +598,6 @@ class LegacyRepository:
                 logger.debug("Upgrading from v1 hints.%d", transaction_id)
                 self.segments = hints["segments"]
                 self.compact = FreeSpace()
-                self.storage_quota_use = 0
                 self.shadow_index = {}
                 for segment in sorted(hints["compact"]):
                     logger.debug("Rebuilding sparse info for segment %d", segment)
@@ -637,7 +608,6 @@ class LegacyRepository:
             else:
                 self.segments = hints["segments"]
                 self.compact = FreeSpace(hints["compact"])
-                self.storage_quota_use = hints.get("storage_quota_use", 0)
                 self.shadow_index = hints.get("shadow_index", {})
             # Drop uncommitted segments in the shadow index
             for key, shadowed_segments in self.shadow_index.items():
@@ -653,13 +623,7 @@ class LegacyRepository:
         def rename_tmp(file):
             os.replace(file + ".tmp", file)
 
-        hints = {
-            "version": 2,
-            "segments": self.segments,
-            "compact": self.compact,
-            "storage_quota_use": self.storage_quota_use,
-            "shadow_index": self.shadow_index,
-        }
+        hints = {"version": 2, "segments": self.segments, "compact": self.compact, "shadow_index": self.shadow_index}
         integrity = {
             # Integrity version started at 2, the current hints version.
             # Thus, integrity version == hints version, for now.
@@ -783,7 +747,6 @@ class LegacyRepository:
         if not self.compact:
             logger.debug("Nothing to do: compact empty")
             return
-        quota_use_before = self.storage_quota_use
         index_transaction_id = self.get_index_transaction_id()
         segments = self.segments
         unused = []  # list of segments, that are not used anymore
@@ -855,9 +818,6 @@ class LegacyRepository:
                     segments.setdefault(new_segment, 0)
                     segments[new_segment] += 1
                     segments[segment] -= 1
-                    if tag == TAG_PUT:
-                        # old tag is PUT, but new will be PUT2 and use a bit more storage
-                        self.storage_quota_use += self.io.ENTRY_HASH_SIZE
                 elif tag in (TAG_PUT2, TAG_PUT) and not is_index_object:
                     # If this is a PUT shadowed by a later tag, then it will be gone when this segment is deleted after
                     # this loop. Therefore it is removed from the shadow index.
@@ -867,7 +827,6 @@ class LegacyRepository:
                         # do not remove entry with empty shadowed_segments list here,
                         # it is needed for shadowed_put_exists code (see below)!
                         pass
-                    self.storage_quota_use -= header_size(tag) + len(data)
                 elif tag == TAG_DELETE and not in_index:
                     # If the shadow index doesn't contain this key, then we can't say if there's a shadowed older tag,
                     # therefore we do not drop the delete, but write it to a current segment.
@@ -945,8 +904,6 @@ class LegacyRepository:
         self._send_log()
         complete_xfer(intermediate=False)
         self.io.clear_empty_dirs()
-        quota_use_after = self.storage_quota_use
-        logger.info("Compaction freed about %s repository space.", format_file_size(quota_use_before - quota_use_after))
         logger.debug("Compaction completed.")
 
     def replay_segments(self, index_transaction_id, segments_transaction_id):
@@ -990,7 +947,6 @@ class LegacyRepository:
                     pass
                 self.index[key] = NSIndex1Entry(segment, offset)
                 self.segments[segment] += 1
-                self.storage_quota_use += header_size(tag) + size
             elif tag == TAG_DELETE:
                 try:
                     # if the deleted PUT is not in the index, there is nothing to clean up
@@ -1232,20 +1188,14 @@ class LegacyRepository:
             pass
         else:
             # this put call supersedes a previous put to same id.
-            # it is essential to do a delete first to get correct quota bookkeeping
-            # and also a correctly updated shadow_index, so that the compaction code
-            # does not wrongly resurrect an old PUT by dropping a DEL that is still needed.
+            # it is essential to do a delete first to get a correctly updated shadow_index,
+            # so that the compaction code does not wrongly resurrect an old PUT by
+            # dropping a DEL that is still needed.
             self._delete(id, in_index.segment, in_index.offset, 0)
         segment, offset = self.io.write_put(id, data)
-        self.storage_quota_use += header_size(TAG_PUT2) + len(data)
         self.segments.setdefault(segment, 0)
         self.segments[segment] += 1
         self.index[id] = NSIndex1Entry(segment, offset)
-        if self.storage_quota and self.storage_quota_use > self.storage_quota:
-            self.transaction_doomed = self.StorageQuotaExceeded(
-                format_file_size(self.storage_quota), format_file_size(self.storage_quota_use)
-            )
-            raise self.transaction_doomed
 
     def delete(self, id, wait=True):
         """delete a repo object

+ 1 - 9
src/borg/remote.py

@@ -183,7 +183,7 @@ class RepositoryServer:  # pragma: no cover
         "store_move",
     )
 
-    def __init__(self, restrict_to_paths, restrict_to_repositories, append_only, storage_quota, use_socket):
+    def __init__(self, restrict_to_paths, restrict_to_repositories, append_only, use_socket):
         self.repository = None
         self.RepoCls = None
         self.rpc_methods = ("open", "close", "negotiate")
@@ -194,7 +194,6 @@ class RepositoryServer:  # pragma: no cover
         # whatever the client wants, except when initializing a new repository
         # (see RepositoryServer.open below).
         self.append_only = append_only
-        self.storage_quota = storage_quota
         self.client_version = None  # we update this after client sends version information
         if use_socket is False:
             self.socket_path = None
@@ -276,7 +275,6 @@ class RepositoryServer:  # pragma: no cover
                                 Repository.PathAlreadyExists,
                                 PathNotAllowed,
                                 Repository.InsufficientFreeSpaceError,
-                                Repository.StorageQuotaExceeded,
                             )
                             # logger.exception(e)
                             ex_short = traceback.format_exception_only(e.__class__, e)
@@ -407,7 +405,6 @@ class RepositoryServer:  # pragma: no cover
             lock_wait=lock_wait,
             lock=lock,
             append_only=append_only,
-            storage_quota=self.storage_quota,
             exclusive=exclusive,
             send_log_cb=self.send_queued_log,
         )
@@ -735,9 +732,6 @@ class RemoteRepository:
                     topic = "borg.debug." + topic
                 if "repository" in topic:
                     opts.append("--debug-topic=%s" % topic)
-
-            if "storage_quota" in args and args.storage_quota:
-                opts.append("--storage-quota=%s" % args.storage_quota)
         env_vars = []
         if testing:
             return env_vars + [sys.executable, "-m", "borg", "serve"] + opts + self.extra_test_args
@@ -834,8 +828,6 @@ class RemoteRepository:
                 raise Repository.InsufficientFreeSpaceError(args[0], args[1])
             elif error == "InvalidRepositoryConfig":
                 raise Repository.InvalidRepositoryConfig(self.location.processed, args[1])
-            elif error == "StorageQuotaExceeded":
-                raise Repository.StorageQuotaExceeded(args[0], args[1])
             else:
                 raise self.RPCError(unpacked)
 

+ 2 - 14
src/borg/repository.py

@@ -86,10 +86,7 @@ class Repository:
 
         exit_mcode = 19
 
-    class StorageQuotaExceeded(Error):
-        """The storage quota ({}) has been exceeded ({}). Try deleting some archives."""
-
-        exit_mcode = 20
+    # StorageQuotaExceeded was exit_mcode = 20
 
     class PathPermissionDenied(Error):
         """Permission denied to {}."""
@@ -104,7 +101,6 @@ class Repository:
         lock_wait=1.0,
         lock=True,
         append_only=False,
-        storage_quota=None,
         send_log_cb=None,
     ):
         if isinstance(path_or_location, Location):
@@ -144,8 +140,6 @@ class Repository:
         self.acceptable_repo_versions = (3,)
         self.opened = False
         self.append_only = append_only  # XXX not implemented / not implementable
-        self.storage_quota = storage_quota  # XXX not implemented
-        self.storage_quota_use = 0  # XXX not implemented
         self.lock = None
         self.do_lock = lock
         self.lock_wait = lock_wait
@@ -260,13 +254,7 @@ class Repository:
         """return some infos about the repo (must be opened first)"""
         # note: don't do anything expensive here or separate the lock refresh into a separate method.
         self._lock_refresh()  # do not remove, see do_with_lock()
-        info = dict(
-            id=self.id,
-            version=self.version,
-            storage_quota_use=self.storage_quota_use,
-            storage_quota=self.storage_quota,
-            append_only=self.append_only,
-        )
+        info = dict(id=self.id, version=self.version, append_only=self.append_only)
         return info
 
     def check(self, repair=False, max_duration=0):

+ 0 - 7
src/borg/testsuite/archiver/argparsing_test.py

@@ -1,7 +1,6 @@
 import argparse
 import pytest
 
-from ...helpers import parse_storage_quota
 from . import Archiver, RK_ENCRYPTION, cmd
 
 
@@ -187,9 +186,3 @@ class TestCommonOptions:
         }
 
         assert parse_vars_from_line(*line) == result
-
-
-def test_parse_storage_quota():
-    assert parse_storage_quota("50M") == 50 * 1000**2
-    with pytest.raises(argparse.ArgumentTypeError):
-        parse_storage_quota("5M")

+ 0 - 8
src/borg/testsuite/legacyrepository_test.py

@@ -1051,15 +1051,7 @@ def test_remote_borg_cmd(remote_repository):
             "--debug-topic=borg.debug.repository_compaction",
         ]
         args = _get_mock_args()
-        args.storage_quota = 0
         assert remote_repository.borg_cmd(args, testing=False) == ["borg", "serve", "--info"]
-        args.storage_quota = 314159265
-        assert remote_repository.borg_cmd(args, testing=False) == [
-            "borg",
-            "serve",
-            "--info",
-            "--storage-quota=314159265",
-        ]
         args.rsh = "ssh -i foo"
         remote_repository._args = args
         assert remote_repository.ssh_cmd(Location("ssh://example.com/foo")) == ["ssh", "-i", "foo", "example.com"]

+ 1 - 1
src/borg/testsuite/remote_test.py

@@ -41,7 +41,7 @@ class TestSleepingBandwidthLimiter:
 
         now = 100
 
-        it = SleepingBandwidthLimiter(100)
+        it = SleepingBandwidthLimiter(100)  # bandwidth quota
 
         # all fits
         self.expect_write(5, b"test")

+ 0 - 8
src/borg/testsuite/repository_test.py

@@ -263,15 +263,7 @@ def test_remote_borg_cmd(remote_repository):
             "--debug-topic=borg.debug.repository_compaction",
         ]
         args = _get_mock_args()
-        args.storage_quota = 0
         assert remote_repository.borg_cmd(args, testing=False) == ["borg", "serve", "--info"]
-        args.storage_quota = 314159265
-        assert remote_repository.borg_cmd(args, testing=False) == [
-            "borg",
-            "serve",
-            "--info",
-            "--storage-quota=314159265",
-        ]
         args.rsh = "ssh -i foo"
         remote_repository._args = args
         assert remote_repository.ssh_cmd(Location("ssh://example.com/foo")) == ["ssh", "-i", "foo", "example.com"]