summaryrefslogblamecommitdiffstats
path: root/libmount/src/context_mount.c
blob: f2b42eb0b4b6248dcf961093f613dca48dc75f1e (plain) (tree)
1
2
3
4
5
6
7
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580






                                                        





                                                         







                            
                          
                   
                     
 
  
                                                                               
                                                                                  




























                                                                                
                                 






                                                              



                                                                                   









                                                              
                                                                                      



























                                                                            





                                                                                    
                                                                                      



























                                                                                 























                                                                      
  

                                                                 
                                                 
 
                   
                   

                             
                             

                                   







                                                                
      

                       







                                                              
      
                    

                                                        
                                                              

                         
                                                           
 

                     

                                                                            

                                                                              
                          
           


                                                                       











                                                             
                                                               
                                                                     



                                                                       
                                                                
                                                                           



                                                                           







                                                          




                                               






                                                 
 
                             

                      














                                                                                   
                                                               
                           


                                                  


                                                                               
         
      
                 
                                                 

                           


                                                                              
                                                                                   
                                                                 
                                                                                   
                      


                                                                       
                                     

                                                            
                                                                                




                                                                     
                                                                             


                                                                           
                 
                                                                            






                                                                           



                                  
                                                                           
                                                           
 



                                               
     

                                             
                                                           

                                                                             


                                       



                  
                                                                                  
                                 
   
                                                                            
 
                                            

                                





                        
                                                                           
 


                                                 
 

                                                    

                                                                           

                                                                        
                  

                                                                                     
                                                                                 
                                                                              











                                                                                
                                           
                                                                           




                                                            
                                                        




                                                                              
                                                                  






                                                                           
         




                       


                  
  
                                            
  
                                                                        

                                                                        
   
                                                           
 
                                  
 
                    

                                                        


                         
                                                                    
 
                                                   






                                                      



                             
                                                  
                 
                                                                                                  


                                      
                  




                                                                                  
                   

                                                         
 


                                                                         
                                                                                                  


                                                                     









                                                                               











                                                                                                 
                                                                                   

                                              






                                                                                    
 
                                   
                                           


                                                                             
                                                                                               






                                      

                                      
  
                                                                               




                                                                              
                                                                          


                         

                                             





























                                                                  




                  
                                                  






                            

                                                        
                                                                              


                                             
                               
















                                                  

                                                               

                                                               
                                               
                                                               
                                             
                                                               
                                               
                                                               
                                                




                                                               


                                                      



                                                                
                                         
                                                                      
                                                                     









                                                                          
                                                                    
                                                                          
                                                 



                      
                                                      
                                                            


                      
                


                  

















                                                                              
                                                                               







                                                                                
                                                 








                                                                        

                                                                          
                                                                           



                                                                
   
                                                                     
 
                   




                                        
                                                        


                                                                        
                       

                                  
 



                                            











                                                                     
                    
                               

                                                             
                                                                      
                                                                                         
                             
         

                                                       

                                    

                                   
 
                                               
                                                          
                                                             
                                                                  


                                                                   



                                       
                                        










                                                                                  
                                                                      
                                                     
                                                                                  
                                                                               
                                                    
                 
                                                        
                                        









                                                                       
         

                                      
                                                                      


                                                             
 


                  

                                                                                 
 
                         
                     

                    
                                                        
 
                                                                             
 















                                                                              
                                                                                                  
                                         
                                               
                                          
                         


                                                                     
 




                                                    
 


                  
 









                                                                               





                                                                        

         

                                                                                  
          
                                                                  




                                                                     


                                         



                                                

                                                                   
                              


                                          


   

                             
  
                                                                     
  
                                                     
   
                                                         
 
                         
 
                                                            
                               

                                                                       


                                         


                                             
                                    
 
                                                       
 
                                          
                
                                                   






                                                      
                                                     
                


                                                                    
                 
                                                                      
                          
         







                                      
                                                                           
  
                                                                          
                                                                       
  

                                                                             

                                
                                                                       
                                                         





                                                                
   
                                                    

                         
                






                                                        
                                               
 
                                                      
 
                                             
                                                                         

                                          

                                      
                                                                                       
                                                           


                                                  

                                                                    
                                
                                       
                                        



















                                                                               

                                                              




                                                                     
      
 
                   


   
























                                                                              

                      
                                                                                  
  
                      






                                          



                                                                                  
  
                                                                       
                                                         

                                                       
  


                                                                
   
                                                 







                                             
      




                                                     

                                                  

























                                                                                                    


                  




                                      
                                                           
                                                                             




                                                                              

                                                                         





                                                                               
                                                            









































                                                                              
                                                                 

                          
                                      

                                    
                                                                          



                                                                        
                                                                   


                                                                
                                                                



                                                                
                                                                  

















                                                                                          











                                                                
                                          






                                            
                                                                                 


                          


                 








































































































































































































































































































































                                                                                                                            
/*
 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
 *
 * This file may be redistributed under the terms of the
 * GNU Lesser General Public License.
 */

/**
 * SECTION: context-mount
 * @title: Mount context
 * @short_description: high-level API to mount operation.
 */

#ifdef HAVE_LIBSELINUX
#include <selinux/selinux.h>
#include <selinux/context.h>
#endif

#include <sys/wait.h>
#include <sys/mount.h>

#include "linux_version.h"
#include "mountP.h"
#include "strutils.h"

/*
 * Kernel supports only one MS_PROPAGATION flag change by one mount(2) syscall,
 * to bypass this restriction we call mount(2) per flag. It's really not a perfect
 * solution, but it's the same like to execute multiple mount(8) commands.
 *
 * We use cxt->addmounts (additional mounts) list to keep order of the requested
 * flags changes.
 */
struct libmnt_addmount *mnt_new_addmount(void)
{
	struct libmnt_addmount *ad = calloc(1, sizeof(*ad));
	if (!ad)
		return NULL;

	INIT_LIST_HEAD(&ad->mounts);
	return ad;
}

void mnt_free_addmount(struct libmnt_addmount *ad)
{
	if (!ad)
		return;
	list_del(&ad->mounts);
	free(ad);
}

static int mnt_context_append_additional_mount(struct libmnt_context *cxt,
					       struct libmnt_addmount *ad)
{
	assert(cxt);
	assert(ad);

	DBG(CXT, ul_debugobj(cxt,
			"mount: add additional flag: 0x%08lx",
			ad->mountflags));

	list_add_tail(&ad->mounts, &cxt->addmounts);
	return 0;
}

/*
 * add additional mount(2) syscall requests when necessary to set propagation flags
 * after regular mount(2).
 */
