(root)/
util-linux-2.39/
libmount/
src/
tab_update.c
       1  /* SPDX-License-Identifier: LGPL-2.1-or-later */
       2  /*
       3   * This file is part of libmount from util-linux project.
       4   *
       5   * Copyright (C) 2011-2018 Karel Zak <kzak@redhat.com>
       6   *
       7   * libmount is free software; you can redistribute it and/or modify it
       8   * under the terms of the GNU Lesser General Public License as published by
       9   * the Free Software Foundation; either version 2.1 of the License, or
      10   * (at your option) any later version.
      11   */
      12  
      13  /**
      14   * SECTION: update
      15   * @title: Tables update
      16   * @short_description: userspace mount information management
      17   *
      18   * The struct libmnt_update provides an abstraction to manage mount options in
      19   * userspace independently of system configuration. The userspace mount options
      20   * (e.g. user=) are stored in the /run/mount/utab file.
      21   *
      22   * It's recommended to use high-level struct libmnt_context API.
      23   */
      24  #include <sys/file.h>
      25  #include <fcntl.h>
      26  #include <signal.h>
      27  
      28  #include "mountP.h"
      29  #include "mangle.h"
      30  #include "pathnames.h"
      31  #include "strutils.h"
      32  
      33  struct libmnt_update {
      34  	char		*target;
      35  	struct libmnt_fs *fs;
      36  	char		*filename;
      37  	unsigned long	mountflags;
      38  	int		ready;
      39  
      40  	struct libmnt_table *mountinfo;
      41  };
      42  
      43  static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
      44  static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags);
      45  
      46  /**
      47   * mnt_new_update:
      48   *
      49   * Returns: newly allocated update handler
      50   */
      51  struct libmnt_update *mnt_new_update(void)
      52  {
      53  	struct libmnt_update *upd;
      54  
      55  	upd = calloc(1, sizeof(*upd));
      56  	if (!upd)
      57  		return NULL;
      58  
      59  	DBG(UPDATE, ul_debugobj(upd, "allocate"));
      60  	return upd;
      61  }
      62  
      63  /**
      64   * mnt_free_update:
      65   * @upd: update
      66   *
      67   * Deallocates struct libmnt_update handler.
      68   */
      69  void mnt_free_update(struct libmnt_update *upd)
      70  {
      71  	if (!upd)
      72  		return;
      73  
      74  	DBG(UPDATE, ul_debugobj(upd, "free"));
      75  
      76  	mnt_unref_fs(upd->fs);
      77  	mnt_unref_table(upd->mountinfo);
      78  	free(upd->target);
      79  	free(upd->filename);
      80  	free(upd);
      81  }
      82  
      83  /*
      84   * Returns 0 on success, <0 in case of error.
      85   */
      86  int mnt_update_set_filename(struct libmnt_update *upd, const char *filename)
      87  {
      88  	const char *path = NULL;
      89  	int rw = 0;
      90  
      91  	if (!upd)
      92  		return -EINVAL;
      93  
      94  	/* filename explicitly defined */
      95  	if (filename) {
      96  		char *p = strdup(filename);
      97  		if (!p)
      98  			return -ENOMEM;
      99  
     100  		free(upd->filename);
     101  		upd->filename = p;
     102  	}
     103  
     104  	if (upd->filename)
     105  		return 0;
     106  
     107  	/* detect tab filename -- /run/mount/utab
     108  	 */
     109  	path = NULL;
     110  	mnt_has_regular_utab(&path, &rw);
     111  	if (!rw)
     112  		return -EACCES;
     113  	upd->filename = strdup(path);
     114  	if (!upd->filename)
     115  		return -ENOMEM;
     116  
     117  	return 0;
     118  }
     119  
     120  /**
     121   * mnt_update_get_filename:
     122   * @upd: update
     123   *
     124   * This function returns the file name of the up-dated file.
     125   *
     126   * Returns: pointer to filename that will be updated or NULL in case of error.
     127   */
     128  const char *mnt_update_get_filename(struct libmnt_update *upd)
     129  {
     130  	return upd ? upd->filename : NULL;
     131  }
     132  
     133  /**
     134   * mnt_update_is_ready:
     135   * @upd: update handler
     136   *
     137   * Returns: 1 if entry described by @upd is successfully prepared and will be
     138   * written to the utab file.
     139   */
     140  int mnt_update_is_ready(struct libmnt_update *upd)
     141  {
     142  	return upd ? upd->ready : FALSE;
     143  }
     144  
     145  /**
     146   * mnt_update_set_fs:
     147   * @upd: update handler
     148   * @mountflags: MS_* flags
     149   * @target: umount target, must be NULL for mount
     150   * @fs: mount filesystem description, must be NULL for umount
     151   *
     152   * Returns: <0 in case on error, 0 on success, 1 if update is unnecessary.
     153   */
     154  int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags,
     155  		      const char *target, struct libmnt_fs *fs)
     156  {
     157  	int rc;
     158  
     159  	if (!upd)
     160  		return -EINVAL;
     161  	if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs)))
     162  		return -EINVAL;
     163  	if (target && fs)
     164  		return -EINVAL;
     165  
     166  	DBG(UPDATE, ul_debugobj(upd,
     167  			"resetting FS [target=%s, flags=0x%08lx]",
     168  			target, mountflags));
     169  	if (fs) {
     170  		DBG(UPDATE, ul_debugobj(upd, "FS template:"));
     171  		DBG(UPDATE, mnt_fs_print_debug(fs, stderr));
     172  	}
     173  
     174  	mnt_unref_fs(upd->fs);
     175  	free(upd->target);
     176  	upd->ready = FALSE;
     177  	upd->fs = NULL;
     178  	upd->target = NULL;
     179  	upd->mountflags = 0;
     180  
     181  	if (mountflags & MS_PROPAGATION)
     182  		return 1;
     183  
     184  	upd->mountflags = mountflags;
     185  
     186  	rc = mnt_update_set_filename(upd, NULL);
     187  	if (rc) {
     188  		DBG(UPDATE, ul_debugobj(upd, "no writable file available [rc=%d]", rc));
     189  		return rc;	/* error or no file available (rc = 1) */
     190  	}
     191  	if (target) {
     192  		upd->target = strdup(target);
     193  		if (!upd->target)
     194  			return -ENOMEM;
     195  
     196  	} else if (fs) {
     197  		if (!(mountflags & MS_MOVE)) {
     198  			rc = utab_new_entry(upd, fs, mountflags);
     199  			if (rc)
     200  			       return rc;
     201  		} else {
     202  			upd->fs = mnt_copy_mtab_fs(fs);
     203  			if (!upd->fs)
     204  				return -ENOMEM;
     205  		}
     206  	}
     207  
     208  	DBG(UPDATE, ul_debugobj(upd, "ready"));
     209  	upd->ready = TRUE;
     210  	return 0;
     211  }
     212  
     213  /**
     214   * mnt_update_get_fs:
     215   * @upd: update
     216   *
     217   * Returns: update filesystem entry or NULL
     218   */
     219  struct libmnt_fs *mnt_update_get_fs(struct libmnt_update *upd)
     220  {
     221  	return upd ? upd->fs : NULL;
     222  }
     223  
     224  /**
     225   * mnt_update_get_mflags:
     226   * @upd: update
     227   *
     228   * Returns: mount flags as was set by mnt_update_set_fs()
     229   */
     230  unsigned long mnt_update_get_mflags(struct libmnt_update *upd)
     231  {
     232  	return upd ? upd->mountflags : 0;
     233  }
     234  
     235  /**
     236   * mnt_update_force_rdonly:
     237   * @upd: update
     238   * @rdonly: is read-only?
     239   *
     240   * Returns: 0 on success and negative number in case of error.
     241   */
     242  int mnt_update_force_rdonly(struct libmnt_update *upd, int rdonly)
     243  {
     244  	int rc = 0;
     245  
     246  	if (!upd || !upd->fs)
     247  		return -EINVAL;
     248  
     249  	if (rdonly && (upd->mountflags & MS_RDONLY))
     250  		return 0;
     251  	if (!rdonly && !(upd->mountflags & MS_RDONLY))
     252  		return 0;
     253  
     254  	if (rdonly)
     255  		upd->mountflags &= ~MS_RDONLY;
     256  	else
     257  		upd->mountflags |= MS_RDONLY;
     258  
     259  	return rc;
     260  }
     261  
     262  
     263  /*
     264   * Allocates an utab entry (upd->fs) for mount/remount. This function should be
     265   * called *before* mount(2) syscall. The @fs is used as a read-only template.
     266   *
     267   * Returns: 0 on success, negative number on error, 1 if utab's update is
     268   *          unnecessary.
     269   */
     270  static int utab_new_entry(struct libmnt_update *upd, struct libmnt_fs *fs,
     271  			  unsigned long mountflags)
     272  {
     273  	int rc = 0;
     274  	const char *o, *a;
     275  	char *u = NULL;
     276  
     277  	assert(fs);
     278  	assert(upd);
     279  	assert(upd->fs == NULL);
     280  	assert(!(mountflags & MS_MOVE));
     281  
     282  	DBG(UPDATE, ul_debug("prepare utab entry"));
     283  
     284  	o = mnt_fs_get_user_options(fs);
     285  	a = mnt_fs_get_attributes(fs);
     286  	upd->fs = NULL;
     287  
     288  	if (o) {
     289  		/* remove non-mtab options */
     290  		rc = mnt_optstr_get_options(o, &u,
     291  				mnt_get_builtin_optmap(MNT_USERSPACE_MAP),
     292  				MNT_NOMTAB);
     293  		if (rc)
     294  			goto err;
     295  	}
     296  
     297  	if (!u && !a) {
     298  		DBG(UPDATE, ul_debug("utab entry unnecessary (no options)"));
     299  		return 1;
     300  	}
     301  
     302  	/* allocate the entry */
     303  	upd->fs = mnt_copy_fs(NULL, fs);
     304  	if (!upd->fs) {
     305  		rc = -ENOMEM;
     306  		goto err;
     307  	}
     308  
     309  	rc = mnt_fs_set_options(upd->fs, u);
     310  	if (rc)
     311  		goto err;
     312  	rc = mnt_fs_set_attributes(upd->fs, a);
     313  	if (rc)
     314  		goto err;
     315  
     316  	if (!(mountflags & MS_REMOUNT)) {
     317  		rc = set_fs_root(upd, fs, mountflags);
     318  		if (rc)
     319  			goto err;
     320  	}
     321  
     322  	free(u);
     323  	DBG(UPDATE, ul_debug("utab entry OK"));
     324  	return 0;
     325  err:
     326  	free(u);
     327  	mnt_unref_fs(upd->fs);
     328  	upd->fs = NULL;
     329  	return rc;
     330  }
     331  
     332  /*
     333   * Sets fs-root and fs-type to @upd->fs according to the @fs template and
     334   * @mountfalgs. For MS_BIND mountflag it reads information about the source
     335   * filesystem from /proc/self/mountinfo.
     336   */
     337  static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs,
     338  		       unsigned long mountflags)
     339  {
     340  	struct libmnt_fs *src_fs;
     341  	char *fsroot = NULL;
     342  	const char *src, *fstype;
     343  	int rc = 0;
     344  
     345  	DBG(UPDATE, ul_debug("setting FS root"));
     346  
     347  	assert(upd);
     348  	assert(upd->fs);
     349  	assert(fs);
     350  
     351  	fstype = mnt_fs_get_fstype(fs);
     352  
     353  	if (mountflags & MS_BIND) {
     354  		if (!upd->mountinfo)
     355  			upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
     356  		src = mnt_fs_get_srcpath(fs);
     357  		if (src) {
     358  			 rc = mnt_fs_set_bindsrc(upd->fs, src);
     359  			 if (rc)
     360  				 goto err;
     361  		}
     362  
     363  	} else if (fstype && (strcmp(fstype, "btrfs") == 0 || strcmp(fstype, "auto") == 0)) {
     364  		if (!upd->mountinfo)
     365  			upd->mountinfo = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO);
     366  	}
     367  
     368  	src_fs = mnt_table_get_fs_root(upd->mountinfo, fs,
     369  					mountflags, &fsroot);
     370  	if (src_fs) {
     371  		src = mnt_fs_get_srcpath(src_fs);
     372  		rc = mnt_fs_set_source(upd->fs, src);
     373  		if (rc)
     374  			goto err;
     375  
     376  		mnt_fs_set_fstype(upd->fs, mnt_fs_get_fstype(src_fs));
     377  	}
     378  
     379  	upd->fs->root = fsroot;
     380  	return 0;
     381  err:
     382  	free(fsroot);
     383  	return rc;
     384  }
     385  
     386  /* mtab and fstab update -- returns zero on success
     387   */
     388  static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs)
     389  {
     390  	const char *o, *src, *fstype, *comm;
     391  	char *m1, *m2, *m3, *m4;
     392  	int rc;
     393  
     394  	assert(fs);
     395  	assert(f);
     396  
     397  	comm = mnt_fs_get_comment(fs);
     398  	src = mnt_fs_get_source(fs);
     399  	fstype = mnt_fs_get_fstype(fs);
     400  	o = mnt_fs_get_options(fs);
     401  
     402  	m1 = src ? mangle(src) : "none";
     403  	m2 = mangle(mnt_fs_get_target(fs));
     404  	m3 = fstype ? mangle(fstype) : "none";
     405  	m4 = o ? mangle(o) : "rw";
     406  
     407  	if (m1 && m2 && m3 && m4) {
     408  		if (comm)
     409  			fputs(comm, f);
     410  		rc = fprintf(f, "%s %s %s %s %d %d\n",
     411  				m1, m2, m3, m4,
     412  				mnt_fs_get_freq(fs),
     413  				mnt_fs_get_passno(fs));
     414  		if (rc > 0)
     415  			rc = 0;
     416  	} else
     417  		rc = -ENOMEM;
     418  
     419  	if (src)
     420  		free(m1);
     421  	free(m2);
     422  	if (fstype)
     423  		free(m3);
     424  	if (o)
     425  		free(m4);
     426  
     427  	return rc;
     428  }
     429  
     430  static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs)
     431  {
     432  	char *p;
     433  	int rc = 0;
     434  
     435  	if (!fs || !f)
     436  		return -EINVAL;
     437  
     438  	if (mnt_fs_get_id(fs) > 0) {
     439  		rc = fprintf(f, "ID=%d ", mnt_fs_get_id(fs));
     440  	}
     441  	if (rc >= 0) {
     442  		p = mangle(mnt_fs_get_source(fs));
     443  		if (p) {
     444  			rc = fprintf(f, "SRC=%s ", p);
     445  			free(p);
     446  		}
     447  	}
     448  	if (rc >= 0) {
     449  		p = mangle(mnt_fs_get_target(fs));
     450  		if (p) {
     451  			rc = fprintf(f, "TARGET=%s ", p);
     452  			free(p);
     453  		}
     454  	}
     455  	if (rc >= 0) {
     456  		p = mangle(mnt_fs_get_root(fs));
     457  		if (p) {
     458  			rc = fprintf(f, "ROOT=%s ", p);
     459  			free(p);
     460  		}
     461  	}
     462  	if (rc >= 0) {
     463  		p = mangle(mnt_fs_get_bindsrc(fs));
     464  		if (p) {
     465  			rc = fprintf(f, "BINDSRC=%s ", p);
     466  			free(p);
     467  		}
     468  	}
     469  	if (rc >= 0) {
     470  		p = mangle(mnt_fs_get_attributes(fs));
     471  		if (p) {
     472  			rc = fprintf(f, "ATTRS=%s ", p);
     473  			free(p);
     474  		}
     475  	}
     476  	if (rc >= 0) {
     477  		p = mangle(mnt_fs_get_user_options(fs));
     478  		if (p) {
     479  			rc = fprintf(f, "OPTS=%s", p);
     480  			free(p);
     481  		}
     482  	}
     483  	if (rc >= 0)
     484  		rc = fprintf(f, "\n");
     485  
     486  	if (rc > 0)
     487  		rc = 0;	/* success */
     488  	return rc;
     489  }
     490  
     491  static int update_table(struct libmnt_update *upd, struct libmnt_table *tb)
     492  {
     493  	FILE *f;
     494  	int rc, fd;
     495  	char *uq = NULL;
     496  
     497  	if (!tb || !upd->filename)
     498  		return -EINVAL;
     499  
     500  	DBG(UPDATE, ul_debugobj(upd, "%s: updating", upd->filename));
     501  
     502  	fd = mnt_open_uniq_filename(upd->filename, &uq);
     503  	if (fd < 0)
     504  		return fd;	/* error */
     505  
     506  	f = fdopen(fd, "w" UL_CLOEXECSTR);
     507  	if (f) {
     508  		struct stat st;
     509  		struct libmnt_iter itr;
     510  		struct libmnt_fs *fs;
     511  
     512  		mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     513  
     514  		if (tb->comms && mnt_table_get_intro_comment(tb))
     515  			fputs(mnt_table_get_intro_comment(tb), f);
     516  
     517  		while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
     518  			rc = fprintf_utab_fs(f, fs);
     519  			if (rc) {
     520  				DBG(UPDATE, ul_debugobj(upd,
     521  					"%s: write entry failed: %m", uq));
     522  				goto leave;
     523  			}
     524  		}
     525  		if (tb->comms && mnt_table_get_trailing_comment(tb))
     526  			fputs(mnt_table_get_trailing_comment(tb), f);
     527  
     528  		if (fflush(f) != 0) {
     529  			rc = -errno;
     530  			DBG(UPDATE, ul_debugobj(upd, "%s: fflush failed: %m", uq));
     531  			goto leave;
     532  		}
     533  
     534  		rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
     535  
     536  		if (!rc && stat(upd->filename, &st) == 0)
     537  			/* Copy uid/gid from the present file before renaming. */
     538  			rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0;
     539  
     540  		fclose(f);
     541  		f = NULL;
     542  
     543  		if (!rc)
     544  			rc = rename(uq, upd->filename) ? -errno : 0;
     545  	} else {
     546  		rc = -errno;
     547  		close(fd);
     548  	}
     549  
     550  leave:
     551  	if (f)
     552  		fclose(f);
     553  
     554  	unlink(uq);	/* be paranoid */
     555  	free(uq);
     556  	DBG(UPDATE, ul_debugobj(upd, "%s: done [rc=%d]", upd->filename, rc));
     557  	return rc;
     558  }
     559  
     560  /**
     561   * mnt_table_write_file
     562   * @tb: parsed file (e.g. fstab)
     563   * @file: target
     564   *
     565   * This function writes @tb to @file.
     566   *
     567   * Returns: 0 on success, negative number on error.
     568   */
     569  int mnt_table_write_file(struct libmnt_table *tb, FILE *file)
     570  {
     571  	int rc = 0;
     572  	struct libmnt_iter itr;
     573  	struct libmnt_fs *fs;
     574  
     575  	if (tb->comms && mnt_table_get_intro_comment(tb))
     576  		fputs(mnt_table_get_intro_comment(tb), file);
     577  
     578  	mnt_reset_iter(&itr, MNT_ITER_FORWARD);
     579  	while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
     580  		rc = fprintf_mtab_fs(file, fs);
     581  		if (rc)
     582  			return rc;
     583  	}
     584  	if (tb->comms && mnt_table_get_trailing_comment(tb))
     585  		fputs(mnt_table_get_trailing_comment(tb), file);
     586  
     587  	if (fflush(file) != 0)
     588  		rc = -errno;
     589  
     590  	DBG(TAB, ul_debugobj(tb, "write file done [rc=%d]", rc));
     591  	return rc;
     592  }
     593  
     594  /**
     595   * mnt_table_replace_file
     596   * @tb: parsed file (e.g. fstab)
     597   * @filename: target
     598   *
     599   * This function replaces @file by the new content from @tb.
     600   *
     601   * Returns: 0 on success, negative number on error.
     602   */
     603  int mnt_table_replace_file(struct libmnt_table *tb, const char *filename)
     604  {
     605  	int fd, rc = 0;
     606  	FILE *f;
     607  	char *uq = NULL;
     608  
     609  	DBG(TAB, ul_debugobj(tb, "%s: replacing", filename));
     610  
     611  	fd = mnt_open_uniq_filename(filename, &uq);
     612  	if (fd < 0)
     613  		return fd;	/* error */
     614  
     615  	f = fdopen(fd, "w" UL_CLOEXECSTR);
     616  	if (f) {
     617  		struct stat st;
     618  
     619  		mnt_table_write_file(tb, f);
     620  
     621  		if (fflush(f) != 0) {
     622  			rc = -errno;
     623  			DBG(UPDATE, ul_debug("%s: fflush failed: %m", uq));
     624  			goto leave;
     625  		}
     626  
     627  		rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0;
     628  
     629  		if (!rc && stat(filename, &st) == 0)
     630  			/* Copy uid/gid from the present file before renaming. */
     631  			rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0;
     632  
     633  		fclose(f);
     634  		f = NULL;
     635  
     636  		if (!rc)
     637  			rc = rename(uq, filename) ? -errno : 0;
     638  	} else {
     639  		rc = -errno;
     640  		close(fd);
     641  	}
     642  
     643  leave:
     644  	if (f)
     645  		fclose(f);
     646  	unlink(uq);
     647  	free(uq);
     648  
     649  	DBG(TAB, ul_debugobj(tb, "replace done [rc=%d]", rc));
     650  	return rc;
     651  }
     652  
     653  static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd)
     654  {
     655  	struct libmnt_fs *fs;
     656  
     657  	assert(upd);
     658  
     659  	fs = mnt_copy_fs(NULL, upd->fs);
     660  	if (!fs)
     661  		return -ENOMEM;
     662  
     663  	mnt_table_add_fs(tb, fs);
     664  	mnt_unref_fs(fs);
     665  
     666  	return update_table(upd, tb);
     667  }
     668  
     669  static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
     670  {
     671  	struct libmnt_table *tb;
     672  	int rc = 0;
     673  
     674  	assert(upd);
     675  	assert(upd->fs);
     676  
     677  	DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename));
     678  
     679  	if (lc)
     680  		rc = mnt_lock_file(lc);
     681  	if (rc)
     682  		return -MNT_ERR_LOCK;
     683  
     684  	tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
     685  	if (tb)
     686  		rc = add_file_entry(tb, upd);
     687  	if (lc)
     688  		mnt_unlock_file(lc);
     689  
     690  	mnt_unref_table(tb);
     691  	return rc;
     692  }
     693  
     694  static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc)
     695  {
     696  	struct libmnt_table *tb;
     697  	int rc = 0;
     698  
     699  	assert(upd);
     700  	assert(upd->target);
     701  
     702  	DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename));
     703  
     704  	if (lc)
     705  		rc = mnt_lock_file(lc);
     706  	if (rc)
     707  		return -MNT_ERR_LOCK;
     708  
     709  	tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
     710  	if (tb) {
     711  		struct libmnt_fs *rem = mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD);
     712  		if (rem) {
     713  			mnt_table_remove_fs(tb, rem);
     714  			rc = update_table(upd, tb);
     715  		}
     716  	}
     717  	if (lc)
     718  		mnt_unlock_file(lc);
     719  
     720  	mnt_unref_table(tb);
     721  	return rc;
     722  }
     723  
     724  static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc)
     725  {
     726  	struct libmnt_table *tb = NULL;
     727  	int rc = 0;
     728  
     729  	assert(upd);
     730  	DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename));
     731  
     732  	if (lc)
     733  		rc = mnt_lock_file(lc);
     734  	if (rc)
     735  		return -MNT_ERR_LOCK;
     736  
     737  	tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
     738  	if (tb) {
     739  		const char *upd_source = mnt_fs_get_srcpath(upd->fs);
     740  		const char *upd_target = mnt_fs_get_target(upd->fs);
     741  		struct libmnt_iter itr;
     742  		struct libmnt_fs *fs;
     743  		char *cn_target = mnt_resolve_path(upd_target, NULL);
     744  
     745  		if (!cn_target) {
     746  			rc = -ENOMEM;
     747  			goto done;
     748  		}
     749  
     750  		mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
     751  		while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
     752  			char *p;
     753  			const char *e;
     754  
     755  			e = startswith(mnt_fs_get_target(fs), upd_source);
     756  			if (!e || (*e && *e != '/'))
     757  				continue;
     758  			if (*e == '/')
     759  				e++;		/* remove extra '/' */
     760  
     761  			/* no subdirectory, replace entire path */
     762  			if (!*e)
     763  				rc = mnt_fs_set_target(fs, cn_target);
     764  
     765  			/* update start of the path, keep subdirectory */
     766  			else if (asprintf(&p, "%s/%s", cn_target, e) > 0) {
     767  				rc = mnt_fs_set_target(fs, p);
     768  				free(p);
     769  			} else
     770  				rc = -ENOMEM;
     771  
     772  			if (rc < 0)
     773  				break;
     774  		}
     775  
     776  		if (!rc)
     777  			rc = update_table(upd, tb);
     778  		free(cn_target);
     779  	}
     780  
     781  done:
     782  	if (lc)
     783  		mnt_unlock_file(lc);
     784  
     785  	mnt_unref_table(tb);
     786  	return rc;
     787  }
     788  
     789  static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc)
     790  {
     791  	struct libmnt_table *tb = NULL;
     792  	int rc = 0;
     793  	struct libmnt_fs *fs;
     794  
     795  	assert(upd);
     796  	assert(upd->fs);
     797  
     798  	DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename));
     799  
     800  	fs = upd->fs;
     801  
     802  	if (lc)
     803  		rc = mnt_lock_file(lc);
     804  	if (rc)
     805  		return -MNT_ERR_LOCK;
     806  
     807  	tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
     808  	if (tb) {
     809  		struct libmnt_fs *cur = mnt_table_find_target(tb,
     810  					mnt_fs_get_target(fs),
     811  					MNT_ITER_BACKWARD);
     812  		if (cur) {
     813  			rc = mnt_fs_set_attributes(cur,	mnt_fs_get_attributes(fs));
     814  			if (!rc)
     815  				rc = mnt_fs_set_options(cur, mnt_fs_get_options(fs));
     816  			if (!rc)
     817  				rc = update_table(upd, tb);
     818  		} else
     819  			rc = add_file_entry(tb, upd);	/* not found, add new */
     820  	}
     821  
     822  	if (lc)
     823  		mnt_unlock_file(lc);
     824  
     825  	mnt_unref_table(tb);
     826  	return rc;
     827  }
     828  
     829  /**
     830   * mnt_update_table:
     831   * @upd: update
     832   * @lc: lock or NULL
     833   *
     834   * High-level API to update /etc/mtab (or private /run/mount/utab file).
     835   *
     836   * The @lc lock is optional and will be created if necessary. Note that
     837   * an automatically created lock blocks all signals.
     838   *
     839   * See also mnt_lock_block_signals() and mnt_context_get_lock().
     840   *
     841   * Returns: 0 on success, negative number on error.
     842   */
     843  int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc)
     844  {
     845  	struct libmnt_lock *lc0 = lc;
     846  	int rc = -EINVAL;
     847  
     848  	if (!upd || !upd->filename)
     849  		return -EINVAL;
     850  	if (!upd->ready)
     851  		return 0;
     852  
     853  	DBG(UPDATE, ul_debugobj(upd, "%s: update tab", upd->filename));
     854  	if (upd->fs) {
     855  		DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr));
     856  	}
     857  	if (!lc) {
     858  		lc = mnt_new_lock(upd->filename, 0);
     859  		if (lc)
     860  			mnt_lock_block_signals(lc, TRUE);
     861  	}
     862  
     863  	if (!upd->fs && upd->target)
     864  		rc = update_remove_entry(upd, lc);	/* umount */
     865  	else if (upd->mountflags & MS_MOVE)
     866  		rc = update_modify_target(upd, lc);	/* move */
     867  	else if (upd->mountflags & MS_REMOUNT)
     868  		rc = update_modify_options(upd, lc);	/* remount */
     869  	else if (upd->fs)
     870  		rc = update_add_entry(upd, lc);	/* mount */
     871  
     872  	upd->ready = FALSE;
     873  	DBG(UPDATE, ul_debugobj(upd, "%s: update tab: done [rc=%d]",
     874  				upd->filename, rc));
     875  	if (lc != lc0)
     876  		 mnt_free_lock(lc);
     877  	return rc;
     878  }
     879  
     880  int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc)
     881  {
     882  	struct libmnt_table *tb = NULL;
     883  	struct libmnt_lock *lc0 = lc;
     884  	int rc = 0;
     885  
     886  	if (!upd || !upd->filename || (!upd->fs && !upd->target))
     887  		return -EINVAL;
     888  
     889  	DBG(UPDATE, ul_debugobj(upd, "%s: checking for previous update", upd->filename));
     890  
     891  	if (!lc) {
     892  		lc = mnt_new_lock(upd->filename, 0);
     893  		if (lc)
     894  			mnt_lock_block_signals(lc, TRUE);
     895  	}
     896  	if (lc) {
     897  		rc = mnt_lock_file(lc);
     898  		if (rc) {
     899  			rc = -MNT_ERR_LOCK;
     900  			goto done;
     901  		}
     902  	}
     903  
     904  	tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1);
     905  	if (lc)
     906  		mnt_unlock_file(lc);
     907  	if (!tb)
     908  		goto done;
     909  
     910  	if (upd->fs) {
     911  		/* mount */
     912  		const char *tgt = mnt_fs_get_target(upd->fs);
     913  		const char *src = mnt_fs_get_bindsrc(upd->fs) ?
     914  					mnt_fs_get_bindsrc(upd->fs) :
     915  					mnt_fs_get_source(upd->fs);
     916  
     917  		if (mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD)) {
     918  			DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s",
     919  						upd->filename, src, tgt));
     920  			rc = 1;
     921  		}
     922  	} else if (upd->target) {
     923  		/* umount */
     924  		if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) {
     925  			DBG(UPDATE, ul_debugobj(upd, "%s: not-found (umounted) %s",
     926  						upd->filename, upd->target));
     927  			rc = 1;
     928  		}
     929  	}
     930  
     931  	mnt_unref_table(tb);
     932  done:
     933  	if (lc && lc != lc0)
     934  		mnt_free_lock(lc);
     935  	DBG(UPDATE, ul_debugobj(upd, "%s: previous update check done [rc=%d]",
     936  				upd->filename, rc));
     937  	return rc;
     938  }
     939  
     940  
     941  #ifdef TEST_PROGRAM
     942  
     943  static int update(const char *target, struct libmnt_fs *fs, unsigned long mountflags)
     944  {
     945  	int rc;
     946  	struct libmnt_update *upd;
     947  
     948  	DBG(UPDATE, ul_debug("update test"));
     949  
     950  	upd = mnt_new_update();
     951  	if (!upd)
     952  		return -ENOMEM;
     953  
     954  	rc = mnt_update_set_fs(upd, mountflags, target, fs);
     955  	if (rc == 1) {
     956  		/* update is unnecessary */
     957  		rc = 0;
     958  		goto done;
     959  	}
     960  	if (rc) {
     961  		fprintf(stderr, "failed to set FS\n");
     962  		goto done;
     963  	}
     964  
     965  	/* [... mount(2) call should be here...]  */
     966  
     967  	rc = mnt_update_table(upd, NULL);
     968  done:
     969  	mnt_free_update(upd);
     970  	return rc;
     971  }
     972  
     973  static int test_add(struct libmnt_test *ts, int argc, char *argv[])
     974  {
     975  	struct libmnt_fs *fs = mnt_new_fs();
     976  	int rc;
     977  
     978  	if (argc < 5 || !fs)
     979  		return -1;
     980  	mnt_fs_set_source(fs, argv[1]);
     981  	mnt_fs_set_target(fs, argv[2]);
     982  	mnt_fs_set_fstype(fs, argv[3]);
     983  	mnt_fs_set_options(fs, argv[4]);
     984  
     985  	rc = update(NULL, fs, 0);
     986  	mnt_unref_fs(fs);
     987  	return rc;
     988  }
     989  
     990  static int test_remove(struct libmnt_test *ts, int argc, char *argv[])
     991  {
     992  	if (argc < 2)
     993  		return -1;
     994  	return update(argv[1], NULL, 0);
     995  }
     996  
     997  static int test_move(struct libmnt_test *ts, int argc, char *argv[])
     998  {
     999  	struct libmnt_fs *fs = mnt_new_fs();
    1000  	int rc;
    1001  
    1002  	if (argc < 3)
    1003  		return -1;
    1004  	mnt_fs_set_source(fs, argv[1]);
    1005  	mnt_fs_set_target(fs, argv[2]);
    1006  
    1007  	rc = update(NULL, fs, MS_MOVE);
    1008  
    1009  	mnt_unref_fs(fs);
    1010  	return rc;
    1011  }
    1012  
    1013  static int test_remount(struct libmnt_test *ts, int argc, char *argv[])
    1014  {
    1015  	struct libmnt_fs *fs = mnt_new_fs();
    1016  	int rc;
    1017  
    1018  	if (argc < 3)
    1019  		return -1;
    1020  	mnt_fs_set_target(fs, argv[1]);
    1021  	mnt_fs_set_options(fs, argv[2]);
    1022  
    1023  	rc = update(NULL, fs, MS_REMOUNT);
    1024  	mnt_unref_fs(fs);
    1025  	return rc;
    1026  }
    1027  
    1028  static int test_replace(struct libmnt_test *ts, int argc, char *argv[])
    1029  {
    1030  	struct libmnt_fs *fs = mnt_new_fs();
    1031  	struct libmnt_table *tb = mnt_new_table();
    1032  	int rc;
    1033  
    1034  	if (argc < 3)
    1035  		return -1;
    1036  
    1037  	mnt_table_enable_comments(tb, TRUE);
    1038  	mnt_table_parse_fstab(tb, NULL);
    1039  
    1040  	mnt_fs_set_source(fs, argv[1]);
    1041  	mnt_fs_set_target(fs, argv[2]);
    1042  	mnt_fs_append_comment(fs, "# this is new filesystem\n");
    1043  
    1044  	mnt_table_add_fs(tb, fs);
    1045  	mnt_unref_fs(fs);
    1046  
    1047  	rc = mnt_table_replace_file(tb, mnt_get_fstab_path());
    1048  	mnt_unref_table(tb);
    1049  	return rc;
    1050  }
    1051  
    1052  int main(int argc, char *argv[])
    1053  {
    1054  	struct libmnt_test tss[] = {
    1055  	{ "--add",    test_add,     "<src> <target> <type> <options>  add a line to mtab" },
    1056  	{ "--remove", test_remove,  "<target>                      MS_REMOUNT mtab change" },
    1057  	{ "--move",   test_move,    "<old_target>  <target>        MS_MOVE mtab change" },
    1058  	{ "--remount",test_remount, "<target>  <options>           MS_REMOUNT mtab change" },
    1059  	{ "--replace",test_replace, "<src> <target>                Add a line to LIBMOUNT_FSTAB and replace the original file" },
    1060  	{ NULL }
    1061  	};
    1062  
    1063  	return mnt_run_test(tss, argc, argv);
    1064  }
    1065  
    1066  #endif /* TEST_PROGRAM */