commit c5d15ba9b5213d8ccd16dbc78f599b2057509874
Author: Jim Meyering <meyering(a)redhat.com>
Date: Fri Mar 9 22:36:58 2012 +0100
reject attempt to add attribute k/v pair to a nonexistent object
* rest.c (proxy_put_attr): Fail with NOT_FOUND when attempting to
apply an attribute to a nonexistent object.
* t/put-attr-on-nonexistent-object: Test for the above.
* t/Makefile.am (TESTS): Add it.
* t/replication: Create each test object before trying to set
its attributes.
* NEWS (Bug fixes): Mention this.
Reported by Shveta Sachdeva in
http://bugzilla.redhat.com/795403
As Pete Zaitecev noted in review, iwhd is still subject to
this problem due to the inherent TOCTOU race: the object whose
existence we've just confirmed may be removed by another party
in the brief interval before we add the attribute.
NEWS | 7 ++++
rest.c | 9 ++++++
t/Makefile.am | 1 +
t/put-attr-on-nonexistent-object | 59 ++++++++++++++++++++++++++++++++++++++
t/replication | 2 +
5 files changed, 78 insertions(+), 0 deletions(-)
---
diff --git a/NEWS b/NEWS
index e919cda..17c2326 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,13 @@ iwhd NEWS -*- outline
-*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** Bug fixes
+
+ iwhd now rejects an attempt to add attribute k/v pair to a nonexistent
+ object. Before, it would add the attribute, which would then make it
+ appear (listing the parent bucket) as if the still-nonexistent object
+ existed.
+
* Noteworthy changes in release 1.4 (2012-03-05) [stable]
diff --git a/rest.c b/rest.c
index 1aca0af..832c486 100644
--- a/rest.c
+++ b/rest.c
@@ -727,6 +727,15 @@ proxy_put_attr (void *cctx, struct MHD_Connection *conn, const char
*url,
DPRINTF("PROXY PUT ATTR %s (%zu)\n",url,*data_size);
if (ms->state == MS_NEW) {
+ /* Given a non-reserved bucket name, reject any attempt
+ to attach attributes to a nonexistent bucket/key. */
+ if (!is_reserved(ms->bucket, reserved_bucket_name)
+ && !is_reserved(ms->key, reserved_name)) {
+ char *my_etag = meta_has_copy(ms->bucket,ms->key,me);
+ if (!my_etag || *my_etag == 0)
+ return do_resp(conn,ms,MHD_HTTP_NOT_FOUND,NULL,
+ "404 no such object\r\n");
+ }
ms->state = MS_NORMAL;
ms->url = strdup(url);
if (!ms->url) {
diff --git a/t/Makefile.am b/t/Makefile.am
index 77094b9..2b4b2e7 100644
--- a/t/Makefile.am
+++ b/t/Makefile.am
@@ -22,6 +22,7 @@ TESTS = \
exercise \
mongo-restart \
provider \
+ put-attr-on-nonexistent-object \
replication \
timeout \
auto \
diff --git a/t/put-attr-on-nonexistent-object b/t/put-attr-on-nonexistent-object
new file mode 100755
index 0000000..721c74a
--- /dev/null
+++ b/t/put-attr-on-nonexistent-object
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Adding an attribute to a nonexistent object must fail.
+# Before iwhd-1.5, it would succeed, and a subsequent listing
+# of the containing bucket would show the nonexistent object.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ..
+
+mkdir FS mongod iwhd || framework_failure_ mkdir failed
+
+m_port=$(get_port $mongo_base_port $lock_dir/m-) \
+ || fail_ "failed to get mongodb port"
+
+mongod --help > /dev/null || fail_ "the mongod program is not installed"
+
+mongod $mongo_opt --port $m_port --pidfilepath mongod/pid --dbpath mongod &
+mongo_pid=$!
+cleanup_() { kill -9 $mongo_pid; }
+
+# Wait for up to 5 seconds for mongod to begin listening.
+wait_for_mongo $m_port \
+ || framework_failure_ mongod failed to start
+
+port=$(get_port 9095 $lock_dir/i-) || fail_ "failed to get iwhd port"
+
+printf '[{"path": "FS", "type": "fs",
"name": "primary"}]\n' \
+ > iwhd.cfg || fail=1
+
+iwhd -v -p $port -c iwhd.cfg -d localhost:$m_port &
+iwhd_pid=$!
+cleanup_() { kill -9 $mongo_pid; kill $iwhd_pid; }
+
+# Wait for up to 5 seconds for iwhd to begin listening on $port.
+wait_for_mongo $m_port \
+ || { echo iwhd failed to listen; Exit 1; }
+
+
+curl_w() { curl --write-out '%{http_code}' "$@"; }
+
+apply_attribute_to_nonexistent_object ()
+{
+ # Create a bucket.
+ local b=http://localhost:$port/b
+ curl_w -XPUT $b > http-code || fail=1
+ test "$(cat http-code)" = 201 || fail=1
+
+ # Trying to add an attribute to a non-existent object evokes 404 NOT_FOUND
+ local attr=$b/o/color
+ printf blue | curl_w -T - $attr > http-code || fail=1
+ test "$(tr '\012\015' :: < http-code)" = '404 no such
object::404' || fail=1
+
+ # Ensure that the object does not appear in a listing:
+ printf '[\n]\n200' > exp || fail=1
+ curl_w -H 'Accept: */json' $b > out || fail=1
+ compare out exp || fail=1
+}
+
+apply_attribute_to_nonexistent_object
+
+Exit $fail
diff --git a/t/replication b/t/replication
index 6ef09a5..4a9cc99 100755
--- a/t/replication
+++ b/t/replication
@@ -89,12 +89,14 @@ printf '$color==#color' | curl -T - $bkt/_default/_policy ||
fail=1
# Positive test.
# NB this file is also used by the delete test below
+curl -X PUT $bkt/sel_pos || fail=1
printf blue | curl -T - $bkt/sel_pos/color || fail=1
printf foo | curl -T - $bkt/sel_pos || fail=1
wait_for .1 50 "wait_for_repl $api" || fail_ "replication seems
stuck"
test -f fs_downstream/rbucket/sel_pos || fail=1
# Negative test.
+curl -X PUT $bkt/sel_neg || fail=1
printf red | curl -T - $bkt/sel_neg/color || fail=1
printf foo | curl -T - $bkt/sel_neg || fail=1
wait_for .1 50 "wait_for_repl $api" || fail_ "replication seems
stuck"