static int init_propagation(struct libmnt_context *cxt)
{
	char *name;
	char *opts = (char *) mnt_fs_get_vfs_options(cxt->fs);
	size_t namesz;
	struct libmnt_optmap const *maps[1];

	if (!opts)
		return 0;

	DBG(CXT, ul_debugobj(cxt, "mount: initialize additional propagation mounts"));

	maps[0] = mnt_get_builtin_optmap(MNT_LINUX_MAP);

	while (!mnt_optstr_next_option(&opts, &name, &namesz, NULL, NULL)) {
		const struct libmnt_optmap *ent;
		struct libmnt_addmount *ad;
		int rc;

		if (!mnt_optmap_get_entry(maps, 1, name, namesz, &ent)
		    || !ent
		    || !(ent->id & MS_PROPAGATION))
			continue;

		ad = mnt_new_addmount();
		if (!ad)
			return -ENOMEM;

		ad->mountflags = ent->id;
		rc = mnt_context_append_additional_mount(cxt, ad);
		if (rc)
			return rc;

		cxt->mountflags &= ~ent->id;
	}

	return 0;
}

/*
 * add additional mount(2) syscall request to implement "ro,bind", the first regular
 * mount(2) is the "bind" operation, the second is "remount,ro,bind" call.
 *
 * Note that we don't remove "ro" from the first syscall (kernel silently
 * ignores this flags for bind operation) -- maybe one day kernel will support
 * read-only binds in one step and then all will be done by the first mount(2) and the
 * second remount will be noop...
 */
static int init_robind(struct libmnt_context *cxt)
{
	struct libmnt_addmount *ad;
	int rc;

	assert(cxt);
	assert(cxt->mountflags & MS_BIND);
	assert(cxt->mountflags & MS_RDONLY);
	assert(!(cxt->mountflags & MS_REMOUNT));

	DBG(CXT, ul_debugobj(cxt, "mount: initialize additional ro,bind mount"));

	ad = mnt_new_addmount();
	if (!ad)
		return -ENOMEM;

	ad->mountflags = MS_REMOUNT | MS_BIND | MS_RDONLY;
	if (cxt->mountflags & MS_REC)
		ad->mountflags |= MS_REC;
	rc = mnt_context_append_additional_mount(cxt, ad);
	if (rc)
		return rc;

	return 0;
}

#if defined(HAVE_LIBSELINUX) || defined(HAVE_SMACK)
struct libmnt_optname {
	const char *name;
	size_t namesz;
};

#define DEF_OPTNAME(n)		{ .name = n, .namesz = sizeof(n) - 1 }
#define DEF_OPTNAME_LAST	{ .name = NULL }

static int is_option(const char *name, size_t namesz,
		     const struct libmnt_optname *names)
{
	const struct libmnt_optname *p;

	for (p = names; p && p->name; p++) {
		if (p->namesz == namesz
		    && strncmp(name, p->name, namesz) == 0)
			return 1;
	}

	return 0;
}
#endif /* HAVE_LIBSELINUX || HAVE_SMACK */

/*
 * this has to be called after mnt_context_evaluate_permissions()
 */
static int fix_optstr(struct libmnt_context *cxt)
{
	int rc = 0;
	char *next;
	char *name, *val;
	size_t namesz, valsz;
	struct libmnt_fs *fs;
#ifdef HAVE_LIBSELINUX
	int se_fix = 0, se_rem = 0;
	static const struct libmnt_optname selinux_options[] = {
		DEF_OPTNAME("context"),
		DEF_OPTNAME("fscontext"),
		DEF_OPTNAME("defcontext"),
		DEF_OPTNAME("rootcontext"),
		DEF_OPTNAME("seclabel"),
		DEF_OPTNAME_LAST
	};
#endif
#ifdef HAVE_SMACK
	int sm_rem = 0;
	static const struct libmnt_optname smack_options[] = {
		DEF_OPTNAME("smackfsdef"),
		DEF_OPTNAME("smackfsfloor"),
		DEF_OPTNAME("smackfshat"),
		DEF_OPTNAME("smackfsroot"),
		DEF_OPTNAME("smackfstransmute"),
		DEF_OPTNAME_LAST
	};
#endif
	assert(cxt);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	if (!cxt->fs || (cxt->flags & MNT_FL_MOUNTOPTS_FIXED))
		return 0;

	DBG(CXT, ul_debugobj(cxt, "mount: fixing optstr"));

	fs = cxt->fs;

	/*
	 * The "user" options is our business (so we can modify the option),
	 * the exception is command line for /sbin/mount.<type> helpers. Let's
	 * save the original user=<name> to call the helpers with an unchanged
	 * "user" setting.
	 */
	if (cxt->user_mountflags & MNT_MS_USER) {
		if (!mnt_optstr_get_option(fs->user_optstr,
					"user", &val, &valsz) && val) {
			cxt->orig_user = strndup(val, valsz);
			if (!cxt->orig_user) {
				rc = -ENOMEM;
				goto done;
			}
		}
		cxt->flags |= MNT_FL_SAVED_USER;
	}

	/*
	 * Sync mount options with mount flags
	 */
	DBG(CXT, ul_debugobj(cxt, "mount: fixing vfs optstr"));
	rc = mnt_optstr_apply_flags(&fs->vfs_optstr, cxt->mountflags,
				mnt_get_builtin_optmap(MNT_LINUX_MAP));
	if (rc)
		goto done;

	DBG(CXT, ul_debugobj(cxt, "mount: fixing user optstr"));
	rc = mnt_optstr_apply_flags(&fs->user_optstr, cxt->user_mountflags,
				mnt_get_builtin_optmap(MNT_USERSPACE_MAP));
	if (rc)
		goto done;

	if (fs->vfs_optstr && *fs->vfs_optstr == '\0') {
		free(fs->vfs_optstr);
		fs->vfs_optstr = NULL;
	}
	if (fs->user_optstr && *fs->user_optstr == '\0') {
		free(fs->user_optstr);
		fs->user_optstr = NULL;
	}
	if (cxt->mountflags & MS_PROPAGATION) {
		rc = init_propagation(cxt);
		if (rc)
			return rc;
	}
	if ((cxt->mountflags & MS_BIND)
	    && (cxt->mountflags & MS_RDONLY)
	    && !(cxt->mountflags & MS_REMOUNT)) {
		rc = init_robind(cxt);
		if (rc)
			return rc;
	}

	next = fs->fs_optstr;

#ifdef HAVE_LIBSELINUX
	if (!is_selinux_enabled())
		/* Always remove SELinux garbage if SELinux disabled */
		se_rem = 1;
	else if (cxt->mountflags & MS_REMOUNT)
		/*
		 * Linux kernel < 2.6.39 does not allow to remount with any
		 * selinux specific mount options.
		 *
		 * Kernel 2.6.39 commits:  ff36fe2c845cab2102e4826c1ffa0a6ebf487c65
		 *                         026eb167ae77244458fa4b4b9fc171209c079ba7
		 * fix this odd behavior, so we don't have to care about it in
		 * userspace.
		 */
		se_rem = get_linux_version() < KERNEL_VERSION(2, 6, 39);
	else
		/* For normal mount, contexts are translated */
		se_fix = 1;

	if (!se_rem) {
		/* de-duplicate SELinux options */
		const struct libmnt_optname *p;
		for (p = selinux_options; p && p->name; p++)
			mnt_optstr_deduplicate_option(&fs->fs_optstr, p->name);
	}
#endif
#ifdef HAVE_SMACK
	if (access("/sys/fs/smackfs", F_OK) != 0)
		sm_rem = 1;
#endif
	while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {

		if (namesz == 3 && !strncmp(name, "uid", 3))
			rc = mnt_optstr_fix_uid(&fs->fs_optstr, val, valsz, &next);
		else if (namesz == 3 && !strncmp(name, "gid", 3))
			rc = mnt_optstr_fix_gid(&fs->fs_optstr, val, valsz, &next);
#ifdef HAVE_LIBSELINUX
		else if ((se_rem || se_fix)
			 && is_option(name, namesz, selinux_options)) {

			if (se_rem) {
				/* remove context= option */
				next = name;
				rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
						name,
						val ? val + valsz :
						      name + namesz);
			} else if (se_fix && val && valsz)
				/* translate selinux contexts */
				rc = mnt_optstr_fix_secontext(&fs->fs_optstr,
							val, valsz, &next);
		}
#endif
#ifdef HAVE_SMACK
		else if (sm_rem && is_option(name, namesz, smack_options)) {

			next = name;
			rc = mnt_optstr_remove_option_at(&fs->fs_optstr,
					name,
					val ? val + valsz : name + namesz);
		}
#endif
		if (rc)
			goto done;
	}

	if (!rc && cxt->restricted && (cxt->user_mountflags & MNT_MS_USER))
		rc = mnt_optstr_fix_user(&fs->user_optstr);

	/* refresh merged optstr */
	free(fs->optstr);
	fs->optstr = NULL;
	fs->optstr = mnt_fs_strdup_options(fs);
