free(fs->user_optstr);
free(fs->attrs);
free(fs->opt_fields);
+ free(fs->comment);
free(fs);
}
return rc;
}
+/**
+ * mnt_fs_get_comment:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Returns: 0 on success, 1 when not found the @name or negative number in case of error.
+ */
+const char *mnt_fs_get_comment(struct libmnt_fs *fs)
+{
+ assert(fs);
+ if (!fs)
+ return NULL;
+ return fs->comment;
+}
+
+/**
+ * mnt_fs_set_comment:
+ * @fs: fstab entry pointer
+ * @comm: comment string
+ *
+ * Returns: 0 on success or <0 in case of error.
+ */
+int mnt_fs_set_comment(struct libmnt_fs *fs, const char *comm)
+{
+ char *p = NULL;
+
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+ if (comm) {
+ p = strdup(comm);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(fs->comment);
+ fs->comment = p;
+ return 0;
+}
+
+/**
+ * mnt_fs_append_comment:
+ * @fs: fstab entry pointer
+ *
+ * Returns: 0 on success or <0 in case of error.
+ */
+int mnt_fs_append_comment(struct libmnt_fs *fs, const char *comm)
+{
+ assert(fs);
+ if (!fs)
+ return -EINVAL;
+
+ return append_string(&fs->comment, comm);
+}
+
/**
* mnt_fs_match_target:
* @fs: filesystem
minor(mnt_fs_get_devno(fs)));
if (mnt_fs_get_tid(fs))
fprintf(file, "tid: %d\n", mnt_fs_get_tid(fs));
+ if (mnt_fs_get_comment(fs))
+ fprintf(file, "comment: '%s'\n", mnt_fs_get_comment(fs));
return 0;
}
extern off_t mnt_fs_get_usedsize(struct libmnt_fs *fs);
extern int mnt_fs_get_priority(struct libmnt_fs *fs);
+extern const char *mnt_fs_get_comment(struct libmnt_fs *fs);
+extern int mnt_fs_set_comment(struct libmnt_fs *fs, const char *comm);
+extern int mnt_fs_append_comment(struct libmnt_fs *fs, const char *comm);
+
extern int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
struct libmnt_cache *cache);
extern int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
extern int mnt_reset_table(struct libmnt_table *tb);
extern int mnt_table_get_nents(struct libmnt_table *tb);
+
+extern void mnt_table_enable_comments(struct libmnt_table *tb, int enable);
+extern const char *mnt_table_get_intro_comment(struct libmnt_table *tb);
+extern int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm);
+extern int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm);
+extern int mnt_table_set_tailing_comment(struct libmnt_table *tb, const char *comm);
+extern const char *mnt_table_get_tailing_comment(struct libmnt_table *tb);
+extern int mnt_table_append_tailing_comment(struct libmnt_table *tb, const char *comm);
+
extern int mnt_table_set_cache(struct libmnt_table *tb, struct libmnt_cache *mpc);
extern struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb);
extern int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs);
mnt_context_find_umount_fs;
mnt_table_find_mountpoint;
} MOUNT_2.22;
+
+MOUNT_2.24 {
+global:
+ mnt_fs_append_comment;
+ mnt_fs_get_comment;
+ mnt_fs_set_comment;
+ mnt_table_append_intro_comment;
+ mnt_table_append_tailing_comment;
+ mnt_table_enable_comments;
+ mnt_table_get_intro_comment;
+ mnt_table_get_tailing_comment;
+ mnt_table_set_intro_comment;
+ mnt_table_set_tailing_comment;
+} MOUNT_2.23;
int flags; /* MNT_FS_* flags */
pid_t tid; /* /proc/<tid>/mountinfo otherwise zero */
+ char *comment; /* fstab comment */
+
void *userdata; /* library independent data */
};
struct libmnt_table {
int fmt; /* MNT_FMT_* file format */
int nents; /* number of valid entries */
+ int comms; /* enable/disable comment parsing */
+ char *comm_intro; /* First comment in file */
+ char *comm_tail; /* Last comment in file */
struct libmnt_cache *cache; /* canonicalized paths/tags cache */
mnt_reset_table(tb);
DBG(TAB, mnt_debug_h(tb, "free"));
+ free(tb->comm_intro);
+ free(tb->comm_tail);
free(tb);
}
return tb ? tb->nents : 0;
}
+/**
+ * mnt_table_enable_comments:
+ * @tb: pointer to tab
+ *
+ * Enables parsing of comments.
+ *
+ * The initial (intro) file comment is accessible by
+ * mnt_table_get_intro_comment(). The intro and the comment of the first fstab
+ * entry has to be separated by blank line. The filesystem comments are
+ * accessible by mnt_fs_get_comment(). The tailing fstab comment is accessible
+ * by mnt_table_get_tailing_comment().
+ *
+ * <informalexample>
+ * <programlisting>
+ * #
+ * # Intro comment
+ * #
+ *
+ * # this comments belongs to the first fs
+ * LABEL=foo /mnt/foo auto defaults 1 2
+ * # this comments belongs to the second fs
+ * LABEL=bar /mnt/bar auto defaults 1 2
+ * # tailing comment
+ * </programlisting>
+ * </informalexample>
+ */
+void mnt_table_enable_comments(struct libmnt_table *tb, int enable)
+{
+ assert(tb);
+ if (tb)
+ tb->comms = enable;
+}
+
+/**
+ * mnt_table_get_intro_comment:
+ * @tb: pointer to tab
+ *
+ * Returns: initial comment in tb
+ */
+const char *mnt_table_get_intro_comment(struct libmnt_table *tb)
+{
+ assert(tb);
+ return tb ? tb->comm_intro : NULL;
+}
+
+/**
+ * mnt_table_set_into_comment:
+ * @tb: pointer to tab
+ * @comm: comment or NULL
+ *
+ * Sets initial comment in tb.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_set_intro_comment(struct libmnt_table *tb, const char *comm)
+{
+ char *p = NULL;
+
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+ if (comm) {
+ p = strdup(comm);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(tb->comm_intro);
+ tb->comm_intro = p;
+ return 0;
+}
+
+/**
+ * mnt_table_append_into_comment:
+ * @tb: pointer to tab
+ * @comm: comment of NULL
+ *
+ * Appends the initial comment in tb.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_append_intro_comment(struct libmnt_table *tb, const char *comm)
+{
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+ return append_string(&tb->comm_intro, comm);
+}
+
+/**
+ * mnt_table_get_tailing_comment:
+ * @tb: pointer to tab
+ *
+ * Returns: table tailing comment
+ */
+const char *mnt_table_get_tailing_comment(struct libmnt_table *tb)
+{
+ assert(tb);
+ return tb ? tb->comm_tail : NULL;
+}
+
+/**
+ * mnt_table_set_tailing_comment
+ * @tb: pointer to tab
+ *
+ * Sets tailing comment in table.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_set_tailing_comment(struct libmnt_table *tb, const char *comm)
+{
+ char *p = NULL;
+
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+ if (comm) {
+ p = strdup(comm);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(tb->comm_tail);
+ tb->comm_tail = p;
+ return 0;
+}
+
+/**
+ * mnt_table_append_tailing_comment:
+ * @tb: pointer to tab
+ * @comm: comment of NULL
+ *
+ * Appends to the tailing table comment.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int mnt_table_append_tailing_comment(struct libmnt_table *tb, const char *comm)
+{
+ assert(tb);
+ if (!tb)
+ return -EINVAL;
+ return append_string(&tb->comm_tail, comm);
+}
+
/**
* mnt_table_set_cache:
* @tb: pointer to tab
return 1; /* all errors are recoverable -- this is default */
}
-struct libmnt_table *create_table(const char *file)
+struct libmnt_table *create_table(const char *file, int comments)
{
struct libmnt_table *tb;
if (!tb)
goto err;
+ mnt_table_enable_comments(tb, comments);
mnt_table_set_parser_errcb(tb, parser_errcb);
if (mnt_table_parse_file(tb, file) != 0)
struct libmnt_fs *fs;
int rc = -1;
- tb = create_table(argv[1]);
+ tb = create_table(argv[1], FALSE);
if (!tb)
return -1;
struct libmnt_iter *itr = NULL;
struct libmnt_fs *fs;
int rc = -1;
+ int parse_comments = FALSE;
+
+ if (argc == 3 && !strcmp(argv[2], "--comments"))
+ parse_comments = TRUE;
- tb = create_table(argv[1]);
+ tb = create_table(argv[1], parse_comments);
if (!tb)
return -1;
if (!itr)
goto done;
+ if (mnt_table_get_intro_comment(tb))
+ fprintf(stdout, "Initial comment:\n\"%s\"\n",
+ mnt_table_get_intro_comment(tb));
+
while(mnt_table_next_fs(tb, itr, &fs) == 0)
mnt_fs_print_debug(fs, stdout);
+
+ if (mnt_table_get_tailing_comment(tb))
+ fprintf(stdout, "Trailing comment:\n\"%s\"\n",
+ mnt_table_get_tailing_comment(tb));
rc = 0;
done:
mnt_free_iter(itr);
file = argv[1], find = argv[2], what = argv[3];
- tb = create_table(file);
+ tb = create_table(file, FALSE);
if (!tb)
goto done;
struct libmnt_cache *mpc = NULL;
int rc = -1;
- tb = create_table(argv[1]);
+ tb = create_table(argv[1], FALSE);
if (!tb)
return -1;
mpc = mnt_new_cache();
return -1;
}
- fstab = create_table(argv[1]);
+ fstab = create_table(argv[1], FALSE);
if (!fstab)
goto done;
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
- { "--parse", test_parse, "<file> parse and print tab" },
+ { "--parse", test_parse, "<file> [--comments] parse and print tab" },
{ "--find-forward", test_find_fw, "<file> <source|target> <string>" },
{ "--find-backward", test_find_bw, "<file> <source|target> <string>" },
{ "--find-pair", test_find_pair, "<file> <source> <target>" },
return MNT_FMT_FSTAB; /* fstab, mtab or /proc/mounts */
}
+static int is_comment_line(char *line)
+{
+ char *p = skip_spaces(line);
+
+ if (p && (*p == '#' || *p == '\n'))
+ return 1;
+ return 0;
+}
+
+/* returns 1 if the last line in the @str is blank */
+static int is_terminated_by_blank(const char *str)
+{
+ size_t sz = str ? strlen(str) : 0;
+ const char *p = sz ? str + (sz - 1) : NULL;
+
+ if (!sz || !p || *p != '\n')
+ return 0; /* empty or not terminated by '\n' */
+ if (p == str)
+ return 1; /* only '\n' */
+ p--;
+ while (p >= str && (*p == ' ' || *p == '\t'))
+ p--;
+ return *p == '\n' ? 1 : 0;
+}
+
+/*
+ * Reads the next line from the file.
+ *
+ * Returns 0 if the line is comment
+ * 1 if the line is not comment
+ * <0 on error
+ */
+static int next_comment_line(char *buf, size_t bufsz,
+ FILE *f, char **last, int *nlines)
+{
+ if (fgets(buf, bufsz, f) == NULL)
+ return feof(f) ? 1 : -EINVAL;
+
+ ++*nlines;
+ *last = strchr(buf, '\n');
+
+ return is_comment_line(buf) ? 0 : 1;
+}
+
+static int append_comment(struct libmnt_table *tb,
+ struct libmnt_fs *fs,
+ const char *comm,
+ int eof)
+{
+ int rc, intro = mnt_table_get_nents(tb) == 0;
+
+ if (intro && is_terminated_by_blank(mnt_table_get_intro_comment(tb)))
+ intro = 0;
+
+ DBG(TAB, mnt_debug_h(tb, "appending %s comment",
+ intro ? "intro" :
+ eof ? "tailing" : "fs"));
+ if (intro)
+ rc = mnt_table_append_intro_comment(tb, comm);
+ else if (eof) {
+ rc = mnt_table_set_tailing_comment(tb,
+ mnt_fs_get_comment(fs));
+ if (!rc)
+ rc = mnt_table_append_tailing_comment(tb, comm);
+ if (!rc)
+ rc = mnt_fs_set_comment(fs, NULL);
+ } else
+ rc = mnt_fs_append_comment(fs, comm);
+ return rc;
+}
+
/*
* Read and parse the next line from {fs,m}tab or mountinfo
*/
-static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f, struct libmnt_fs *fs,
+static int mnt_table_parse_next(struct libmnt_table *tb, FILE *f,
+ struct libmnt_fs *fs,
const char *filename, int *nlines)
{
char buf[BUFSIZ];
goto err;
}
}
+
+ /* comments parser */
+ if (tb->comms
+ && (tb->fmt == MNT_FMT_GUESS || tb->fmt == MNT_FMT_FSTAB)
+ && is_comment_line(buf)) {
+ do {
+ rc = append_comment(tb, fs, buf, feof(f));
+ if (!rc)
+ rc = next_comment_line(buf,
+ sizeof(buf),
+ f, &s, nlines);
+ } while (rc == 0);
+
+ if (rc == 1 && feof(f))
+ rc = append_comment(tb, fs, NULL, 1);
+ if (rc < 0)
+ return rc;
+
+ }
+
*s = '\0';
if (--s >= buf && *s == '\r')
*s = '\0';
*/
static int fprintf_mtab_fs(FILE *f, struct libmnt_fs *fs)
{
- const char *o, *src, *fstype;
+ const char *o, *src, *fstype, *comm;
char *m1, *m2, *m3, *m4;
int rc;
assert(fs);
assert(f);
+ comm = mnt_fs_get_comment(fs);
src = mnt_fs_get_source(fs);
fstype = mnt_fs_get_fstype(fs);
o = mnt_fs_get_options(fs);
m4 = o ? mangle(o) : "rw";
if (m1 && m2 && m3 && m4) {
+ if (comm)
+ fputs(comm, f);
rc = fprintf(f, "%s %s %s %s %d %d\n",
m1, m2, m3, m4,
mnt_fs_get_freq(fs),
struct libmnt_fs *fs;
mnt_reset_iter(&itr, MNT_ITER_FORWARD);
+
+ if (tb->comms && mnt_table_get_intro_comment(tb))
+ fputs(mnt_table_get_intro_comment(tb), f);
+
while(mnt_table_next_fs(tb, &itr, &fs) == 0) {
if (upd->userspace_only)
rc = fprintf_utab_fs(f, fs);
goto leave;
}
}
+ if (tb->comms && mnt_table_get_tailing_comment(tb))
+ fputs(mnt_table_get_tailing_comment(tb), f);
if (fflush(f) != 0) {
rc = -errno;
--- /dev/null
+Initial comment:
+"# this is a leading comment
+
+"
+------ fs:
+source: UUID=d3a8f783-df75-4dc8-9163-975a891052c0
+target: /
+fstype: ext3
+Non-data:
+"# this comments belongs to the first fs
+"
+------ fs:
+source: UUID=fef7ccb3-821c-4de8-88dc-71472be5946f
+target: /boot
+fstype: ext3
+optstr: noatime,defaults
+VFS-optstr: noatime
+freq: 1
+pass: 2
+------ fs:
+source: UUID=1f2aa318-9c34-462e-8d29-260819ffd657
+target: swap
+fstype: swap
+Non-data:
+"
+# 3rd fs comment + newline padding
+
+"
+------ fs:
+source: tmpfs
+target: /dev/shm
+fstype: tmpfs
+optstr: defaults
+------ fs:
+source: devpts
+target: /dev/pts
+fstype: devpts
+optstr: gid=5,mode=620
+FS-opstr: gid=5,mode=620
+------ fs:
+source: sysfs
+target: /sys
+fstype: sysfs
+optstr: defaults
+------ fs:
+source: proc
+target: /proc
+fstype: proc
+optstr: defaults
+------ fs:
+source: /dev/mapper/foo
+target: /home/foo
+fstype: ext4
+Non-data:
+"# this is comment
+"
+------ fs:
+source: foo.com:/mnt/share
+target: /mnt/remote
+fstype: nfs
+optstr: noauto
+user-optstr: noauto
+------ fs:
+source: //bar.com/gogogo
+target: /mnt/gogogo
+fstype: cifs
+optstr: user=SRGROUP/baby,noauto
+user-optstr: user=SRGROUP/baby,noauto
+------ fs:
+source: /dev/foo
+target: /any/foo/
+fstype: auto
+optstr: defaults
+Trailing comment:
+"
+#this is a trailing comment
+"
--- /dev/null
+#
+ # this is a leading comment
+#
+
+# this comments belongs to the first fs
+UUID=d3a8f783-df75-4dc8-9163-975a891052c0 / ext3 noatime,defaults 1 1
+UUID=fef7ccb3-821c-4de8-88dc-71472be5946f /boot ext3 noatime,defaults 1 2
+
+# 3rd fs comment + newline padding
+
+UUID=1f2aa318-9c34-462e-8d29-260819ffd657 swap swap defaults 0 0
+tmpfs /dev/shm tmpfs defaults 0 0
+devpts /dev/pts devpts gid=5,mode=620 0 0
+sysfs /sys sysfs defaults 0 0
+proc /proc proc defaults 0 0
+# this is comment
+/dev/mapper/foo /home/foo ext4 noatime,defaults 0 0
+foo.com:/mnt/share /mnt/remote nfs noauto
+//bar.com/gogogo /mnt/gogogo cifs user=SRGROUP/baby,noauto
+/dev/foo /any/foo/ auto defaults 0 0
+
+#this is a trailing comment
sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT
ts_finalize_subtest
+ts_init_subtest "parse-fstab-full"
+ts_valgrind $TESTPROG --parse "$TS_SELF/files/fstab.comment" --comments &> $TS_OUTPUT
+sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT
+ts_finalize_subtest
+
ts_init_subtest "parse-mtab"
ts_valgrind $TESTPROG --parse "$TS_SELF/files/mtab" &> $TS_OUTPUT
sed -i -e 's/fs: 0x.*/fs:/g' $TS_OUTPUT