commit 1e47e49d3960411570be77e013aac15711c315c3
Author: Chrysostomos Nanakos <cnanakos@grnet.gr>
Date:   Tue Nov 12 17:22:58 2013 +0200

    pithos: Add support for recognizing Archipelago Volumes
    
    Fix mapfile support the new snapshot naming
    
    Introduce new IllegalOperationError exception
    
    Add access control to Archipelago Volume hashmaps and blocks.
    Raise an IllegalOperationError exception when trying to update an
    Archipelago Volume

diff --git a/snf-pithos-app/pithos/api/functions.py b/snf-pithos-app/pithos/api/functions.py
index 3a0dbaf..5ea232b 100644
--- a/snf-pithos-app/pithos/api/functions.py
+++ b/snf-pithos-app/pithos/api/functions.py
@@ -66,7 +66,7 @@ from pithos.api import settings
 
 from pithos.backends.base import (
     NotAllowedError, QuotaError, ContainerNotEmpty, ItemNotExists,
-    VersionNotExists, ContainerExists, InvalidHash)
+    VersionNotExists, ContainerExists, InvalidHash, IllegalOperationError)
 
 from pithos.backends.filter import parse_filters
 
@@ -1115,6 +1115,8 @@ def object_write(request, v_account, v_container, v_object):
             request.user_uniq, v_account, v_container, v_object, size,
             content_type, hashmap, checksum, 'pithos', meta, True, permissions
         )
+    except IllegalOperationError, e:
+        raise faults.Forbidden(e[0])
     except NotAllowedError:
         raise faults.Forbidden('Not allowed')
     except IndexError, e:
@@ -1176,6 +1178,8 @@ def object_write_form(request, v_account, v_container, v_object):
             request.user_uniq, v_account, v_container, v_object, file.size,
             file.content_type, file.hashmap, checksum, 'pithos', {}, True
         )
+    except IllegalOperationError, e:
+        faults.Forbidden(e[0])
     except NotAllowedError:
         raise faults.Forbidden('Not allowed')
     except ItemNotExists:
@@ -1437,8 +1441,11 @@ def object_update(request, v_account, v_container, v_object):
                         hashmap[bi] = src_hashmap[sbi]
                     else:
                         data = request.backend.get_block(src_hashmap[sbi])
-                        hashmap[bi] = request.backend.update_block(
-                            hashmap[bi], data[:bl], 0)
+                        try:
+                            hashmap[bi] = request.backend.update_block(
+                                hashmap[bi], data[:bl], 0)
+                        except IllegalOperationError, e:
+                            raise faults.Forbidden(e[0])
                 else:
                     hashmap.append(src_hashmap[sbi])
                 offset += bl
@@ -1485,6 +1492,8 @@ def object_update(request, v_account, v_container, v_object):
             prev_meta['type'], hashmap, checksum, 'pithos', meta, replace,
             permissions
         )
+    except IllegalOperationError, e:
+        raise faults.Forbidden(e[0])
     except NotAllowedError:
         raise faults.Forbidden('Not allowed')
     except ItemNotExists:
diff --git a/snf-pithos-app/pithos/api/util.py b/snf-pithos-app/pithos/api/util.py
index d2ee895..cef9e9b 100644
--- a/snf-pithos-app/pithos/api/util.py
+++ b/snf-pithos-app/pithos/api/util.py
@@ -72,7 +72,7 @@ from pithos.api.settings import (BACKEND_DB_MODULE, BACKEND_DB_CONNECTION,
 from pithos.api.resources import resources
 from pithos.backends import connect_backend
 from pithos.backends.base import (NotAllowedError, QuotaError, ItemNotExists,
-                                  VersionNotExists)
+                                  VersionNotExists, IllegalOperationError)
 
 from synnefo.lib import join_urls
 from synnefo.util import text
@@ -967,7 +967,11 @@ def put_object_block(request, hashmap, data, offset):
     bo = offset % request.backend.block_size
     bl = min(len(data), request.backend.block_size - bo)
     if bi < len(hashmap):
