From 16d9e55f844926f8798e434cdcfc043ab3284cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Borgstr=C3=B6m?= Date: Sun, 11 Aug 2013 22:18:56 +0200 Subject: [PATCH] Support access of read only repositories --- CHANGES | 5 +++-- attic/_hashindex.c | 27 ++++++++++++++++++++------- attic/hashindex.pyx | 6 +++--- attic/repository.py | 4 ++-- attic/testsuite/archiver.py | 9 +++++++++ 5 files changed, 37 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 5ddeffc8f..d9c513448 100644 --- a/CHANGES +++ b/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 diff --git a/attic/_hashindex.c b/attic/_hashindex.c index b481c381f..52212fd7e 100644 --- a/attic/_hashindex.c +++ b/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; } diff --git a/attic/hashindex.pyx b/attic/hashindex.pyx index dbebc2acc..317b8af3c 100644 --- a/attic/hashindex.pyx +++ b/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(os.fsencode(path)) + def __cinit__(self, path, readonly=False): + self.index = hashindex_open(os.fsencode(path), readonly) if not self.index: raise Exception('Failed to open %s' % path) diff --git a/attic/repository.py b/attic/repository.py index e5a0bd3c0..0b10f412d 100644 --- a/attic/repository.py +++ b/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')) diff --git a/attic/testsuite/archiver.py b/attic/testsuite/archiver.py index 876cb1d6a..3dd898cab 100644 --- a/attic/testsuite/archiver.py +++ b/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)