done:
	cxt->flags |= MNT_FL_MOUNTOPTS_FIXED;

	DBG(CXT, ul_debugobj(cxt, "fixed options [rc=%d]: "
		"vfs: '%s' fs: '%s' user: '%s', optstr: '%s'", rc,
		fs->vfs_optstr, fs->fs_optstr, fs->user_optstr, fs->optstr));

	if (rc)
		rc = -MNT_ERR_MOUNTOPT;
	return rc;
}

/*
 * Converts the already evaluated and fixed options to the form that is compatible
 * with /sbin/mount.type helpers.
 */
static int generate_helper_optstr(struct libmnt_context *cxt, char **optstr)
{
	struct libmnt_optmap const *maps[2];
	char *next, *name, *val;
	size_t namesz, valsz;
	int rc = 0;

	assert(cxt);
	assert(cxt->fs);
	assert(optstr);

	DBG(CXT, ul_debugobj(cxt, "mount: generate helper mount options"));

	*optstr = mnt_fs_strdup_options(cxt->fs);
	if (!*optstr)
		return -ENOMEM;

	if ((cxt->user_mountflags & MNT_MS_USER) ||
	    (cxt->user_mountflags & MNT_MS_USERS)) {
		/*
		 * This is unnecessary for real user-mounts as mount.<type>
		 * helpers always have to follow fstab rather than mount
		 * options on the command line.
		 *
		 * However, if you call mount.<type> as root, then the helper follows
		 * the command line. If there is (for example) "user,exec" in fstab,
		 * then we have to manually append the "exec" back to the options
		 * string, because there is nothing like MS_EXEC (we only have
		 * MS_NOEXEC in mount flags and we don't care about the original
		 * mount string in libmount for VFS options).
		 */
		if (!(cxt->mountflags & MS_NOEXEC))
			mnt_optstr_append_option(optstr, "exec", NULL);
		if (!(cxt->mountflags & MS_NOSUID))
			mnt_optstr_append_option(optstr, "suid", NULL);
		if (!(cxt->mountflags & MS_NODEV))
			mnt_optstr_append_option(optstr, "dev", NULL);
	}


	if (cxt->flags & MNT_FL_SAVED_USER)
		rc = mnt_optstr_set_option(optstr, "user", cxt->orig_user);
	if (rc)
		goto err;

	/* remove userspace options with MNT_NOHLPS flag */
	maps[0] = mnt_get_builtin_optmap(MNT_USERSPACE_MAP);
	maps[1] = mnt_get_builtin_optmap(MNT_LINUX_MAP);
	next = *optstr;

	while (!mnt_optstr_next_option(&next, &name, &namesz, &val, &valsz)) {
		const struct libmnt_optmap *ent;

		mnt_optmap_get_entry(maps, 2, name, namesz, &ent);
		if (ent && ent->id && (ent->mask & MNT_NOHLPS)) {
			next = name;
			rc = mnt_optstr_remove_option_at(optstr, name,
					val ? val + valsz : name + namesz);
			if (rc)
				goto err;
		}
	}

	return rc;
err:
	free(*optstr);
	*optstr = NULL;
	return rc;
}

/*
 * this has to be called before fix_optstr()
 *
 * Note that user=<name> may be used by some filesystems as a filesystem
 * specific option (e.g. cifs). Yes, developers of such filesystems have
 * allocated pretty hot place in hell...
 */
static int evaluate_permissions(struct libmnt_context *cxt)
{
	unsigned long u_flags = 0;

	assert(cxt);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	if (!cxt->fs)
		return 0;

	DBG(CXT, ul_debugobj(cxt, "mount: evaluating permissions"));

	mnt_context_get_user_mflags(cxt, &u_flags);

	if (!mnt_context_is_restricted(cxt)) {
		/*
		 * superuser mount
		 */
		cxt->user_mountflags &= ~MNT_MS_OWNER;
		cxt->user_mountflags &= ~MNT_MS_GROUP;
	} else {
		/*
		 * user mount
		 */
		if (!mnt_context_tab_applied(cxt))
		{
			DBG(CXT, ul_debugobj(cxt, "perms: fstab not applied, ignore user mount"));
			return -EPERM;
		}

		/*
		 * MS_OWNERSECURE and MS_SECURE mount options are already
		 * applied by mnt_optstr_get_flags() in mnt_context_merge_mflags()
		 * if "user" (but no user=<name> !) options is set.
		 *
		 * Let's ignore all user=<name> (if <name> is set) requests.
		 */
		if (cxt->user_mountflags & MNT_MS_USER) {
			size_t valsz = 0;

			if (!mnt_optstr_get_option(cxt->fs->user_optstr,
					"user", NULL, &valsz) && valsz) {

				DBG(CXT, ul_debugobj(cxt, "perms: user=<name> detected, ignore"));
				cxt->user_mountflags &= ~MNT_MS_USER;
			}
		}

		/*
		 * MS_OWNER: Allow owners to mount when fstab contains the
		 * owner option.  Note that this should never be used in a high
		 * security environment, but may be useful to give people at
		 * the console the possibility of mounting a floppy.  MS_GROUP:
		 * Allow members of device group to mount. (Martin Dickopp)
		 */
		if (u_flags & (MNT_MS_OWNER | MNT_MS_GROUP)) {
			struct stat sb;
			struct libmnt_cache *cache = NULL;
			char *xsrc = NULL;
			const char *srcpath = mnt_fs_get_srcpath(cxt->fs);

			if (!srcpath) {					/* Ah... source is TAG */
				cache = mnt_context_get_cache(cxt);
				xsrc = mnt_resolve_spec(
						mnt_context_get_source(cxt),
						cache);
				srcpath = xsrc;
			}
			if (!srcpath) {
				DBG(CXT, ul_debugobj(cxt, "perms: src undefined"));
				return -EPERM;
			}

			if (strncmp(srcpath, "/dev/", 5) == 0 &&
			    stat(srcpath, &sb) == 0 &&
			    (((u_flags & MNT_MS_OWNER) && getuid() == sb.st_uid) ||
			     ((u_flags & MNT_MS_GROUP) && mnt_in_group(sb.st_gid))))

				cxt->user_mountflags |= MNT_MS_USER;

			if (!cache)
				free(xsrc);
		}

		if (!(cxt->user_mountflags & (MNT_MS_USER | MNT_MS_USERS))) {
			DBG(CXT, ul_debugobj(cxt, "permissions evaluation ends with -EPERMS"));
			return -EPERM;
		}
	}

	return 0;
}

