summaryrefslogtreecommitdiffstats
path: root/utils/uvol/files/common.uc
blob: 22fada0777fe283080aaeb9ae102d79acfa4edbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// SPDX-License-Identifier: GPL-2.0-or-later
// shared backend selection and locking for uvol entry points
//  (c) 2022 Daniel Golle <daniel@makrotopia.org>

let common_fs = require("fs");
let common_uci = require("uci");

include("/usr/lib/uvol/mount.uc");

let shell_quote = function(word) {
	return "'" + replace(`${word}`, /'/g, "'\\''") + "'";
};

// volumes are taken down whether or not anything mounted them, so umount is
// expected to fail and stays quiet. That needs a shell, hence the quoting.
let umount_dev = function(dev) {
	return system(sprintf("umount %s 2>/dev/null", shell_quote(sprintf("/dev/%s", dev))));
};

let lock_open = function(path) {
	common_fs.mkdir("/tmp/run", 0755);
	let lockfd = common_fs.open(path, "a");
	if (lockfd)
		lockfd.lock("x");
	return lockfd;
};

uvol_common = {
	// volume names reach tool arguments and lock file paths; anything outside
	// this set is refused before it gets there. A leading '.' is reserved for
	// internal volumes such as .meta, and a leading '-' would be taken for an
	// option by the backend tools.
	name_valid: function(name, allow_internal) {
		if (type(name) != "string")
			return false;
		if (allow_internal && name == ".meta")
			return true;
		return !!match(name, /^[A-Za-z0-9_][A-Za-z0-9._-]*$/);
	},

	shell_quote: shell_quote,

	ctx_init: function() {
		let ctx = {};
		ctx.cursor = common_uci ? common_uci.cursor() : null;
		ctx.fs = common_fs;
		ctx.register = uvol_mount.register;
		ctx.unregister = uvol_mount.unregister;
		ctx.shell_quote = shell_quote;
		ctx.umount_dev = umount_dev;
		return ctx;
	},

	backend_select: function(ctx) {
		let backend = null;
		let tried = [];
		for (let plugin in common_fs.glob("/usr/lib/uvol/backends/*.uc")) {
			let current_backend = {};
			include(plugin, { backend: current_backend });
			push(tried, current_backend.backend);
			if (type(backend) == "object" &&
			    type(backend.priority) == "int" &&
			    type(current_backend.priority) == "int" &&
			    backend.priority > current_backend.priority)
				continue;
			if (type(current_backend.init) == "function" &&
			    current_backend.init(ctx)) {
				backend = current_backend;
				break;
			}
		}
		return { backend: backend, tried: tried };
	},

	// released on process exit; the caller keeps the returned fd alive
	lock_device: function() {
		return lock_open("/tmp/run/uvol.lock");
	},

	lock_volume: function(vol_name) {
		return lock_open(sprintf("/tmp/run/uvol.lock.%s", vol_name));
	}
};