浏览代码

Support access of read only repositories

Jonas Borgström 11 年之前
父节点
当前提交
16d9e55f84
共有 5 个文件被更改,包括 37 次插入14 次删除
  1. 3 2
      CHANGES
  2. 20 7
      attic/_hashindex.c
  3. 3 3
      attic/hashindex.pyx
  4. 2 2
      attic/repository.py
  5. 9 0
      attic/testsuite/archiver.py

+ 3 - 2
CHANGES

@@ -9,9 +9,10 @@ Version 0.8
 
 (feature release, released on X)
 
+- Support access of read only repositories.
 - New syntax to enable repository encryption:
-    attic init --encryption="none|passphrase|keyfile"
-- Detect and abort if repository is older than the cache
+    attic init --encryption="none|passphrase|keyfile".
+- Detect and abort if repository is older than the cache.
 
 
 Version 0.7

+ 20 - 7
attic/_hashindex.c

@@ -33,6 +33,7 @@ typedef struct {
     int bucket_size;
     int lower_limit;
     int upper_limit;
+    int readonly;
 } HashIndex;
 
 #define MAGIC "ATTICIDX"
@@ -56,7 +57,7 @@ typedef struct {
 #define EPRINTF(msg, ...) EPRINTF_PATH(index->path, msg, ##__VA_ARGS__)
 #define EPRINTF_PATH(path, msg, ...) fprintf(stderr, "hashindex: %s: " msg "\n", path, ##__VA_ARGS__)
 
-static HashIndex *hashindex_open(const char *path);
+static HashIndex *hashindex_open(const char *path, int readonly);
 static int hashindex_close(HashIndex *index);
 static int hashindex_clear(HashIndex *index);
 static int hashindex_flush(HashIndex *index);
@@ -138,15 +139,24 @@ hashindex_resize(HashIndex *index, int capacity)
 
 /* Public API */
 static HashIndex *
-hashindex_open(const char *path)
+hashindex_open(const char *path, int readonly)
 {
     void *addr;
-    int fd;
+    int fd, oflags, prot;
     off_t length;
     HashHeader *header;
     HashIndex *index;
 
-    if((fd = open(path, O_RDWR)) < 0) {
+    if(readonly) {
+        oflags = O_RDONLY;
+        prot = PROT_READ;
+    }
+    else {
+        oflags = O_RDWR;
+        prot = PROT_READ | PROT_WRITE;
+    }
+
+    if((fd = open(path, oflags)) < 0) {
         EPRINTF_PATH(path, "open failed");
         fprintf(stderr, "Failed to open %s\n", path);
         return NULL;
@@ -158,7 +168,7 @@ hashindex_open(const char *path)
         }
         return NULL;
     }
-    addr = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+    addr = mmap(0, length, prot, MAP_SHARED, fd, 0);
     if(close(fd) < 0) {
         EPRINTF_PATH(path, "close failed");
         return NULL;
@@ -180,6 +190,7 @@ hashindex_open(const char *path)
         EPRINTF_PATH(path, "malloc failed");
         return NULL;
     }
+    index->readonly = readonly;
     index->map_addr = addr;
     index->map_length = length;
     index->num_entries = header->num_entries;
@@ -228,7 +239,7 @@ hashindex_create(const char *path, int capacity, int key_size, int value_size)
         EPRINTF_PATH(path, "fclose failed");
         return NULL;
     }
-    return hashindex_open(path);
+    return hashindex_open(path, 0);
 error:
     EPRINTF_PATH(path, "fwrite failed");
     if(fclose(fd) < 0) {
@@ -251,6 +262,9 @@ hashindex_clear(HashIndex *index)
 static int
 hashindex_flush(HashIndex *index)
 {
+    if(index->readonly) {
+        return 1;
+    }
     *((int32_t *)(index->map_addr + 8)) = index->num_entries;
     *((int32_t *)(index->map_addr + 12)) = index->num_buckets;
     if(msync(index->map_addr, index->map_length, MS_SYNC) < 0) {
@@ -264,7 +278,6 @@ static int
 hashindex_close(HashIndex *index)
 {
     int rv = 1;
-
     if(hashindex_flush(index) < 0) {
         rv = 0;
     }

+ 3 - 3
attic/hashindex.pyx

@@ -6,7 +6,7 @@ cdef extern from "_hashindex.c":
     ctypedef struct HashIndex:
         pass
 
-    HashIndex *hashindex_open(char *path)
+    HashIndex *hashindex_open(char *path, int readonly)
     HashIndex *hashindex_create(char *path, int capacity, int key_size, int value_size)
     int hashindex_get_size(HashIndex *index)
     int hashindex_clear(HashIndex *index)
@@ -24,8 +24,8 @@ cdef class IndexBase:
     cdef HashIndex *index
     key_size = 32
 
-    def __cinit__(self, path):
-        self.index = hashindex_open(<bytes>os.fsencode(path))
+    def __cinit__(self, path, readonly=False):
+        self.index = hashindex_open(<bytes>os.fsencode(path), readonly)
         if not self.index:
             raise Exception('Failed to open %s' % path)
 

+ 2 - 2
attic/repository.py

@@ -71,7 +71,7 @@ class Repository(object):
         self.path = path
         if not os.path.isdir(path):
             raise self.DoesNotExist(path)
-        self.lock_fd = open(os.path.join(path, 'README'), 'r+')
+        self.lock_fd = open(os.path.join(path, 'config'), 'r')
         fcntl.flock(self.lock_fd, fcntl.LOCK_EX)
         self.config = RawConfigParser()
         self.config.read(os.path.join(self.path, 'config'))
@@ -108,7 +108,7 @@ class Repository(object):
             self.compact = set()
         else:
             if read_only:
-                self.index = NSIndex((os.path.join(self.path, 'index.%d') % head).encode('utf-8'))
+                self.index = NSIndex((os.path.join(self.path, 'index.%d') % head).encode('utf-8'), readonly=True)
             else:
                 shutil.copy(os.path.join(self.path, 'index.%d' % head),
                             os.path.join(self.path, 'index.tmp'))

+ 9 - 0
attic/testsuite/archiver.py

@@ -210,6 +210,15 @@ class ArchiverTestCase(AtticTestCase):
         fd.close()
         self.attic('verify', self.repository_location + '::test', exit_code=1)
 
+    def test_readonly_repository(self):
+        self.create_src_archive('test')
+        os.system('chmod -R ugo-w ' + self.repository_path)
+        try:
+            self.attic('verify', self.repository_location + '::test')
+        finally:
+            # Restore permissions so shutil.rmtree is able to delete it
+            os.system('chmod -R u+w ' + self.repository_path)
+
     def test_prune_repository(self):
         self.attic('init', self.repository_location)
         self.attic('create', self.repository_location + '::test1', src_dir)