/*
 * mnt_context_helper_setopt() backend
 *
 * This function applies the mount.type command line option (for example parsed
 * by getopt() or getopt_long()) to @cxt. All unknown options are ignored and
 * then 1 is returned.
 *
 * Returns: negative number on error, 1 if @c is unknown option, 0 on success.
 */
int mnt_context_mount_setopt(struct libmnt_context *cxt, int c, char *arg)
{
	int rc = -EINVAL;

	assert(cxt);
	assert(cxt->action == MNT_ACT_MOUNT);

	switch(c) {
	case 'f':
		rc = mnt_context_enable_fake(cxt, TRUE);
		break;
	case 'n':
		rc = mnt_context_disable_mtab(cxt, TRUE);
		break;
	case 'r':
		rc = mnt_context_append_options(cxt, "ro");
		break;
	case 'v':
		rc = mnt_context_enable_verbose(cxt, TRUE);
		break;
	case 'w':
		rc = mnt_context_append_options(cxt, "rw");
		break;
	case 'o':
		if (arg)
			rc = mnt_context_append_options(cxt, arg);
		break;
	case 's':
		rc = mnt_context_enable_sloppy(cxt, TRUE);
		break;
	case 't':
		if (arg)
			rc = mnt_context_set_fstype(cxt, arg);
		break;
	default:
		return 1;
	}

	return rc;
}

static int exec_helper(struct libmnt_context *cxt)
{
	char *o = NULL;
	int rc;

	assert(cxt);
	assert(cxt->fs);
	assert(cxt->helper);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	DBG(CXT, ul_debugobj(cxt, "mount: executing helper %s", cxt->helper));

	rc = generate_helper_optstr(cxt, &o);
	if (rc)
		return -EINVAL;

	DBG_FLUSH;

	switch (fork()) {
	case 0:
	{
		const char *args[12], *type;
		int i = 0;

		if (setgid(getgid()) < 0)
			exit(EXIT_FAILURE);

		if (setuid(getuid()) < 0)
			exit(EXIT_FAILURE);

		type = mnt_fs_get_fstype(cxt->fs);

		args[i++] = cxt->helper;		/* 1 */
		args[i++] = mnt_fs_get_srcpath(cxt->fs);/* 2 */
		args[i++] = mnt_fs_get_target(cxt->fs);	/* 3 */

		if (mnt_context_is_sloppy(cxt))
			args[i++] = "-s";		/* 4 */
		if (mnt_context_is_fake(cxt))
			args[i++] = "-f";		/* 5 */
		if (mnt_context_is_nomtab(cxt))
			args[i++] = "-n";		/* 6 */
		if (mnt_context_is_verbose(cxt))
			args[i++] = "-v";		/* 7 */
		if (o) {
			args[i++] = "-o";		/* 8 */
			args[i++] = o;			/* 9 */
		}
		if (type
		    && strchr(type, '.')
		    && !endswith(cxt->helper, type)) {
			args[i++] = "-t";		/* 10 */
			args[i++] = type;		/* 11 */
		}
		args[i] = NULL;				/* 12 */
		for (i = 0; args[i]; i++)
			DBG(CXT, ul_debugobj(cxt, "argv[%d] = \"%s\"",
							i, args[i]));
		DBG_FLUSH;
		execv(cxt->helper, (char * const *) args);
		exit(EXIT_FAILURE);
	}
	default:
	{
		int st;
		wait(&st);
		cxt->helper_status = WIFEXITED(st) ? WEXITSTATUS(st) : -1;

		DBG(CXT, ul_debugobj(cxt, "%s executed [status=%d]",
					cxt->helper, cxt->helper_status));
		cxt->helper_exec_status = rc = 0;
		break;
	}

	case -1:
		cxt->helper_exec_status = rc = -errno;
		DBG(CXT, ul_debugobj(cxt, "fork() failed"));
		break;
	}

	free(o);
	return rc;
}

static int do_mount_additional(struct libmnt_context *cxt,
			       const char *target,
			       unsigned long flags,
			       int *syserr)
{
	struct list_head *p;

	assert(cxt);
	assert(target);

	if (syserr)
		*syserr = 0;

	list_for_each(p, &cxt->addmounts) {
		int rc;
		struct libmnt_addmount *ad =
				list_entry(p, struct libmnt_addmount, mounts);

		DBG(CXT, ul_debugobj(cxt, "mount(2) changing flag: 0x%08lx %s",
				ad->mountflags,
				ad->mountflags & MS_REC ? " (recursive)" : ""));

		rc = mount("none", target, NULL,
				ad->mountflags | (flags & MS_SILENT), NULL);
		if (rc) {
			if (syserr)
				*syserr = -errno;
			DBG(CXT, ul_debugobj(cxt,
					"mount(2) failed [errno=%d %m]",
					errno));
			return rc;
		}
	}

	return 0;
}

/*
 * The default is to use fstype from cxt->fs, this could be overwritten by
 * @try_type argument. If @try_type is specified then mount with MS_SILENT.
 *
 * Returns: 0 on success,
 *         >0 in case of mount(2) error (returns syscall errno),
 *         <0 in case of other errors.
 */