-        hashmap[bi] = request.backend.update_block(hashmap[bi], data[:bl], bo)
+        try:
+            hashmap[bi] = request.backend.update_block(hashmap[bi],
+                                                       data[:bl], bo)
+        except IllegalOperationError, e:
+            raise faults.Forbidden(e)
     else:
         hashmap.append(request.backend.put_block(('\x00' * bo) + data[:bl]))
     return bl  # Return ammount of data written.
diff --git a/snf-pithos-backend/pithos/backends/base.py b/snf-pithos-backend/pithos/backends/base.py
index 61efffc..3a9903d 100644
--- a/snf-pithos-backend/pithos/backends/base.py
+++ b/snf-pithos-backend/pithos/backends/base.py
@@ -41,6 +41,10 @@ class NotAllowedError(Exception):
     pass
 
 
+class IllegalOperationError(NotAllowedError):
+    pass
+
+
 class QuotaError(Exception):
     pass
 
@@ -68,9 +72,11 @@ class ItemNotExists(NameError):
 class VersionNotExists(IndexError):
     pass
 
+
 class InvalidHash(TypeError):
     pass
 
+
 class BaseBackend(object):
     """Abstract backend class.
 
diff --git a/snf-pithos-backend/pithos/backends/modular.py b/snf-pithos-backend/pithos/backends/modular.py
index db7180f..b7848d4 100644
--- a/snf-pithos-backend/pithos/backends/modular.py
+++ b/snf-pithos-backend/pithos/backends/modular.py
@@ -50,7 +50,7 @@ from base import (DEFAULT_ACCOUNT_QUOTA, DEFAULT_CONTAINER_QUOTA,
                   DEFAULT_CONTAINER_VERSIONING, NotAllowedError, QuotaError,
                   BaseBackend, AccountExists, ContainerExists, AccountNotEmpty,
                   ContainerNotEmpty, ItemNotExists, VersionNotExists,
-                  InvalidHash)
+                  InvalidHash, IllegalOperationError)
 
 
 class DisabledAstakosClient(object):
@@ -1078,8 +1078,14 @@ class ModularBackend(BaseBackend):
         props = self._get_version(node, version)
         if props[self.HASH] is None:
             return 0, ()
-        hashmap = self.store.map_get(self._unhexlify_hash(props[self.HASH]))
-        return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
+        if props[self.HASH].startswith('archip:'):
+            hashmap = self.store.map_get_archipelago(props[self.HASH],
+                                                     props[self.SIZE])
+            return props[self.SIZE], [x for x in hashmap]
+        else:
+            hashmap = self.store.map_get(self._unhexlify_hash(
+                props[self.HASH]))
+            return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
 
     def _update_object_hash(self, user, account, container, name, size, type,
                             hash, checksum, domain, meta, replace_meta,
@@ -1159,6 +1165,10 @@ class ModularBackend(BaseBackend):
                               replace_meta=False, permissions=None):
         """Create/update an object's hashmap and return the new version."""
 
+        for h in hashmap:
+            if h.startswith('archip_'):
+                raise IllegalOperationError(
+                    'Cannot update Archipelago Volume hashmap.')
         meta = meta or {}
         if size == 0:  # No such thing as an empty hashmap.
             hashmap = [self.put_block('')]
@@ -1451,7 +1461,10 @@ class ModularBackend(BaseBackend):
         """Return a block's data."""
 
         logger.debug("get_block: %s", hash)
-        block = self.store.block_get(self._unhexlify_hash(hash))
+        if hash.startswith('archip_'):
+            block = self.store.block_get_archipelago(hash)
+        else:
+            block = self.store.block_get(self._unhexlify_hash(hash))
         if not block:
             raise ItemNotExists('Block does not exist')
         return block
@@ -1466,6 +1479,9 @@ class ModularBackend(BaseBackend):
         """Update a known block and return the hash."""
 
         logger.debug("update_block: %s %s %s", hash, len(data), offset)
+        if hash.startswith('archip_'):
+            raise IllegalOperationError(
+                'Cannot update an Archipelago Volume block.')
         if offset == 0 and len(data) == self.block_size:
             return self.put_block(data)
         h = self.store.block_update(self._unhexlify_hash(hash), offset, data)
