##// END OF EJS Templates
git-lfs: always validate uploaded files size....
marcink -
r197:e75ea5c6 default
parent child Browse files
Show More
@@ -1,166 +1,171 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2017 RodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import os
19 19 import shutil
20 20 import logging
21 21 from collections import OrderedDict
22 22
23 23 log = logging.getLogger(__name__)
24 24
25 25
26 26 class OidHandler(object):
27 27
28 28 def __init__(self, store, repo_name, auth, oid, obj_size, obj_data, obj_href,
29 29 obj_verify_href=None):
30 30 self.current_store = store
31 31 self.repo_name = repo_name
32 32 self.auth = auth
33 33 self.oid = oid
34 34 self.obj_size = obj_size
35 35 self.obj_data = obj_data
36 36 self.obj_href = obj_href
37 37 self.obj_verify_href = obj_verify_href
38 38
39 39 def get_store(self, mode=None):
40 40 return self.current_store
41 41
42 42 def get_auth(self):
43 43 """returns auth header for re-use in upload/download"""
44 44 return " ".join(self.auth)
45 45
46 46 def download(self):
47 47
48 48 store = self.get_store()
49 49 response = None
50 50 has_errors = None
51 51
52 52 if not store.has_oid():
53 53 # error reply back to client that something is wrong with dl
54 54 err_msg = 'object: {} does not exist in store'.format(store.oid)
55 55 has_errors = OrderedDict(
56 56 error=OrderedDict(
57 57 code=404,
58 58 message=err_msg
59 59 )
60 60 )
61 61
62 62 download_action = OrderedDict(
63 63 href=self.obj_href,
64 64 header=OrderedDict([("Authorization", self.get_auth())])
65 65 )
66 66 if not has_errors:
67 67 response = OrderedDict(download=download_action)
68 68 return response, has_errors
69 69
70 70 def upload(self, skip_existing=True):
71 71 """
72 72 Write upload action for git-lfs server
73 73 """
74 74
75 75 store = self.get_store()
76 76 response = None
77 77 has_errors = None
78 78
79 79 # verify if we have the OID before, if we do, reply with empty
80 80 if store.has_oid():
81 81 log.debug('LFS: store already has oid %s', store.oid)
82 if skip_existing:
82
83 # validate size
84 size_match = store.size_oid() == self.obj_size
85 if not size_match:
86 log.warning('LFS: size mismatch for oid:%s', self.oid)
87 elif skip_existing:
83 88 log.debug('LFS: skipping further action as oid is existing')
84 89 return response, has_errors
85 90
86 91 upload_action = OrderedDict(
87 92 href=self.obj_href,
88 93 header=OrderedDict([("Authorization", self.get_auth())])
89 94 )
90 95 if not has_errors:
91 96 response = OrderedDict(upload=upload_action)
92 97 # if specified in handler, return the verification endpoint
93 98 if self.obj_verify_href:
94 99 verify_action = OrderedDict(
95 100 href=self.obj_verify_href,
96 101 header=OrderedDict([("Authorization", self.get_auth())])
97 102 )
98 103 response['verify'] = verify_action
99 104 return response, has_errors
100 105
101 106 def exec_operation(self, operation, *args, **kwargs):
102 107 handler = getattr(self, operation)
103 108 log.debug('LFS: handling request using %s handler', handler)
104 109 return handler(*args, **kwargs)
105 110
106 111
107 112 class LFSOidStore(object):
108 113
109 114 def __init__(self, oid, repo, store_location=None):
110 115 self.oid = oid
111 116 self.repo = repo
112 117 self.store_path = store_location or self.get_default_store()
113 118 self.tmp_oid_path = os.path.join(self.store_path, oid + '.tmp')
114 119 self.oid_path = os.path.join(self.store_path, oid)
115 120 self.fd = None
116 121
117 122 def get_engine(self, mode):
118 123 """
119 124 engine = .get_engine(mode='wb')
120 125 with engine as f:
121 126 f.write('...')
122 127 """
123 128
124 129 class StoreEngine(object):
125 130 def __init__(self, mode, store_path, oid_path, tmp_oid_path):
126 131 self.mode = mode
127 132 self.store_path = store_path
128 133 self.oid_path = oid_path
129 134 self.tmp_oid_path = tmp_oid_path
130 135
131 136 def __enter__(self):
132 137 if not os.path.isdir(self.store_path):
133 138 os.makedirs(self.store_path)
134 139
135 140 # TODO(marcink): maybe write metadata here with size/oid ?
136 141 fd = open(self.tmp_oid_path, self.mode)
137 142 self.fd = fd
138 143 return fd
139 144
140 145 def __exit__(self, exc_type, exc_value, traceback):
141 146 # close tmp file, and rename to final destination
142 147 self.fd.close()
143 148 shutil.move(self.tmp_oid_path, self.oid_path)
144 149
145 150 return StoreEngine(
146 151 mode, self.store_path, self.oid_path, self.tmp_oid_path)
147 152
148 153 def get_default_store(self):
149 154 """
150 155 Default store, consistent with defaults of Mercurial large files store
151 156 which is /home/username/.cache/largefiles
152 157 """
153 158 user_home = os.path.expanduser("~")
154 159 return os.path.join(user_home, '.cache', 'lfs-store')
155 160
156 161 def has_oid(self):
157 162 return os.path.exists(os.path.join(self.store_path, self.oid))
158 163
159 164 def size_oid(self):
160 165 size = -1
161 166
162 167 if self.has_oid():
163 168 oid = os.path.join(self.store_path, self.oid)
164 169 size = os.stat(oid).st_size
165 170
166 171 return size
General Comments 0
You need to be logged in to leave comments. Login now