static int do_mount(struct libmnt_context *cxt, const char *try_type)
{
	int rc = 0;
	const char *src, *target, *type;
	unsigned long flags;

	assert(cxt);
	assert(cxt->fs);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	if (try_type && !cxt->helper) {
		rc = mnt_context_prepare_helper(cxt, "mount", try_type);
		if (rc)
			return rc;
	}

	flags = cxt->mountflags;
	src = mnt_fs_get_srcpath(cxt->fs);
	target = mnt_fs_get_target(cxt->fs);

	if (cxt->helper) {
		rc = exec_helper(cxt);

		if (mnt_context_helper_executed(cxt)
		    && mnt_context_get_helper_status(cxt) == 0
		    && !list_empty(&cxt->addmounts)
		    && do_mount_additional(cxt, target, flags, NULL))

			return -MNT_ERR_APPLYFLAGS;
		return rc;
	}

	if (!target)
		return -EINVAL;
	if (!src) {
		/* unnecessary, should be already resolved in
		 * mnt_context_prepare_srcpath(), but to be sure... */
		DBG(CXT, ul_debugobj(cxt, "WARNING: source is NULL -- using \"none\"!"));
		src = "none";
	}
	type = try_type ? : mnt_fs_get_fstype(cxt->fs);

	if (!(flags & MS_MGC_MSK))
		flags |= MS_MGC_VAL;
	if (try_type)
		flags |= MS_SILENT;

	DBG(CXT, ul_debugobj(cxt, "%smount(2) "
			"[source=%s, target=%s, type=%s, "
			" mountflags=0x%08lx, mountdata=%s]",
			mnt_context_is_fake(cxt) ? "(FAKE) " : "",
			src, target, type,
			flags, cxt->mountdata ? "yes" : "<none>"));

	if (mnt_context_is_fake(cxt)) {
		/*
		 * fake
		 */
		cxt->syscall_status = 0;

	} else if (mnt_context_propagation_only(cxt)) {
		/*
		 * propagation flags *only*
		 */
		if (do_mount_additional(cxt, target, flags, &cxt->syscall_status))
			return -MNT_ERR_APPLYFLAGS;
	} else {
		/*
		 * regular mount
		 */
		if (mount(src, target, type, flags, cxt->mountdata)) {
			cxt->syscall_status = -errno;
			DBG(CXT, ul_debugobj(cxt, "mount(2) failed [errno=%d %m]",
							-cxt->syscall_status));
			return -cxt->syscall_status;
		}
		DBG(CXT, ul_debugobj(cxt, "  success"));
		cxt->syscall_status = 0;

		/*
		 * additional mounts for extra propagation flags
		 */
		if (!list_empty(&cxt->addmounts)
		    && do_mount_additional(cxt, target, flags, NULL)) {

			/* TODO: call umount? */
			return -MNT_ERR_APPLYFLAGS;
		}
	}

	if (try_type && cxt->update) {
		struct libmnt_fs *fs = mnt_update_get_fs(cxt->update);
		if (fs)
			rc = mnt_fs_set_fstype(fs, try_type);
	}

	return rc;
}

/* try mount(2) for all items in comma separated list of the filesystem @types */
static int do_mount_by_types(struct libmnt_context *cxt, const char *types)
{
	int rc = -EINVAL;
	char *p, *p0;

	assert(cxt);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	DBG(CXT, ul_debugobj(cxt, "trying to mount by FS list '%s'", types));

	p0 = p = strdup(types);
	if (!p)
		return -ENOMEM;
	do {
		char *autotype = NULL;
		char *end = strchr(p, ',');

		if (end)
			*end = '\0';

		DBG(CXT, ul_debugobj(cxt, "-->trying '%s'", p));

		/* Let's support things like "udf,iso9660,auto" */
		if (strcmp(p, "auto") == 0) {
			rc = mnt_context_guess_srcpath_fstype(cxt, &autotype);
			if (rc) {
				DBG(CXT, ul_debugobj(cxt, "failed to guess FS type [rc=%d]", rc));
				free(p0);
				free(autotype);
				return rc;
			}
			p = autotype;
			DBG(CXT, ul_debugobj(cxt, "   --> '%s'", p));
		}

		if (p)
			rc = do_mount(cxt, p);
		p = end ? end + 1 : NULL;
		free(autotype);
	} while (!mnt_context_get_status(cxt) && p);

	free(p0);
	return rc;
}


static int do_mount_by_pattern(struct libmnt_context *cxt, const char *pattern)
{
	int neg = pattern && strncmp(pattern, "no", 2) == 0;
	int rc = -EINVAL;
	char **filesystems, **fp;

	assert(cxt);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));

	/*
	 * Use the pattern as list of the filesystems
	 */
	if (!neg && pattern) {
		DBG(CXT, ul_debugobj(cxt, "use FS pattern as FS list"));
		return do_mount_by_types(cxt, pattern);
	}

	DBG(CXT, ul_debugobj(cxt, "trying to mount by FS pattern '%s'", pattern));

	/*
	 * Apply pattern to /etc/filesystems and /proc/filesystems
	 */
	rc = mnt_get_filesystems(&filesystems, neg ? pattern : NULL);
	if (rc)
		return rc;

	if (filesystems == NULL)
		return -MNT_ERR_NOFSTYPE;

	for (fp = filesystems; *fp; fp++) {
		rc = do_mount(cxt, *fp);
		if (mnt_context_get_status(cxt))
			break;
		if (mnt_context_get_syscall_errno(cxt) != EINVAL &&
		    mnt_context_get_syscall_errno(cxt) != ENODEV)
			break;
	}
	mnt_free_filesystems(filesystems);
	return rc;
}

/**
 * mnt_context_prepare_mount:
 * @cxt: context
 *
 * Prepare context for mounting, unnecessary for mnt_context_mount().
 *
 * Returns: negative number on error, zero on success
 */
int mnt_context_prepare_mount(struct libmnt_context *cxt)
{
	int rc = -EINVAL;

	if (!cxt || !cxt->fs || mnt_fs_is_swaparea(cxt->fs))
		return -EINVAL;
	if (!mnt_fs_get_source(cxt->fs) && !mnt_fs_get_target(cxt->fs))
		return -EINVAL;
	if (cxt->flags & MNT_FL_PREPARED)
		return 0;

	assert(cxt->helper_exec_status == 1);
	assert(cxt->syscall_status == 1);

	cxt->action = MNT_ACT_MOUNT;

	DBG(CXT, ul_debugobj(cxt, "mount: preparing"));

	rc = mnt_context_apply_fstab(cxt);
	if (!rc)
		rc = mnt_context_merge_mflags(cxt);
	if (!rc)
		rc = evaluate_permissions(cxt);
	if (!rc)
		rc = fix_optstr(cxt);
	if (!rc)
		rc = mnt_context_prepare_srcpath(cxt);
	if (!rc)
		rc = mnt_context_prepare_target(cxt);
	if (!rc)
		rc = mnt_context_guess_fstype(cxt);
	if (!rc)
		rc = mnt_context_prepare_helper(cxt, "mount", NULL);
	if (rc) {
		DBG(CXT, ul_debugobj(cxt, "mount: preparing failed"));
		return rc;
	}
	cxt->flags |= MNT_FL_PREPARED;
	return rc;
}

/**
 * mnt_context_do_mount
 * @cxt: context
 *
 * Call mount(2) or mount.type helper. Unnecessary for mnt_context_mount().
 *
 * Note that this function could be called only once. If you want to mount
 * another source or target, then you have to call mnt_reset_context().
 *
 * If you want to call mount(2) for the same source and target with different
 * mount flags or fstype, then call mnt_context_reset_status() and then try
 * again mnt_context_do_mount().
 *
 * WARNING: non-zero return code does not mean that mount(2) syscall or
 *          mount.type helper wasn't successfully called.
 *
 *          Check mnt_context_get_status() after error!
*
 * Returns: 0 on success;
 *         >0 in case of mount(2) error (returns syscall errno),
 *         <0 in case of other errors.
 */
