... | ... |
@@ -67,6 +67,8 @@ class CombinedFSConfiguration(object): |
67 | 67 |
self.files = conf.get('files', {}) |
68 | 68 |
self.uid = int(conf.get('uid', DEFAULT_UID)) |
69 | 69 |
self.gid = int(conf.get('gid', DEFAULT_GID)) |
70 |
+ self.same_uid_as = conf.get('same-uid-as', None) |
|
71 |
+ self.same_gid_as = conf.get('same-gid-as', None) |
|
70 | 72 |
self.dir_mode = read_mode_setting(conf, 'dir_mode', DEFAULT_DIR_MODE) |
71 | 73 |
self.reg_mode = read_mode_setting(conf, 'reg_mode', DEFAULT_REG_MODE) |
72 | 74 |
self.key_mode = read_mode_setting(conf, 'key_mode', DEFAULT_KEY_MODE) |
... | ... |
@@ -171,6 +173,79 @@ class CombinedFS(Operations): |
171 | 173 |
def get_conf(self): |
172 | 174 |
return self.configuration |
173 | 175 |
|
176 |
+ # uid/gid-related helpers; in the end, the xid (uid/gid) precedence is: |
|
177 |
+ # - filespec/same-xid-as |
|
178 |
+ # - filespec/xid |
|
179 |
+ # - conf/same-xid-as |
|
180 |
+ # - conf/xid |
|
181 |
+ # - DEFAULT_XID |
|
182 |
+ def get_uid_gid(self, conf, filespec=None): |
|
183 |
+ """ |
|
184 |
+ Just-get-it-done wrapper around get_{uid,gid}_{global,for_filespec}. |
|
185 |
+ """ |
|
186 |
+ stats = {} |
|
187 |
+ if filespec is None: |
|
188 |
+ return self.get_uid_global(conf, stats), self.get_gid_global(conf, stats) |
|
189 |
+ return self.get_uid_for_filespec(conf, filespec, stats), self.get_gid_for_filespec(conf, filespec, stats) |
|
190 |
+ |
|
191 |
+ def get_uid_for_filespec(self, conf, filespec, stats): |
|
192 |
+ """ |
|
193 |
+ File-specific uid selection mechanism: attempt to use file-specific same-uid-as, falling back on |
|
194 |
+ file-specific uid, falling back on global uid selection mechanism. |
|
195 |
+ """ |
|
196 |
+ uid = self.get_stat_attr(filespec.get('same-uid-as', None), 'st_uid', filespec.get('uid', None), stats) |
|
197 |
+ if uid is None: |
|
198 |
+ uid = self.get_uid_global(conf, stats) |
|
199 |
+ return uid |
|
200 |
+ |
|
201 |
+ def get_gid_for_filespec(self, conf, filespec, stats): |
|
202 |
+ """ |
|
203 |
+ File-specific gid selection mechanism: attempt to use file-specific same-gid-as, falling back on |
|
204 |
+ file-specific gid, falling back on global gid selection mechanism. |
|
205 |
+ """ |
|
206 |
+ gid = self.get_stat_attr(filespec.get('same-gid-as', None), 'st_gid', filespec.get('gid', None), stats) |
|
207 |
+ if gid is None: |
|
208 |
+ gid = self.get_gid_global(conf, stats) |
|
209 |
+ return gid |
|
210 |
+ |
|
211 |
+ def get_uid_global(self, conf, stats): |
|
212 |
+ """ |
|
213 |
+ Global uid selection mechanism: attempt to use same-uid-as, falling back on uid. |
|
214 |
+ """ |
|
215 |
+ return self.get_stat_attr(conf.same_uid_as, 'st_uid', conf.uid, stats) |
|
216 |
+ |
|
217 |
+ def get_gid_global(self, conf, stats): |
|
218 |
+ """ |
|
219 |
+ Global gid selection mechanism: attempt to use same-gid-as, falling back on gid. |
|
220 |
+ """ |
|
221 |
+ return self.get_stat_attr(conf.same_gid_as, 'st_gid', conf.gid, stats) |
|
222 |
+ |
|
223 |
+ def get_stat_attr(self, path, attr, default, stats): |
|
224 |
+ """ |
|
225 |
+ Stat path and return the request attribute, or the default value if something goes wrong. |
|
226 |
+ """ |
|
227 |
+ if path is None: |
|
228 |
+ return default |
|
229 |
+ try: |
|
230 |
+ return getattr(self.get_stat(path, stats), attr) |
|
231 |
+ except: |
|
232 |
+ return default |
|
233 |
+ |
|
234 |
+ def get_stat(self, path, stats): |
|
235 |
+ """ |
|
236 |
+ Simple wrapper around os.stat() that uses a dict to implement some basic caching (for the sake of |
|
237 |
+ uid/gid consistency, not actually for performance). Return either None or a stat structure. |
|
238 |
+ Should throw no exceptions as long as stats is provided. |
|
239 |
+ """ |
|
240 |
+ stat = stats.get(path) |
|
241 |
+ if stat is None: |
|
242 |
+ try: |
|
243 |
+ stats[path] = stat = os.stat(path) |
|
244 |
+ except: |
|
245 |
+ pass |
|
246 |
+ return stat |
|
247 |
+ # End of uid/gid-related helpers |
|
248 |
+ |
|
174 | 249 |
def iterate_paths(self, func, paths): |
175 | 250 |
for filepath in paths: |
176 | 251 |
try: |
... | ... |
@@ -215,10 +290,11 @@ class CombinedFS(Operations): |
215 | 290 |
return None |
216 | 291 |
|
217 | 292 |
def handle_reload_getattr(self, conf, fh): |
293 |
+ uid, gid = self.get_uid_gid(conf) |
|
218 | 294 |
return { |
219 | 295 |
'st_nlink': 1, |
220 |
- 'st_uid': conf.uid, |
|
221 |
- 'st_gid': conf.gid, |
|
296 |
+ 'st_uid': uid, |
|
297 |
+ 'st_gid': gid, |
|
222 | 298 |
'st_size': RELOAD_FILESIZE, |
223 | 299 |
'st_mode': stat.S_IFREG | conf.key_mode, |
224 | 300 |
} |
... | ... |
@@ -261,14 +337,16 @@ class CombinedFS(Operations): |
261 | 337 |
dir_attrs[prop] = 0 |
262 | 338 |
else: |
263 | 339 |
raise |
264 |
- dir_attrs['st_uid'] = conf.uid |
|
265 |
- dir_attrs['st_gid'] = conf.gid |
|
340 |
+ uid, gid = self.get_uid_gid(conf) |
|
341 |
+ dir_attrs['st_uid'] = uid |
|
342 |
+ dir_attrs['st_gid'] = gid |
|
266 | 343 |
dir_attrs['st_mode'] = stat.S_IFDIR | conf.dir_mode |
267 | 344 |
return dir_attrs |
345 |
+ uid, gid = self.get_uid_gid(conf, file_spec) |
|
268 | 346 |
attrs = { |
269 | 347 |
'st_nlink': 1, |
270 |
- 'st_uid': file_spec.get('uid', conf.uid), |
|
271 |
- 'st_gid': file_spec.get('gid', conf.gid), |
|
348 |
+ 'st_uid': uid, |
|
349 |
+ 'st_gid': gid, |
|
272 | 350 |
'st_size': 0, |
273 | 351 |
} |
274 | 352 |
def_mode = conf.reg_mode |
... | ... |
@@ -39,6 +39,15 @@ separator: '/' |
39 | 39 |
# These settings do NOT understand user and group names. |
40 | 40 |
uid: 1305 |
41 | 41 |
gid: 25519 |
42 |
+# They default to 0. |
|
43 |
+ |
|
44 |
+# However, hardcoding user/group ids can be a timebomb. |
|
45 |
+# These settings provide the ability to reflect the same uid or gid as a given |
|
46 |
+# file: |
|
47 |
+same-uid-as: '/path/to/target/user/home/directory' |
|
48 |
+same-gid-as: '/path/to/file/with/adequate/ownership' |
|
49 |
+# If the mentioned paths cannot be stat()ed, combinedfs defaults to the values |
|
50 |
+# set via uid and gid respectively. |
|
42 | 51 |
|
43 | 52 |
# Unix modes: base-8 notation in a string. |
44 | 53 |
# Directories: |
... | ... |
@@ -64,7 +73,7 @@ files: |
64 | 73 |
# content: ['chain.pem'] |
65 | 74 |
fullchain.pem: |
66 | 75 |
content: ['fullchain.pem'] |
67 |
- # Per-file permissions: |
|
76 |
+ # Per-file permissions; same-uid-as and same-gid-as are available too: |
|
68 | 77 |
uid: 2223 |
69 | 78 |
gid: 2226 |
70 | 79 |
mode: '0440' |