Skip to content
API Documentation
BastionGuard™
SmbMount Class Reference
Inheritance diagram for SmbMount:

Static Public Member Functions

static bool is_available (out string error_message)
static string mount (SmbShare share, out string error_message)
static bool unmount (SmbShare share)
static bool unmount_path (string mount_point)
static bool is_mounted_at (string mount_point)
static Gee.ArrayList< SmbShare > list_active_mounts ()
static Gee.ArrayList< SmbShare > load_saved_shares ()
static void save_share (SmbShare share)
static void forget_share (SmbShare share)

Properties

static string mount_root [get]

Static Private Member Functions

static void write_shares (Gee.ArrayList< SmbShare > list)

Static Private Attributes

const string SHARES_CONF = "/etc/bastionguard-backup/smb-shares.json"

Detailed Description

Static helper: drives mount.cifs, tracks active mounts and persists the user's recently-used shares.

Definition at line 88 of file SmbMount.vala.

Member Function Documentation

◆ forget_share()

void SmbMount.forget_share ( SmbShare share)
inlinestatic

Definition at line 371 of file SmbMount.vala.

371 {
372 var list = load_saved_shares();
373 var keep = new Gee.ArrayList<SmbShare>();
374 foreach (var existing in list){
375 if (existing.server == share.server
376 && existing.share == share.share
377 && existing.subpath == share.subpath){
378 continue;
379 }
380 keep.add(existing);
381 }
382 write_shares(keep);
383 }
static void write_shares(Gee.ArrayList< SmbShare > list)
static Gee.ArrayList< SmbShare > load_saved_shares()
string subpath
Definition SmbMount.vala:31
string server
Definition SmbMount.vala:29
string share
Definition SmbMount.vala:30

References load_saved_shares(), and write_shares().

◆ is_available()

bool SmbMount.is_available ( out string error_message)
inlinestatic

Returns true if both mount.cifs and cifs-utils look usable on this system. Sets error_message with a hint otherwise.

Definition at line 109 of file SmbMount.vala.

109 {
110 error_message = "";
111 if (!cmd_exists("mount.cifs")){
112 error_message = _("Network shares (SMB/CIFS) are not available on this system.")
113 + "\n\n"
114 + _("Please install the 'cifs-utils' package and try again.");
115 return false;
116 }
117 return true;
118 }
#define _(s)

References _.

Referenced by mount(), and BackupDeviceBox.on_add_smb_share_clicked().

◆ is_mounted_at()

bool SmbMount.is_mounted_at ( string mount_point)
inlinestatic

Definition at line 261 of file SmbMount.vala.

261 {
262 try {
263 string contents;
264 FileUtils.get_contents("/proc/mounts", out contents);
265 foreach (string line in contents.split("\n")){
266 var parts = line.split(" ");
267 if (parts.length >= 3 && parts[1] == mount_point && parts[2] == "cifs"){
268 return true;
269 }
270 }
271 }
272 catch (Error e){
273 log_debug(e.message);
274 }
275 return false;
276 }
void log_debug(string message)

References Error, and TeeJee.Logging.log_debug().

Referenced by mount().

◆ list_active_mounts()

Gee.ArrayList< SmbShare > SmbMount.list_active_mounts ( )
inlinestatic

Returns every CIFS mount currently visible in /proc/mounts as an SmbShare with the mount_point field populated. Username and credentials are NOT recoverable from the kernel.

Definition at line 283 of file SmbMount.vala.

283 {
284 var list = new Gee.ArrayList<SmbShare>();
285 try {
286 string contents;
287 FileUtils.get_contents("/proc/mounts", out contents);
288 foreach (string line in contents.split("\n")){
289 if (line.strip().length == 0){ continue; }
290 var parts = line.split(" ");
291 if (parts.length < 3){ continue; }
292 if (parts[2] != "cifs" && parts[2] != "smb3"){ continue; }
293
294 string source = parts[0]; // e.g. //server/share
295 string mp = parts[1];
296
297 // /proc/mounts escapes spaces as \040 etc — decode.
298 mp = mp.replace("\\040", " ");
299
300 var share = new SmbShare();
301 share.mount_point = mp;
302 if (source.has_prefix("//")){
303 string rest = source.substring(2);
304 int slash = rest.index_of("/");
305 if (slash > 0){
306 share.server = rest.substring(0, slash);
307 share.share = rest.substring(slash + 1);
308 }
309 else {
310 share.server = rest;
311 }
312 }
313 list.add(share);
314 }
315 }
316 catch (Error e){
317 log_debug(e.message);
318 }
319 return list;
320 }

References Error, and TeeJee.Logging.log_debug().

Referenced by BackupDeviceBox.device_list_refresh().

◆ load_saved_shares()

Gee.ArrayList< SmbShare > SmbMount.load_saved_shares ( )
inlinestatic

Definition at line 324 of file SmbMount.vala.