int mnt_context_do_mount(struct libmnt_context *cxt)
{
	const char *type;
	int res;

	assert(cxt);
	assert(cxt->fs);
	assert(cxt->helper_exec_status == 1);
	assert(cxt->syscall_status == 1);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
	assert((cxt->flags & MNT_FL_PREPARED));
	assert((cxt->action == MNT_ACT_MOUNT));

	DBG(CXT, ul_debugobj(cxt, "mount: do mount"));

	if (!(cxt->flags & MNT_FL_MOUNTDATA))
		cxt->mountdata = (char *) mnt_fs_get_fs_options(cxt->fs);

	type = mnt_fs_get_fstype(cxt->fs);
	if (type) {
		if (strchr(type, ','))
			/* this only happens if fstab contains a list of filesystems */
			res = do_mount_by_types(cxt, type);
		else
			res = do_mount(cxt, NULL);
	} else
		res = do_mount_by_pattern(cxt, cxt->fstype_pattern);

#ifdef USE_LIBMOUNT_SUPPORT_MTAB
	if (mnt_context_get_status(cxt)
	    && !mnt_context_is_fake(cxt)
	    && !cxt->helper) {
		/*
		 * Mounted by mount(2), do some post-mount checks
		 *
		 * Kernel allows to use MS_RDONLY for bind mounts, but the
		 * read-only request could be silently ignored. Check it to
		 * avoid 'ro' in mtab and 'rw' in /proc/mounts.
		 */
		if ((cxt->mountflags & MS_BIND)
		    && (cxt->mountflags & MS_RDONLY)
		    && !mnt_is_readonly(mnt_context_get_target(cxt)))

			mnt_context_set_mflags(cxt,
					cxt->mountflags & ~MS_RDONLY);


		/* Kernel can silently add MS_RDONLY flag when mounting file
		 * system that does not have write support. Check this to avoid
		 * 'ro' in /proc/mounts and 'rw' in mtab.
		 */
		if (!(cxt->mountflags & (MS_RDONLY | MS_MOVE))
		    && !mnt_context_propagation_only(cxt)
		    && mnt_is_readonly(mnt_context_get_target(cxt)))

			mnt_context_set_mflags(cxt,
					cxt->mountflags | MS_RDONLY);
	}
#endif

	return res;
}

/**
 * mnt_context_finalize_mount:
 * @cxt: context
 *
 * Mtab update, etc. Unnecessary for mnt_context_mount(), but should be called
 * after mnt_context_do_mount(). See also mnt_context_set_syscall_status().
 *
 * Returns: negative number on error, 0 on success.
 */
int mnt_context_finalize_mount(struct libmnt_context *cxt)
{
	int rc;

	assert(cxt);
	assert(cxt->fs);
	assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED));
	assert((cxt->flags & MNT_FL_PREPARED));

	rc = mnt_context_prepare_update(cxt);
	if (!rc)
		rc = mnt_context_update_tabs(cxt);;
	return rc;
}

/**
 * mnt_context_mount:
 * @cxt: mount context
 *
 * High-level, mounts the filesystem by mount(2) or fork()+exec(/sbin/mount.type).
 *
 * This is similar to:
 *
 *	mnt_context_prepare_mount(cxt);
 *	mnt_context_do_mount(cxt);
 *	mnt_context_finalize_mount(cxt);
 *
 * See also mnt_context_disable_helpers().
 *
 * Note that this function should be called only once. If you want to mount with
 * different settings, then you have to call mnt_reset_context(). It's NOT enough
 * to call mnt_context_reset_status(). If you want to call this function more than
 * once, the whole context has to be reset.
 *
 * WARNING: non-zero return code does not mean that mount(2) syscall or
 *          mount.type helper wasn't successfully called.
 *
 *          Check mnt_context_get_status() after error!
 *
 * Returns: 0 on success;
 *         >0 in case of mount(2) error (returns syscall errno),
 *         <0 in case of other errors.
 */
int mnt_context_mount(struct libmnt_context *cxt)
{
	int rc;

	assert(cxt);
	assert(cxt->fs);
	assert(cxt->helper_exec_status == 1);
	assert(cxt->syscall_status == 1);

again:
	rc = mnt_context_prepare_mount(cxt);
	if (!rc)
		rc = mnt_context_prepare_update(cxt);
	if (!rc)
		rc = mnt_context_do_mount(cxt);
	if (!rc)
		rc = mnt_context_update_tabs(cxt);

	/*
	 * Read-only device; try mount filesystem read-only
	 */
	if ((rc == -EROFS && !mnt_context_syscall_called(cxt))	/* before syscall; rdonly loopdev */
	     || mnt_context_get_syscall_errno(cxt) == EROFS	/* syscall failed with EROFS */
	     || mnt_context_get_syscall_errno(cxt) == EACCES)	/* syscall failed with EACCES */
	{
		unsigned long mflags = 0;

		mnt_context_get_mflags(cxt, &mflags);

		if (!(mflags & MS_RDONLY)			/* not yet RDONLY */
		    && !(mflags & MS_REMOUNT)			/* not remount */
		    && !(mflags & MS_BIND)			/* not bin mount */
		    && !mnt_context_is_rwonly_mount(cxt)) {	/* no explicit read-write */

			assert(!(cxt->flags & MNT_FL_FORCED_RDONLY));
			DBG(CXT, ul_debugobj(cxt, "write-protected source, trying RDONLY."));

			mnt_context_reset_status(cxt);
			mnt_context_set_mflags(cxt, mflags | MS_RDONLY);
			cxt->flags |= MNT_FL_FORCED_RDONLY;
			goto again;
		}
	}
	return rc;
}

/**
 * mnt_context_next_mount:
 * @cxt: context
 * @itr: iterator
 * @fs: returns the current filesystem
 * @mntrc: returns the return code from mnt_context_mount()
 * @ignored: returns 1 for non-matching and 2 for already mounted filesystems
 *
 * This function tries to mount the next filesystem from fstab (as returned by
 * mnt_context_get_fstab()). See also mnt_context_set_fstab().
 *
 * You can filter out filesystems by:
 *	mnt_context_set_options_pattern() to simulate mount -a -O pattern
 *	mnt_context_set_fstype_pattern()  to simulate mount -a -t pattern
 *
 * If the filesystem is already mounted or does not match defined criteria,
 * then the mnt_context_next_mount() function returns zero, but the @ignored is
 * non-zero. Note that the root filesystem and filesystems with "noauto" option
 * are always ignored.
 *
 * If mount(2) syscall or mount.type helper failed, then the
 * mnt_context_next_mount() function returns zero, but the @mntrc is non-zero.
 * Use also mnt_context_get_status() to check if the filesystem was
 * successfully mounted.
 *
 * Returns: 0 on success,
 *         <0 in case of error (!= mount(2) errors)
 *          1 at the end of the list.
 */
