]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Add RFC4331 quotas for mod_dav_fs
authormanu <manu@unknown>
Thu, 2 Mar 2023 14:36:31 +0000 (14:36 +0000)
committermanu <manu@unknown>
Thu, 2 Mar 2023 14:36:31 +0000 (14:36 +0000)
This is enabled by a new directive: DavQuota (Off|None|#bytes)
Off (default): feature disabled
None: no quota enforced, but used and available bytes are reported
#bytes: an numerical value in bytes as the quota to enforce

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1907974 13f79535-47bb-0310-9956-ffa450edef68

CMakeLists.txt
modules/dav/fs/config6.m4
modules/dav/fs/mod_dav_fs.c
modules/dav/fs/mod_dav_fs.dsp
modules/dav/fs/repos.c
modules/dav/fs/repos.h
modules/dav/main/mod_dav.h

index 95c95eb2c2a12e8fde1e1c22a51681edc43d67af..3ed34c7e10e2281f5f2bc17c9fa76ecc837ce970 100644 (file)
@@ -455,7 +455,7 @@ SET(mod_dav_extra_sources
 SET(mod_dav_install_lib 1)
 SET(mod_dav_fs_extra_sources
   modules/dav/fs/dbm.c               modules/dav/fs/lock.c
-  modules/dav/fs/repos.c
+  modules/dav/fs/quota.c             modules/dav/fs/repos.c
 )
 SET(mod_dav_fs_extra_libs            mod_dav)
 SET(mod_dav_lock_extra_sources       modules/dav/lock/locks.c)
index dd26ec8bab960a60ad038d81b776d62e9d7b4dee..ee0b382f15c83d7516b498dcc2c99900ffe3b674 100644 (file)
@@ -2,7 +2,7 @@ dnl modules enabled in this directory by default
 
 APACHE_MODPATH_INIT(dav/fs)
 
-dav_fs_objects="mod_dav_fs.lo dbm.lo lock.lo repos.lo"
+dav_fs_objects="mod_dav_fs.lo dbm.lo lock.lo quota.lo repos.lo"
 
 if test "x$enable_dav" != "x"; then
   dav_fs_enable=$enable_dav
index 985823d36f94c43aef06b22a288ab30925cb7451..49986fc747ca3f1e9e1a3f8d93565caa9970c735 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "httpd.h"
 #include "http_config.h"
+#include "http_request.h"
 #include "apr_strings.h"
 #if !defined(_MSC_VER) && !defined(NETWARE)
 #include "ap_config_auto.h"
 #include "mod_dav.h"
 #include "repos.h"
 
+/* per-dir configuration */
+typedef struct {
+    const char *dir;
+    apr_off_t quota;
+} dav_fs_dir_conf;
+
 /* per-server configuration */
 typedef struct {
     const char *lockdb_path;
@@ -44,6 +51,60 @@ const char *dav_get_lockdb_path(const request_rec *r)
     return conf->lockdb_path;
 }
 
+static const command_rec dav_fs_cmds[];
+
+dav_error *dav_fs_get_quota(const request_rec *r, const char *path,
+                            apr_off_t *quota_bytes)
+{
+    dav_fs_dir_conf *conf = NULL;
+    dav_error *err = NULL;
+    const char *request_path;
+    request_rec *rr;
+    int status;
+
+    request_path = ap_make_dirstr_parent(r->pool, r->filename);
+
+    /* 
+     * Uses's request's per directry configuration if possible, for
+     * efficiency sake.
+     */
+    if (!strcmp(path, request_path)) {
+        conf = ap_get_module_config(r->per_dir_config, &dav_fs_module);
+        *quota_bytes = conf->quota;
+        goto out;
+    }
+
+    /* 
+     * We need for a per directory configuration from a random path
+     * not tied to current request, for e.g. COPY or MOVE destination.
+     * This is done through a subrequest, with just rr->filename
+     * changed to target path.
+     */
+    rr = ap_sub_req_method_uri(r->method, r->uri, r, r->output_filters);
+    if (!rr || rr->status != HTTP_OK) {
+        err = dav_new_error(r->pool,
+                            rr ? rr->status : HTTP_INTERNAL_SERVER_ERROR,
+                            0, 0,
+                            "quota configuration subrequest failed");
+        *quota_bytes = DAV_FS_BYTES_ERROR;
+        goto out;
+    }
+
+    rr->filename = apr_pstrdup(r->pool, path);
+    if ((status = ap_directory_walk(rr)) != OK)  {
+        err = dav_new_error(r->pool, status, 0, 0,
+                            "quota configuration tree walk failed");
+        *quota_bytes = DAV_FS_BYTES_ERROR;
+        goto out;
+    }
+
+    conf = ap_get_module_config(rr->per_dir_config, &dav_fs_module);
+    *quota_bytes = conf->quota;
+
+out:
+    return err;
+}
+
 static void *dav_fs_create_server_config(apr_pool_t *p, server_rec *s)
 {
     return apr_pcalloc(p, sizeof(dav_fs_server_conf));
@@ -64,6 +125,37 @@ static void *dav_fs_merge_server_config(apr_pool_t *p,
     return newconf;
 }
 
+static void *dav_fs_create_dir_config(apr_pool_t *p, char *dir)
+{
+    /* NOTE: dir==NULL creates the default per-dir config */
+
+    dav_fs_dir_conf *conf;
+
+    conf = (dav_fs_dir_conf *)apr_pcalloc(p, sizeof(*conf));
+    conf->dir = apr_pstrdup(p, dir);
+    conf->quota = DAV_FS_QUOTA_UNSET;
+
+    return conf;
+}
+
+static void *dav_fs_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
+{
+    dav_fs_dir_conf *parent = base;
+    dav_fs_dir_conf *child = overrides;
+    dav_fs_dir_conf *newconf =
+        (dav_fs_dir_conf *)apr_pcalloc(p, sizeof(*newconf));
+
+    newconf->dir = child->dir;
+
+    if (child->quota != DAV_FS_QUOTA_UNSET)
+        newconf->quota = child->quota;
+    else
+        newconf->quota = parent->quota;
+
+    return newconf;
+}
+
+
 static apr_status_t dav_fs_post_config(apr_pool_t *p, apr_pool_t *plog,
                                        apr_pool_t *ptemp, server_rec *base_server)
 {
@@ -101,12 +193,35 @@ static const char *dav_fs_cmd_davlockdb(cmd_parms *cmd, void *config,
     return NULL;
 }
 
+/*
+ * Command handler for the DAVquota directive, which is TAKE1
+ */
+static const char *dav_fs_cmd_quota(cmd_parms *cmd, void *config,
+                                    const char *bytes)
+{
+    dav_fs_dir_conf *conf = (dav_fs_dir_conf *)config;
+
+    if (!strcasecmp(bytes, "Off"))
+        conf->quota = DAV_FS_QUOTA_OFF;
+    else if (!strcasecmp(bytes, "None"))
+        conf->quota = DAV_FS_QUOTA_NONE;
+    else
+        conf->quota = atol(bytes);
+
+    return NULL;
+}
+
+
 static const command_rec dav_fs_cmds[] =
 {
     /* per server */
     AP_INIT_TAKE1("DAVLockDB", dav_fs_cmd_davlockdb, NULL, RSRC_CONF,
                   "specify a lock database"),
 
+    /* per directory */
+    AP_INIT_TAKE1("DAVquota", dav_fs_cmd_quota, NULL, ACCESS_CONF|RSRC_CONF,
+                  "specify a directory quota"),
+
     { NULL }
 };
 
@@ -119,6 +234,8 @@ static void register_hooks(apr_pool_t *p)
     dav_hook_find_liveprop(dav_fs_find_liveprop, NULL, NULL, APR_HOOK_MIDDLE);
     dav_hook_insert_all_liveprops(dav_fs_insert_all_liveprops, NULL, NULL,
                                   APR_HOOK_MIDDLE);
+    dav_hook_method_precondition(dav_fs_method_precondition, NULL, NULL,
+                                  APR_HOOK_MIDDLE);
 
     dav_fs_register(p);
 }
@@ -126,8 +243,8 @@ static void register_hooks(apr_pool_t *p)
 AP_DECLARE_MODULE(dav_fs) =
 {
     STANDARD20_MODULE_STUFF,
-    NULL,                        /* dir config creater */
-    NULL,                        /* dir merger --- default is to override */
+    dav_fs_create_dir_config,    /* dir config */
+    dav_fs_merge_dir_config,     /* merger dir config */
     dav_fs_create_server_config, /* server config */
     dav_fs_merge_server_config,  /* merge server config */
     dav_fs_cmds,                 /* command table */
index fbfc1e4378663a6255816343542be4b9f7dd9648..e4b873066e9f3a48c363dfc90a751e5f1d52e63f 100644 (file)
@@ -116,6 +116,10 @@ SOURCE=.\mod_dav_fs.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\quota.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\repos.c
 # End Source File
 # End Group
index d38868c70a1a2d81a94b4d58237e5edd89d1f3db..889d728a1b7c8112cc65ecfb8ab21e10fe1585b8 100644 (file)
@@ -140,11 +140,6 @@ enum {
 */
 #define DAV_PROPID_FS_executable        1
 
-/*
- * prefix for temporary files
- */
-#define DAV_FS_TMP_PREFIX ".davfs.tmp"
-
 static const dav_liveprop_spec dav_fs_props[] =
 {
     /* standard DAV properties */
@@ -173,6 +168,20 @@ static const dav_liveprop_spec dav_fs_props[] =
         0
     },
 
+    /* RFC 4331 quotas */
+    {
+        DAV_FS_URI_DAV,
+        "quota-available-bytes",
+        DAV_PROPID_quota_available_bytes,
+        0,
+    },
+    {
+        DAV_FS_URI_DAV,
+        "quota-used-bytes",
+        DAV_PROPID_quota_used_bytes,
+        0,
+    },
+
     /* our custom properties */
     {
         DAV_FS_URI_MYPROPS,
@@ -234,6 +243,24 @@ const char *dav_fs_pathname(const dav_resource *resource)
     return resource->info->pathname;
 }
 
+const char *dav_fs_fname(const dav_resource *resource)
+{
+    return resource->info->finfo.fname;
+}
+
+apr_off_t dav_fs_size(const dav_resource *resource)
+{
+    apr_off_t size;
+
+    if ((resource->info->finfo.valid & APR_FINFO_SIZE))
+        size = resource->info->finfo.size;
+    else
+        size = DAV_FS_BYTES_ERROR;
+
+    return size;
+}
+
+
 dav_error * dav_fs_dir_file_name(
     const dav_resource *resource,
     const char **dirpath_p,
@@ -1927,6 +1954,7 @@ static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource,
     apr_pool_t *p = resource->info->pool;
     const dav_liveprop_spec *info;
     long global_ns;
+    apr_off_t bytes;
 
     /* an HTTP-date can be 29 chars plus a null term */
     /* a 64-bit size can be 20 chars plus a null term */
@@ -1992,6 +2020,26 @@ static dav_prop_insert dav_fs_insert_prop(const dav_resource *resource,
             value = "F";
         break;
 
+    case DAV_PROPID_quota_available_bytes:
+        bytes = dav_fs_get_available_bytes(dav_fs_get_request_rec(resource),
+                                           dav_fs_fname(resource), NULL);
+        if (bytes == DAV_FS_BYTES_ERROR)
+            return DAV_PROP_INSERT_NOTDEF;
+
+        apr_snprintf(buf, sizeof(buf), "%" APR_OFF_T_FMT, bytes);
+        value = buf;
+        break;
+
+    case DAV_PROPID_quota_used_bytes:
+        bytes = dav_fs_get_used_bytes(dav_fs_get_request_rec(resource),
+                                           dav_fs_fname(resource));
+        if (bytes == DAV_FS_BYTES_ERROR)
+            return DAV_PROP_INSERT_NOTDEF;
+
+        apr_snprintf(buf, sizeof(buf), "%" APR_OFF_T_FMT, bytes);
+        value = buf;
+        break;
+
     default:
         /* ### what the heck was this property? */
         return DAV_PROP_INSERT_NOTDEF;
@@ -2256,9 +2304,36 @@ void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
                               what, phdr);
 #endif
 
+    /*
+     * RFC 4331 section 2 says quota live properties should not
+     * be returned by <DAV:allprop> PROPFIND, hence we skip
+     " DAV_PROPID_quota_available_bytes and DAV_PROPID_quota_used_bytes.
+     */
+
     /* ### we know the others aren't defined as liveprops */
 }
 
+int dav_fs_method_precondition(request_rec *r,
+                               dav_resource *src, const dav_resource *dst,
+                               const apr_xml_doc *doc, dav_error **err)
+{
+    int ret = DECLINED;
+
+    switch (r->method_number) {
+    case M_COPY: /* FALLTHROUGH */
+    case M_MOVE: /* FALLTHROUGH */
+    case M_MKCOL: /* FALLTHROUGH */
+    case M_PROPPATCH: /* FALLTHROUGH */
+    case M_PUT:
+        ret = dav_fs_quota_precondition(r, src, dst, doc, err);
+        break;
+    default:
+        break;
+    }
+
+    return ret;
+}
+
 void dav_fs_register(apr_pool_t *p)
 {
     /* register the namespace URIs */
index b1646118888fe2623019cda7597cd1f3f2c3f328..fa3b138ea3e1a1c6e2913109a4dabddf3789adc3 100644 (file)
 #define DAV_FS_STATE_DIR                ".DAV"
 #define DAV_FS_STATE_FILE_FOR_DIR       ".state_for_dir"
 #define DAV_FS_LOCK_NULL_FILE           ".locknull"
+#define DAV_FS_TMP_PREFIX               ".davfs.tmp" /* prefix for tmp files */
 
+#define DAV_FS_QUOTA_UNSET       0
+#define DAV_FS_QUOTA_OFF        -1
+#define DAV_FS_QUOTA_NONE       -2
+
+#define DAV_FS_BYTES_ERROR      -1
 
 /* ensure that our state subdirectory is present */
 void dav_fs_ensure_state_dir(apr_pool_t *p, const char *dirname);
@@ -40,6 +46,13 @@ apr_pool_t *dav_fs_pool(const dav_resource *resource);
 /* return the full pathname for a resource */
 const char *dav_fs_pathname(const dav_resource *resource);
 
+/* same as dav_fs_pathname() with directories' trailing slash */
+const char *dav_fs_fname(const dav_resource *resource);
+
+/* return the size for a resource, -1 if unknown */
+apr_off_t dav_fs_size(const dav_resource *resource);
+
+
 /* return the directory and filename for a resource */
 dav_error * dav_fs_dir_file_name(const dav_resource *resource,
                                  const char **dirpath,
@@ -67,6 +80,12 @@ void dav_dbm_close(dav_db *db);
 /* where is the lock database located? */
 const char *dav_get_lockdb_path(const request_rec *r);
 
+dav_error *dav_fs_get_quota(const request_rec *r, const char *path,
+                            apr_off_t *quota_bytes);
+apr_off_t dav_fs_get_used_bytes(request_rec *r, const char *path);
+apr_off_t dav_fs_get_available_bytes(request_rec *r,
+                                     const char *path, int *fs_low);
+
 const dav_hooks_locks *dav_fs_get_lock_hooks(request_rec *r);
 const dav_hooks_propdb *dav_fs_get_propdb_hooks(request_rec *r);
 
@@ -76,6 +95,12 @@ int dav_fs_find_liveprop(const dav_resource *resource,
                          const dav_hooks_liveprop **hooks);
 void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
                                  dav_prop_insert what, apr_text_header *phdr);
+int dav_fs_quota_precondition(request_rec *r,
+                              dav_resource *src, const dav_resource *dst,
+                              const apr_xml_doc *doc, dav_error **err);
+int dav_fs_method_precondition(request_rec *r,
+                               dav_resource *src, const dav_resource *dst,
+                               const apr_xml_doc *doc, dav_error **err);
 
 void dav_fs_register(apr_pool_t *p);
 
index b0c6f526bd114afd500c78ebdb8e20311a313192..fcf146c694ce7001d3ca9dcddcc9fa293081de92 100644 (file)
@@ -1172,6 +1172,10 @@ enum {
     DAV_PROPID_workspace,
     DAV_PROPID_workspace_checkout_set,
 
+    /* RFC 4331 quotas */
+    DAV_PROPID_quota_available_bytes,
+    DAV_PROPID_quota_used_bytes,
+
     DAV_PROPID_END
 };