324 {
325 var list = new Gee.ArrayList<SmbShare>();
326 var f = File.new_for_path(SHARES_CONF);
327 if (!f.query_exists()){
328 return list;
329 }
330 try {
331 var parser = new Json.Parser();
332 parser.load_from_file(SHARES_CONF);
333 var root = parser.get_root();
334 if (root == null){ return list; }
335 var arr = root.get_array();
336 if (arr == null){ return list; }
337 arr.foreach_element((a, idx, node) => {
338 var obj = node.get_object();
339 if (obj != null){
340 list.add(SmbShare.from_json(obj));
341 }
342 });
343 }
344 catch (Error e){
345 log_error("SmbMount.load_saved_shares(): " + e.message);
346 }
347 return list;
348 }
const string SHARES_CONF
void log_error(string message, bool highlight=false, bool is_warning=false)

References Error, SmbShare.from_json(), TeeJee.Logging.log_error(), and SHARES_CONF.

Referenced by forget_share(), and save_share().

◆ mount()

string SmbMount.mount ( SmbShare share,
out string error_message )
inlinestatic

Mount the SMB share described by share. On success the mount_point field of share is populated and the path is returned. On failure returns an empty string and error_message carries a translated explanation.

Definition at line 126 of file SmbMount.vala.

126 {
127 error_message = "";
128
129 if (share.server.length == 0 || share.share.length == 0){
130 error_message = _("Server and share name must both be provided.");
131 return "";
132 }
133
134 string avail_err;
135 if (!is_available(out avail_err)){
136 error_message = avail_err;
137 return "";
138 }
139
140 // Build a stable-but-readable mount point.
141 string slug = "%s_%s".printf(
142 share.server.replace("/", "_").replace(":", "_"),
143 share.share.replace("/", "_"));
144 string mp = path_combine(mount_root, slug);
145 dir_create(mp);
146
147 // Already mounted at our path? Reuse.
148 if (is_mounted_at(mp)){
149 share.mount_point = mp;
150 log_msg(_("SMB share already mounted at") + ": %s".printf(mp));
151 return mp;
152 }
153
154 // Build mount options. We deliberately keep them conservative
155 // so most NASes "just work" while still allowing rsync to set
156 // permissions and create hardlinks.
157 var opts = new Gee.ArrayList<string>();
158
159 // Credentials. Empty user => guest mount.
160 if (share.username.length > 0){
161 opts.add("username=" + share.username);
162 if (share.password.length > 0){
163 opts.add("password=" + share.password);
164 }
165 if (share.domain.length > 0){
166 opts.add("domain=" + share.domain);
167 }
168 }
169 else{
170 opts.add("guest");
171 }
172
173 // Run as root inside the mount (rsync needs this to
174 // preserve ownership of system files).
175 opts.add("uid=0");
176 opts.add("gid=0");
177 opts.add("file_mode=0644");
178 opts.add("dir_mode=0755");
179
180 // Prefer the most modern dialect that is widely supported.
181 opts.add("vers=3.0");
182
183 // rsync uses unix sockets/temp files inside the share, so allow
184 // "noperm" as a fallback if the server is overly strict. This
185 // is safe because the device side already gates access.
186 opts.add("noperm");
187
188 string opt_str = string.joinv(",", opts.to_array());
189
190 string unc = "//%s/%s".printf(share.server, share.share);
191 // mount.cifs takes the share without the optional subpath; the
192 // subpath is just appended to the resulting mount_point later.
193
194 string cmd = "mount -t cifs -o %s \"%s\" \"%s\"".printf(
195 Shell.quote(opt_str).replace("'", "'\\''"),
196 unc,
197 mp);
198
199 // Avoid leaking the password into the log
200 string safe_opt_str = opt_str;
201 if (share.password.length > 0){
202 safe_opt_str = safe_opt_str.replace(
203 "password=" + share.password, "password=***");
204 }
205 log_debug("SmbMount.mount(): mount -t cifs -o %s %s %s".printf(
206 safe_opt_str, unc, mp));
207
208 string std_out, std_err;
209 int ret_val = exec_sync(cmd, out std_out, out std_err);
210
211 if (ret_val != 0){
212 // Map a few common errors to friendlier messages.
213 string err = (std_err ?? "").strip();
214 if (err.length == 0){ err = (std_out ?? "").strip(); }
215
216 if (err.down().contains("permission denied")
217 || err.down().contains("logon failure")
218 || err.down().contains("nt_status_logon_failure")){
219 error_message = _("Authentication failed. Please check the username, password and domain.");
220 }
221 else if (err.down().contains("no route to host")
222 || err.down().contains("connection refused")
223 || err.down().contains("connection timed out")
224 || err.down().contains("host is down")){
225 error_message = _("Could not reach the server. Please check the address and that the share is online.");
226 }
227 else if (err.down().contains("no such file or directory")
228 || err.down().contains("bad share name")){
229 error_message = _("The share was not found on the server. Please check the share name.");
230 }
231 else{
232 error_message = _("Failed to mount the network share.") + "\n\n" + err;
233 }
234 log_error(error_message);
235 return "";
236 }
237
238 share.mount_point = mp;
239 log_msg(_("Mounted network share") + ": %s → %s".printf(unc, mp));
240 return mp;
241 }
static bool is_mounted_at(string mount_point)
static bool is_available(out string error_message)
static string mount_root
Definition SmbMount.vala:93
string password
Definition SmbMount.vala:39
string domain
Definition SmbMount.vala:33
string username
Definition SmbMount.vala:32
string path_combine(string path1, string path2)
bool dir_create(string dir_path, bool show_message=false)
void log_msg(string message, bool highlight=false)
int exec_sync(string cmd, out string? std_out=null, out string? std_err=null)