int mnt_context_next_mount(struct libmnt_context *cxt,
			   struct libmnt_iter *itr,
			   struct libmnt_fs **fs,
			   int *mntrc,
			   int *ignored)
{
	struct libmnt_table *fstab, *mtab;
	const char *o, *tgt;
	int rc, mounted = 0;

	if (ignored)
		*ignored = 0;
	if (mntrc)
		*mntrc = 0;

	if (!cxt || !fs || !itr)
		return -EINVAL;

	mtab = cxt->mtab;
	cxt->mtab = NULL;		/* do not reset mtab */
	mnt_reset_context(cxt);
	cxt->mtab = mtab;

	rc = mnt_context_get_fstab(cxt, &fstab);
	if (rc)
		return rc;

	rc = mnt_table_next_fs(fstab, itr, fs);
	if (rc != 0)
		return rc;	/* more filesystems (or error) */

	o = mnt_fs_get_user_options(*fs);
	tgt = mnt_fs_get_target(*fs);

	DBG(CXT, ul_debugobj(cxt, "next-mount: trying %s", tgt));

	/*  ignore swap */
	if (mnt_fs_is_swaparea(*fs) ||

	/* ignore root filesystem */
	   (tgt && (strcmp(tgt, "/") == 0 || strcmp(tgt, "root") == 0)) ||

	/* ignore noauto filesystems */
	   (o && mnt_optstr_get_option(o, "noauto", NULL, NULL) == 0) ||

	/* ignore filesystems which don't match options patterns */
	   (cxt->fstype_pattern && !mnt_fs_match_fstype(*fs,
					cxt->fstype_pattern)) ||

	/* ignore filesystems which don't match type patterns */
	   (cxt->optstr_pattern && !mnt_fs_match_options(*fs,
					cxt->optstr_pattern))) {
		if (ignored)
			*ignored = 1;
		DBG(CXT, ul_debugobj(cxt, "next-mount: not-match "
				"[fstype: %s, t-pattern: %s, options: %s, O-pattern: %s]",
				mnt_fs_get_fstype(*fs),
				cxt->fstype_pattern,
				mnt_fs_get_options(*fs),
				cxt->optstr_pattern));
		return 0;
	}

	/* ignore already mounted filesystems */
	rc = mnt_context_is_fs_mounted(cxt, *fs, &mounted);
	if (rc)
		return rc;
	if (mounted) {
		if (ignored)
			*ignored = 2;
		return 0;
	}

	if (mnt_context_is_fork(cxt)) {
		rc = mnt_fork_context(cxt);
		if (rc)
			return rc;		/* fork error */

		if (mnt_context_is_parent(cxt)) {
			return 0;		/* parent */
		}
	}

	/* child or non-forked */

	rc = mnt_context_set_fs(cxt, *fs);
	if (!rc) {
		rc = mnt_context_mount(cxt);
		if (mntrc)
			*mntrc = rc;
	}

	if (mnt_context_is_child(cxt)) {
		DBG(CXT, ul_debugobj(cxt, "next-mount: child exit [rc=%d]", rc));
		DBG_FLUSH;
		exit(rc);
	}
	return 0;
}

/*
 * Returns 1 if @dir parent is shared
 */
static int is_shared_tree(struct libmnt_context *cxt, const char *dir)
{
	struct libmnt_table *tb = NULL;
	struct libmnt_fs *fs;
	unsigned long mflags = 0;
	char *mnt = NULL, *p;
	int rc = 0;

	if (!dir)
		return 0;
	if (mnt_context_get_mtab(cxt, &tb) || !tb)
		goto done;

	mnt = strdup(dir);
	if (!mnt)
		goto done;
	p = strrchr(mnt, '/');
	if (!p)
		goto done;
	if (p > mnt)
		*p = '\0';
	fs = mnt_table_find_mountpoint(tb, mnt, MNT_ITER_BACKWARD);

	rc = fs && mnt_fs_is_kernel(fs)
		&& mnt_fs_get_propagation(fs, &mflags) == 0
		&& (mflags & MS_SHARED);
done:
	free(mnt);
	return rc;
}