References _, TeeJee.FileSystem.dir_create(), SmbShare.domain, TeeJee.ProcessHelper.exec_sync(), is_available(), is_mounted_at(), TeeJee.Logging.log_debug(), TeeJee.Logging.log_error(), TeeJee.Logging.log_msg(), mount_root, SmbShare.password, TeeJee.FileSystem.path_combine(), SmbShare.server, SmbShare.share, and SmbShare.username.

Referenced by SmbShareDialog.on_connect_clicked().

◆ save_share()

void SmbMount.save_share ( SmbShare share)
inlinestatic

Definition at line 350 of file SmbMount.vala.

350 {
351 // merge: replace if the same server+share+subpath already exists,
352 // otherwise append.
353 var list = load_saved_shares();
354 bool replaced = false;
355 for (int i = 0; i < list.size; i++){
356 var existing = list[i];
357 if (existing.server == share.server
358 && existing.share == share.share
359 && existing.subpath == share.subpath){
360 list[i] = share;
361 replaced = true;
362 break;
363 }
364 }
365 if (!replaced){
366 list.add(share);
367 }
368 write_shares(list);
369 }

References load_saved_shares(), SmbShare.server, SmbShare.share, SmbShare.subpath, and write_shares().

Referenced by SmbShareDialog.on_connect_clicked().

◆ unmount()

bool SmbMount.unmount ( SmbShare share)
inlinestatic

Definition at line 243 of file SmbMount.vala.

243 {
244 if (share.mount_point.length == 0){
245 return true;
246 }
247 bool ok = Device.unmount(share.mount_point);
248 if (ok){
249 share.mount_point = "";
250 }
251 return ok;
252 }
string mount_point
Definition SmbMount.vala:36

References SmbShare.mount_point, and Device.unmount().

◆ unmount_path()

bool SmbMount.unmount_path ( string mount_point)
inlinestatic

Definition at line 254 of file SmbMount.vala.

254 {
255 if (mount_point.length == 0){
256 return true;
257 }
258 return Device.unmount(mount_point);
259 }

References Device.unmount().

◆ write_shares()

void SmbMount.write_shares ( Gee.ArrayList< SmbShare > list)
inlinestaticprivate

Definition at line 385 of file SmbMount.vala.

385 {
386 try {
387 dir_create(GLib.Path.get_dirname(SHARES_CONF));
388 var arr = new Json.Array();
389 foreach (var s in list){
390 arr.add_object_element(s.to_json());
391 }
392 var node = new Json.Node(Json.NodeType.ARRAY);
393 node.set_array(arr);
394
395 var generator = new Json.Generator();
396 generator.pretty = true;
397 generator.set_root(node);
398 generator.to_file(SHARES_CONF);
399
400 // Best effort: lock down the file because it contains
401 // usernames (no passwords, but still).
402 Posix.chmod(SHARES_CONF, 0600);
403 }
404 catch (Error e){
405 log_error("SmbMount.write_shares(): " + e.message);
406 }
407 }

References TeeJee.FileSystem.dir_create(), Error, TeeJee.Logging.log_error(), and SHARES_CONF.

Referenced by forget_share(), and save_share().

Member Data Documentation

◆ SHARES_CONF

const string SmbMount.SHARES_CONF = "/etc/bastionguard-backup/smb-shares.json"
staticprivate

Definition at line 103 of file SmbMount.vala.

Referenced by load_saved_shares(), and write_shares().

Property Documentation

◆ mount_root

string SmbMount.mount_root
staticget

Definition at line 93 of file SmbMount.vala.

93 {
94 owned get {
95 return (App != null && App.mount_point_app.length > 0)
96 ? App.mount_point_app + "/smb"
97 : "/run/bastionguard-backup/smb";
98 }
99 }
Main App
string mount_point_app
Definition Main.vala:82

Referenced by mount().


The documentation for this class was generated from the following file:
  • BastionGuard/thirdparty/bastionguard-backup/src/Utility/SmbMount.vala