int mnt_context_get_mount_excode(
			struct libmnt_context *cxt,
			int rc,
			char *buf,
			size_t bufsz)
{
	int syserr;
	struct stat st;
	unsigned long uflags = 0, mflags = 0;

	int restricted = mnt_context_is_restricted(cxt);
	const char *tgt = mnt_context_get_target(cxt);
	const char *src = mnt_context_get_source(cxt);

	if (mnt_context_helper_executed(cxt)) {
		/*
		 * /sbin/mount.<type> called, return status
		 */
		if (rc == -MNT_ERR_APPLYFLAGS && buf)
			snprintf(buf, bufsz, _("WARNING: failed to apply propagation flags"));

		return mnt_context_get_helper_status(cxt);
	}

	if (rc == 0 && mnt_context_get_status(cxt) == 1) {
		/*
		 * Libmount success && syscall success.
		 */
		return MNT_EX_SUCCESS;
	}

	mnt_context_get_mflags(cxt, &mflags);		/* mount(2) flags */
	mnt_context_get_user_mflags(cxt, &uflags);	/* userspace flags */

	if (!mnt_context_syscall_called(cxt)) {
		/*
		 * libmount errors (extra library checks)
		 */
		switch (rc) {
		case -EPERM:
			if (buf)
				snprintf(buf, bufsz, _("operation permitted for root only"));
			return MNT_EX_USAGE;
		case -EBUSY:
			if (buf)
				snprintf(buf, bufsz, _("%s is already mounted"), src);
			return MNT_EX_USAGE;
		case -MNT_ERR_NOFSTAB:
			if (!buf)
				return MNT_EX_USAGE;
			if (mnt_context_is_swapmatch(cxt))
				snprintf(buf, bufsz, _("can't find in %s"),
						mnt_get_fstab_path());
			else if (tgt)
				snprintf(buf, bufsz, _("can't find mount point in %s"),
						mnt_get_fstab_path());
			else if (src)
				snprintf(buf, bufsz, _("can't find mount source %s in %s"),
						src, mnt_get_fstab_path());
			return MNT_EX_USAGE;
		case -MNT_ERR_AMBIFS:
			if (buf)
				snprintf(buf, bufsz, _("more filesystems detected on %s; use -t <type> or wipefs(8)"), src);
			return MNT_EX_USAGE;
		case -MNT_ERR_NOFSTYPE:
			if (buf)
				snprintf(buf, bufsz, restricted ?
						_("failed to determine filesystem type") :
						_("no filesystem type specified"));
			return MNT_EX_USAGE;
		case -MNT_ERR_NOSOURCE:
			if (uflags & MNT_MS_NOFAIL)
				return MNT_EX_SUCCESS;
			if (buf) {
				if (src)
					snprintf(buf, bufsz, _("can't find %s"), src);
				else
					snprintf(buf, bufsz, _("no mount source specified"));
			}
			return MNT_EX_USAGE;
		case -MNT_ERR_MOUNTOPT:
			if (buf)
				snprintf(buf, bufsz, errno ?
						_("failed to parse mount options: %m") :
						_("failed to parse mount options"));
			return MNT_EX_USAGE;
		case -MNT_ERR_LOOPDEV:
			if (buf)
				snprintf(buf, bufsz, _("failed to setup loop device for %s"), src);
			return MNT_EX_FAIL;
		case -MNT_ERR_LOOPOVERLAP:
			if (buf)
				snprintf(buf, bufsz, _("overlapping loop device exists for %s"), src);
			return MNT_EX_FAIL;
		default:
			return mnt_context_get_generic_excode(rc, buf, bufsz, _("mount failed: %m"));
		}

	} else if (mnt_context_get_syscall_errno(cxt) == 0) {
		/*
		 * mount(2) syscall success, but something else failed
		 * (probably error in mtab processing).
		 */
		if (rc < 0)
			return mnt_context_get_generic_excode(rc, buf, bufsz,
				_("filesystem was mounted, but any subsequent operation failed: %m"));

		return MNT_EX_SOFTWARE;	/* internal error */

	}

	/*
	 * mount(2) errors
	 */
	syserr = mnt_context_get_syscall_errno(cxt);


	switch(syserr) {
	case EPERM:
		if (!buf)
			break;
		if (geteuid() == 0) {
			if (stat(tgt, &st) || !S_ISDIR(st.st_mode))
				snprintf(buf, bufsz, _("mount point is not a directory"));
			else
				snprintf(buf, bufsz, _("permission denied"));
		} else
			snprintf(buf, bufsz, _("must be superuser to use mount"));
		break;

	case EBUSY:
	{
		struct libmnt_table *tb;

		if (!buf)
			break;
		if (mflags & MS_REMOUNT) {
			snprintf(buf, bufsz, _("mount point is busy"));
			break;
		}
		if (src && mnt_context_get_mtab(cxt, &tb) == 0) {
			struct libmnt_iter itr;
			struct libmnt_fs *fs;

			mnt_reset_iter(&itr, MNT_ITER_FORWARD);
			while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
				const char *s = mnt_fs_get_srcpath(fs),
					   *t = mnt_fs_get_target(fs);

				if (t && s && mnt_fs_streq_srcpath(fs, src)) {
					snprintf(buf, bufsz, _("%s already mounted on %s"), s, t);
					break;
				}
			}
		}
		if (!*buf)
			snprintf(buf, bufsz, _("%s already mounted or mount point busy"), src);
		break;
	}
	case ENOENT:
		if (tgt && lstat(tgt, &st)) {
			if (buf)
				snprintf(buf, bufsz, _("mount point does not exist"));
		} else if (tgt && stat(tgt, &st)) {
			if (buf)
				snprintf(buf, bufsz, _("mount point is a symbolic link to nowhere"));
		} else if (src && stat(src, &st)) {
			if (uflags & MNT_MS_NOFAIL)
				return MNT_EX_SUCCESS;
			if (buf)
				snprintf(buf, bufsz, _("special device %s does not exist"), src);
		} else if (buf) {
			errno = syserr;
			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
		}
		break;

	case ENOTDIR:
		if (stat(tgt, &st) || ! S_ISDIR(st.st_mode)) {
			if (buf)
				snprintf(buf, bufsz, _("mount point is not a directory"));
		} else if (src && stat(src, &st) && errno == ENOTDIR) {
			if (uflags & MNT_MS_NOFAIL)
				return MNT_EX_SUCCESS;
			if (buf)
				snprintf(buf, bufsz, _("special device %s does not exist "
					 "(a path prefix is not a directory)"), src);
		} else if (buf) {
			errno = syserr;
			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
		}
		break;

	case EINVAL:
		if (!buf)
			break;
		if (mflags & MS_REMOUNT)
			snprintf(buf, bufsz, _("mount point not mounted or bad option"));
		else if (rc == -MNT_ERR_APPLYFLAGS)
			snprintf(buf, bufsz, _("not mount point or bad option"));
		else if ((mflags & MS_MOVE) && is_shared_tree(cxt, src))
			snprintf(buf, bufsz,
				_("bad option; moving a mount "
				  "residing under a shared mount is unsupported"));
		else if (mnt_fs_is_netfs(mnt_context_get_fs(cxt)))
			snprintf(buf, bufsz,
				_("bad option; for several filesystems (e.g. nfs, cifs) "
				  "you might need a /sbin/mount.<type> helper program"));
		else
			snprintf(buf, bufsz,
				_("wrong fs type, bad option, bad superblock on %s, "
				  "missing codepage or helper program, or other error"),
				src);
		break;

	case EMFILE:
		if (buf)
			snprintf(buf, bufsz, _("mount table full"));
		break;

	case EIO:
		if (buf)
			snprintf(buf, bufsz, _("can't read superblock on %s"), src);
		break;

	case ENODEV:
		if (!buf)
			break;
		if (mnt_context_get_fstype(cxt))
			snprintf(buf, bufsz, _("unknown filesystem type '%s'"),
					mnt_context_get_fstype(cxt));
		else
			snprintf(buf, bufsz, _("unknown filesystem type"));
		break;

	case ENOTBLK:
		if (uflags & MNT_MS_NOFAIL)
			return MNT_EX_SUCCESS;
		if (!buf)
			break;
		if (stat(src, &st))
			snprintf(buf, bufsz, _("%s is not a block device, and stat(2) fails?"), src);
		else if (S_ISBLK(st.st_mode))
			snprintf(buf, bufsz,
				_("the kernel does not recognize %s as a block device; "
				  "maybe \"modprobe driver\" is necessary"), src);
		else if (S_ISREG(st.st_mode))
			snprintf(buf, bufsz, _("%s is not a block device; try \"-o loop\""), src);
		else
			snprintf(buf, bufsz, _("%s is not a block device"), src);
		break;

	case ENXIO:
		if (uflags & MNT_MS_NOFAIL)
			return MNT_EX_SUCCESS;
		if (buf)
			snprintf(buf, bufsz, _("%s is not a valid block device"), src);
		break;

	case EACCES:
	case EROFS:
		if (!buf)
			break;
		if (mflags & MS_RDONLY)
			snprintf(buf, bufsz, _("cannot mount %s read-only"), src);
		else if (mnt_context_is_rwonly_mount(cxt))
			snprintf(buf, bufsz, _("%s is write-protected but explicit read-write mode requested"), src);
		else if (mflags & MS_REMOUNT)
			snprintf(buf, bufsz, _("cannot remount %s read-write, is write-protected"), src);
		else if (mflags & MS_BIND)
			snprintf(buf, bufsz, _("bind %s failed"), src);
		else {
			errno = syserr;
			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
		}
		break;

	case ENOMEDIUM:
		if (uflags & MNT_MS_NOFAIL)
			return MNT_EX_SUCCESS;
		if (buf)
			snprintf(buf, bufsz, _("no medium found on %s"), src);
		break;

	default:
		if (buf) {
			errno = syserr;
			snprintf(buf, bufsz, _("mount(2) system call failed: %m"));
		}
		break;
	}

	return MNT_EX_FAIL;
}