diff options
Diffstat (limited to 'shlibs')
60 files changed, 8478 insertions, 332 deletions
diff --git a/shlibs/Makefile.am b/shlibs/Makefile.am deleted file mode 100644 index 3e757885..00000000 --- a/shlibs/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -SUBDIRS = - -if BUILD_LIBUUID -SUBDIRS += uuid -endif - -if BUILD_LIBBLKID -SUBDIRS += blkid -endif diff --git a/shlibs/blkid/Makefile.am b/shlibs/blkid/Makefile.am index 041fa79b..59362cfe 100644 --- a/shlibs/blkid/Makefile.am +++ b/shlibs/blkid/Makefile.am @@ -12,5 +12,5 @@ pkgconfig_DATA = blkid.pc dist_man_MANS = libblkid.3 -EXTRA_DIST = README.blkid blkid.pc.in libblkid.3 +EXTRA_DIST = COPYING.libblkid README.blkid blkid.pc.in libblkid.3 diff --git a/shlibs/blkid/libblkid.3 b/shlibs/blkid/libblkid.3 index 46eb868b..ab4c5736 100644 --- a/shlibs/blkid/libblkid.3 +++ b/shlibs/blkid/libblkid.3 @@ -9,7 +9,7 @@ .SH NAME libblkid \- block device identification library .SH SYNOPSIS -.B #include <blkid/blkid.h> +.B #include <blkid.h> .sp .B cc .I file.c diff --git a/shlibs/blkid/src/Makefile.am b/shlibs/blkid/src/Makefile.am index 2d798b6a..bb395c71 100644 --- a/shlibs/blkid/src/Makefile.am +++ b/shlibs/blkid/src/Makefile.am @@ -24,14 +24,15 @@ nodist_blkidinc_HEADERS = blkid.h usrlib_exec_LTLIBRARIES = libblkid.la libblkid_la_SOURCES = cache.c dev.c devname.c devno.c getsize.c llseek.c \ probe.c read.c resolve.c save.c tag.c version.c verify.c \ - encode.c list.h blkidP.h superblocks/superblocks.h \ + encode.c blkidP.h superblocks/superblocks.h \ config.c evaluate.c fat.h \ $(blkidinc_HEADERS) \ $(top_srcdir)/lib/blkdev.c \ $(top_srcdir)/lib/linux_version.c \ $(top_srcdir)/lib/canonicalize.c \ $(top_srcdir)/lib/md5.c \ - $(top_srcdir)/lib/crc32.c + $(top_srcdir)/lib/crc32.c \ + $(top_srcdir)/include/list.h nodist_libblkid_la_SOURCES = blkid.h @@ -45,7 +46,7 @@ libblkid_la_DEPENDENCIES = $(libblkid_la_LIBADD) blkid.sym blkid.h.in libblkid_la_LDFLAGS = -Wl,--version-script=$(ul_libblkid_srcdir)/blkid.sym \ -version-info $(LIBBLKID_VERSION_INFO) -tests = test_cache test_config test_dev test_devname test_devno test_getsize \ +tests = test_cache test_config test_dev test_devname test_devno \ test_read test_resolve test_save test_tag test_verify test_evaluate EXTRA_DIST = blkid.sym tst_types.c blkid.h.in diff --git a/shlibs/blkid/src/list.h b/shlibs/blkid/src/list.h deleted file mode 100644 index c1cbfec5..00000000 --- a/shlibs/blkid/src/list.h +++ /dev/null @@ -1,179 +0,0 @@ -#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD) -#define _BLKID_LIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __GNUC__ -#define _INLINE_ static __inline__ -#else /* For Watcom C */ -#define _INLINE_ static inline -#endif - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -#define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -} while (0) - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -_INLINE_ void __list_add(struct list_head * add, - struct list_head * prev, - struct list_head * next) -{ - next->prev = add; - add->next = next; - add->prev = prev; - prev->next = add; -} - -/** - * list_add - add a new entry - * @add: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -_INLINE_ void list_add(struct list_head *add, struct list_head *head) -{ - __list_add(add, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @add: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head) -{ - __list_add(add, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -_INLINE_ void __list_del(struct list_head * prev, - struct list_head * next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * - * list_empty() on @entry does not return true after this, @entry is - * in an undefined state. - */ -_INLINE_ void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -_INLINE_ void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -_INLINE_ int list_empty(struct list_head *head) -{ - return head->next == head; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -_INLINE_ void list_splice(struct list_head *list, struct list_head *head) -{ - struct list_head *first = list->next; - - if (first != list) { - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) - -/** - * list_for_each - iterate over elements in a list - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_safe - iterate over elements in a list, but don't dereference - * pos after the body is done (in case it is freed) - * @pos: the &struct list_head to use as a loop counter. - * @pnext: the &struct list_head to use as a pointer to the next item. - * @head: the head for your list (not included in iteration). - */ -#define list_for_each_safe(pos, pnext, head) \ - for (pos = (head)->next, pnext = pos->next; pos != (head); \ - pos = pnext, pnext = pos->next) - -#undef _INLINE_ - -#ifdef __cplusplus -} -#endif - -#endif /* _BLKID_LIST_H */ diff --git a/shlibs/blkid/src/partitions/partitions.c b/shlibs/blkid/src/partitions/partitions.c index c670cc18..5597181a 100644 --- a/shlibs/blkid/src/partitions/partitions.c +++ b/shlibs/blkid/src/partitions/partitions.c @@ -829,6 +829,19 @@ int blkid_probe_is_covered_by_pt(blkid_probe pr, end = (offset + size) >> 9; start = offset >> 9; + /* check if the partition table fits into the device */ + for (i = 0; i < nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (par->start + par->size > pr->size) { + DBG(DEBUG_LOWPROBE, printf("partition #%d overflows " + "device (off=%lu size=%lu)\n", + par->partno, par->start, par->size)); + goto done; + } + } + + /* check if the requested area is covered by PT */ for (i = 0; i < nparts; i++) { blkid_partition par = &ls->parts[i]; diff --git a/shlibs/blkid/src/probe.c b/shlibs/blkid/src/probe.c index 89c59504..200a52c5 100644 --- a/shlibs/blkid/src/probe.c +++ b/shlibs/blkid/src/probe.c @@ -103,6 +103,7 @@ #ifdef HAVE_ERRNO_H #include <errno.h> #endif +#include <inttypes.h> #include <stdint.h> #include <stdarg.h> @@ -542,7 +543,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, static void blkid_probe_reset_buffer(blkid_probe pr) { - ssize_t read_ct = 0, len_ct = 0; + uint64_t read_ct = 0, len_ct = 0; if (!pr || list_empty(&pr->buffers)) return; @@ -560,7 +561,8 @@ static void blkid_probe_reset_buffer(blkid_probe pr) } DBG(DEBUG_LOWPROBE, - printf("buffers summary: %jd bytes by %jd read() call(s)\n", + printf("buffers summary: %"PRIu64" bytes " + "by %"PRIu64" read() call(s)\n", len_ct, read_ct)); INIT_LIST_HEAD(&pr->buffers); @@ -650,9 +652,6 @@ int blkid_probe_set_device(blkid_probe pr, int fd, pr->size -= pr->off; } - DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%jd, size=%jd\n", - pr->off, pr->size)); - if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode)) pr->flags |= BLKID_TINY_DEV; @@ -660,6 +659,13 @@ int blkid_probe_set_device(blkid_probe pr, int fd, if (S_ISBLK(sb.st_mode) && ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) pr->flags |= BLKID_CDROM_DEV; #endif + + DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%jd, size=%jd\n", + pr->off, pr->size)); + DBG(DEBUG_LOWPROBE, printf("whole-disk: %s, regfile: %s\n", + blkid_probe_is_wholedisk(pr) ?"YES" : "NO", + S_ISREG(pr->mode) ? "YES" : "NO")); + return 0; err: DBG(DEBUG_LOWPROBE, diff --git a/shlibs/blkid/src/superblocks/Makefile.am b/shlibs/blkid/src/superblocks/Makefile.am index f5b88b8e..1501fab1 100644 --- a/shlibs/blkid/src/superblocks/Makefile.am +++ b/shlibs/blkid/src/superblocks/Makefile.am @@ -46,4 +46,6 @@ libblkid_superblocks_la_SOURCES = \ bfs.c \ drbd.c \ vmfs.c \ - befs.c + befs.c \ + nilfs.c \ + exfat.c diff --git a/shlibs/blkid/src/superblocks/adaptec_raid.c b/shlibs/blkid/src/superblocks/adaptec_raid.c index 73b146c6..570e75e9 100644 --- a/shlibs/blkid/src/superblocks/adaptec_raid.c +++ b/shlibs/blkid/src/superblocks/adaptec_raid.c @@ -81,6 +81,9 @@ static int probe_adraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; + off = ((pr->size / 0x200)-1) * 0x200; ad = (struct adaptec_metadata *) blkid_probe_get_buffer(pr, diff --git a/shlibs/blkid/src/superblocks/ddf_raid.c b/shlibs/blkid/src/superblocks/ddf_raid.c index a48735d2..c0ba3351 100644 --- a/shlibs/blkid/src/superblocks/ddf_raid.c +++ b/shlibs/blkid/src/superblocks/ddf_raid.c @@ -18,12 +18,57 @@ /* http://www.snia.org/standards/home */ #define DDF_GUID_LENGTH 24 #define DDF_REV_LENGTH 8 +#define DDF_MAGIC 0xDE11DE11 + struct ddf_header { - uint8_t signature[4]; + uint32_t signature; uint32_t crc; uint8_t guid[DDF_GUID_LENGTH]; - uint8_t ddf_rev[DDF_REV_LENGTH]; + char ddf_rev[8]; /* 01.02.00 */ + uint32_t seq; /* starts at '1' */ + uint32_t timestamp; + uint8_t openflag; + uint8_t foreignflag; + uint8_t enforcegroups; + uint8_t pad0; /* 0xff */ + uint8_t pad1[12]; /* 12 * 0xff */ + /* 64 bytes so far */ + uint8_t header_ext[32]; /* reserved: fill with 0xff */ + uint64_t primary_lba; + uint64_t secondary_lba; + uint8_t type; + uint8_t pad2[3]; /* 0xff */ + uint32_t workspace_len; /* sectors for vendor space - + * at least 32768(sectors) */ + uint64_t workspace_lba; + uint16_t max_pd_entries; /* one of 15, 63, 255, 1023, 4095 */ + uint16_t max_vd_entries; /* 2^(4,6,8,10,12)-1 : i.e. as above */ + uint16_t max_partitions; /* i.e. max num of configuration + record entries per disk */ + uint16_t config_record_len; /* 1 +ROUNDUP(max_primary_element_entries + *12/512) */ + uint16_t max_primary_element_entries; /* 16, 64, 256, 1024, or 4096 */ + uint8_t pad3[54]; /* 0xff */ + /* 192 bytes so far */ + uint32_t controller_section_offset; + uint32_t controller_section_length; + uint32_t phys_section_offset; + uint32_t phys_section_length; + uint32_t virt_section_offset; + uint32_t virt_section_length; + uint32_t config_section_offset; + uint32_t config_section_length; + uint32_t data_section_offset; + uint32_t data_section_length; + uint32_t bbm_section_offset; + uint32_t bbm_section_length; + uint32_t diag_space_offset; + uint32_t diag_space_length; + uint32_t vendor_offset; + uint32_t vendor_length; + /* 256 bytes so far */ + uint8_t pad4[256]; /* 0xff */ } __attribute__((packed)); static int probe_ddf(blkid_probe pr, const struct blkid_idmag *mag) @@ -32,7 +77,7 @@ static int probe_ddf(blkid_probe pr, const struct blkid_idmag *mag) int i; struct ddf_header *ddf = NULL; char version[DDF_REV_LENGTH + 1]; - uint64_t off; + uint64_t off, lba; if (pr->size < 0x30000) return -1; @@ -46,8 +91,8 @@ static int probe_ddf(blkid_probe pr, const struct blkid_idmag *mag) if (!ddf) return -1; - if (memcmp(ddf->signature, "\x11\xde\x11\xde", 4) == 0 || - memcmp(ddf->signature, "\xde\x11\xde\x11", 4) == 0) + if (ddf->signature == cpu_to_be32(DDF_MAGIC) || + ddf->signature == cpu_to_le32(DDF_MAGIC)) break; ddf = NULL; } @@ -55,6 +100,20 @@ static int probe_ddf(blkid_probe pr, const struct blkid_idmag *mag) if (!ddf) return -1; + lba = ddf->signature == cpu_to_be32(DDF_MAGIC) ? + be64_to_cpu(ddf->primary_lba) : + le64_to_cpu(ddf->primary_lba); + + if (lba > 0) { + /* check primary header */ + unsigned char *buf; + + buf = blkid_probe_get_buffer(pr, + lba << 9, sizeof(ddf->signature)); + if (!buf || memcmp(buf, &ddf->signature, 4)) + return -1; + } + blkid_probe_strncpy_uuid(pr, ddf->guid, sizeof(ddf->guid)); memcpy(version, ddf->ddf_rev, sizeof(ddf->ddf_rev)); @@ -64,7 +123,7 @@ static int probe_ddf(blkid_probe pr, const struct blkid_idmag *mag) return -1; if (blkid_probe_set_magic(pr, off, sizeof(ddf->signature), - (unsigned char *) ddf->signature)) + (unsigned char *) &ddf->signature)) return -1; return 0; } diff --git a/shlibs/blkid/src/superblocks/exfat.c b/shlibs/blkid/src/superblocks/exfat.c new file mode 100644 index 00000000..bada3a83 --- /dev/null +++ b/shlibs/blkid/src/superblocks/exfat.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include "superblocks.h" + +struct exfat_super_block { + uint8_t jump[3]; + uint8_t oem_name[8]; + uint8_t __unused1[53]; + uint64_t block_start; + uint64_t block_count; + uint32_t fat_block_start; + uint32_t fat_block_count; + uint32_t cluster_block_start; + uint32_t cluster_count; + uint32_t rootdir_cluster; + uint8_t volume_serial[4]; + struct { + uint8_t minor; + uint8_t major; + } version; + uint16_t volume_state; + uint8_t block_bits; + uint8_t bpc_bits; + uint8_t fat_count; + uint8_t drive_no; + uint8_t allocated_percent; +} __attribute__((__packed__)); + +struct exfat_entry_label { + uint8_t type; + uint8_t length; + uint8_t name[30]; +} __attribute__((__packed__)); + +#define BLOCK_SIZE(sb) (1 << (sb)->block_bits) +#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits) +#define EXFAT_FIRST_DATA_CLUSTER 2 +#define EXFAT_LAST_DATA_CLUSTER 0xffffff6 +#define EXFAT_ENTRY_SIZE 32 + +#define EXFAT_ENTRY_EOD 0x00 +#define EXFAT_ENTRY_LABEL 0x83 + +static blkid_loff_t block_to_offset(const struct exfat_super_block *sb, + blkid_loff_t block) +{ + return (blkid_loff_t) block << sb->block_bits; +} + +static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb, + uint32_t cluster) +{ + return le32_to_cpu(sb->cluster_block_start) + + ((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) + << sb->bpc_bits); +} + +static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb, + uint32_t cluster) +{ + return block_to_offset(sb, cluster_to_block(sb, cluster)); +} + +static uint32_t next_cluster(blkid_probe pr, + const struct exfat_super_block *sb, uint32_t cluster) +{ + uint32_t *next; + blkid_loff_t fat_offset; + + fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start)) + + (blkid_loff_t) cluster * sizeof(cluster); + next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset, + sizeof(uint32_t)); + if (!next) + return 0; + return le32_to_cpu(*next); +} + +static struct exfat_entry_label *find_label(blkid_probe pr, + const struct exfat_super_block *sb) +{ + uint32_t cluster = le32_to_cpu(sb->rootdir_cluster); + blkid_loff_t offset = cluster_to_offset(sb, cluster); + uint8_t *entry; + + for (;;) { + entry = (uint8_t *) blkid_probe_get_buffer(pr, offset, + EXFAT_ENTRY_SIZE); + if (!entry) + return NULL; + if (entry[0] == EXFAT_ENTRY_EOD) + return NULL; + if (entry[0] == EXFAT_ENTRY_LABEL) + return (struct exfat_entry_label *) entry; + offset += EXFAT_ENTRY_SIZE; + if (offset % CLUSTER_SIZE(sb) == 0) { + cluster = next_cluster(pr, sb, cluster); + if (cluster < EXFAT_FIRST_DATA_CLUSTER) + return NULL; + if (cluster > EXFAT_LAST_DATA_CLUSTER) + return NULL; + offset = cluster_to_offset(sb, cluster); + } + } +} + +static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct exfat_super_block *sb; + struct exfat_entry_label *label; + + sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); + if (!sb) + return -1; + + label = find_label(pr, sb); + if (label) + blkid_probe_set_utf8label(pr, label->name, + min(label->length * 2, 30), BLKID_ENC_UTF16LE); + + blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4, + "%02hhX%02hhX-%02hhX%02hhX", + sb->volume_serial[3], sb->volume_serial[2], + sb->volume_serial[1], sb->volume_serial[0]); + + blkid_probe_sprintf_version(pr, "%hu.%hu", + sb->version.major, sb->version.minor); + + return 0; +} + +const struct blkid_idinfo exfat_idinfo = +{ + .name = "exfat", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_exfat, + .magics = + { + { .magic = "EXFAT ", .len = 8, .sboff = 3 }, + { NULL } + } +}; diff --git a/shlibs/blkid/src/superblocks/hfs.c b/shlibs/blkid/src/superblocks/hfs.c index 8ebff68d..033a65de 100644 --- a/shlibs/blkid/src/superblocks/hfs.c +++ b/shlibs/blkid/src/superblocks/hfs.c @@ -291,6 +291,7 @@ const struct blkid_idinfo hfs_idinfo = .name = "hfs", .usage = BLKID_USAGE_FILESYSTEM, .probefunc = probe_hfs, + .flags = BLKID_IDINFO_TOLERANT, .magics = { { .magic = "BD", .len = 2, .kboff = 1 }, diff --git a/shlibs/blkid/src/superblocks/highpoint_raid.c b/shlibs/blkid/src/superblocks/highpoint_raid.c index 98343c41..25e3114b 100644 --- a/shlibs/blkid/src/superblocks/highpoint_raid.c +++ b/shlibs/blkid/src/superblocks/highpoint_raid.c @@ -30,6 +30,8 @@ static int probe_highpoint45x(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200) - 11) * 0x200; hpt = (struct hpt45x_metadata *) @@ -47,6 +49,14 @@ static int probe_highpoint45x(blkid_probe pr, const struct blkid_idmag *mag) return 0; } +static int probe_highpoint37x(blkid_probe pr, const struct blkid_idmag *mag) +{ + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; + return 0; +} + + const struct blkid_idinfo highpoint45x_idinfo = { .name = "hpt45x_raid_member", .usage = BLKID_USAGE_RAID, @@ -57,6 +67,7 @@ const struct blkid_idinfo highpoint45x_idinfo = { const struct blkid_idinfo highpoint37x_idinfo = { .name = "hpt37x_raid_member", .usage = BLKID_USAGE_RAID, + .probefunc = probe_highpoint37x, .magics = { /* * Superblok offset: 4608 bytes (9 sectors) diff --git a/shlibs/blkid/src/superblocks/isw_raid.c b/shlibs/blkid/src/superblocks/isw_raid.c index 5149c38a..ac6251d7 100644 --- a/shlibs/blkid/src/superblocks/isw_raid.c +++ b/shlibs/blkid/src/superblocks/isw_raid.c @@ -33,6 +33,8 @@ static int probe_iswraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200) - 2) * 0x200; isw = (struct isw_metadata *) diff --git a/shlibs/blkid/src/superblocks/jmicron_raid.c b/shlibs/blkid/src/superblocks/jmicron_raid.c index 24430bf9..d35b17f8 100644 --- a/shlibs/blkid/src/superblocks/jmicron_raid.c +++ b/shlibs/blkid/src/superblocks/jmicron_raid.c @@ -32,6 +32,8 @@ static int probe_jmraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200) - 1) * 0x200; jm = (struct jm_metadata *) diff --git a/shlibs/blkid/src/superblocks/linux_raid.c b/shlibs/blkid/src/superblocks/linux_raid.c index 6f823f15..b73214a4 100644 --- a/shlibs/blkid/src/superblocks/linux_raid.c +++ b/shlibs/blkid/src/superblocks/linux_raid.c @@ -34,27 +34,82 @@ struct mdp0_super_block { uint32_t set_uuid3; }; +/* + * Version-1, little-endian. + */ struct mdp1_super_block { - uint32_t magic; - uint32_t major_version; - uint32_t feature_map; - uint32_t pad0; - uint8_t set_uuid[16]; - uint8_t set_name[32]; + /* constant array information - 128 bytes */ + uint32_t magic; /* MD_SB_MAGIC: 0xa92b4efc - little endian */ + uint32_t major_version; /* 1 */ + uint32_t feature_map; /* 0 for now */ + uint32_t pad0; /* always set to 0 when writing */ + + uint8_t set_uuid[16]; /* user-space generated. */ + unsigned char set_name[32]; /* set and interpreted by user-space */ + + uint64_t ctime; /* lo 40 bits are seconds, top 24 are microseconds or 0*/ + uint32_t level; /* -4 (multipath), -1 (linear), 0,1,4,5 */ + uint32_t layout; /* only for raid5 currently */ + uint64_t size; /* used size of component devices, in 512byte sectors */ + + uint32_t chunksize; /* in 512byte sectors */ + uint32_t raid_disks; + uint32_t bitmap_offset; /* sectors after start of superblock that bitmap starts + * NOTE: signed, so bitmap can be before superblock + * only meaningful of feature_map[0] is set. + */ + + /* These are only valid with feature bit '4' */ + uint32_t new_level; /* new level we are reshaping to */ + uint64_t reshape_position; /* next address in array-space for reshape */ + uint32_t delta_disks; /* change in number of raid_disks */ + uint32_t new_layout; /* new layout */ + uint32_t new_chunk; /* new chunk size (bytes) */ + uint8_t pad1[128-124]; /* set to 0 when written */ + + /* constant this-device information - 64 bytes */ + uint64_t data_offset; /* sector start of data, often 0 */ + uint64_t data_size; /* sectors in this device that can be used for data */ + uint64_t super_offset; /* sector start of this superblock */ + uint64_t recovery_offset;/* sectors before this offset (from data_offset) have been recovered */ + uint32_t dev_number; /* permanent identifier of this device - not role in raid */ + uint32_t cnt_corrected_read; /* number of read errors that were corrected by re-writing */ + uint8_t device_uuid[16]; /* user-space setable, ignored by kernel */ + uint8_t devflags; /* per-device flags. Only one defined...*/ + uint8_t pad2[64-57]; /* set to 0 when writing */ + + /* array state information - 64 bytes */ + uint64_t utime; /* 40 bits second, 24 btes microseconds */ + uint64_t events; /* incremented when superblock updated */ + uint64_t resync_offset; /* data before this offset (from data_offset) known to be in sync */ + uint32_t sb_csum; /* checksum upto dev_roles[max_dev] */ + uint32_t max_dev; /* size of dev_roles[] array to consider */ + uint8_t pad3[64-32]; /* set to 0 when writing */ + + /* device state information. Indexed by dev_number. + * 2 bytes per device + * Note there are no per-device state flags. State information is rolled + * into the 'roles' value. If a device is spare or faulty, then it doesn't + * have a meaningful role. + */ + uint16_t dev_roles[0]; /* role in array, or 0xffff for a spare, or 0xfffe for faulty */ }; + #define MD_RESERVED_BYTES 0x10000 #define MD_SB_MAGIC 0xa92b4efc -static int probe_raid0(blkid_probe pr, off_t off) +static int probe_raid0(blkid_probe pr, blkid_loff_t off) { struct mdp0_super_block *mdp0; union { uint32_t ints[4]; uint8_t bytes[16]; } uuid; + uint32_t ma, mi, pa; + uint64_t size; - if (pr->size < 0x10000) + if (pr->size < MD_RESERVED_BYTES) return -1; mdp0 = (struct mdp0_super_block *) blkid_probe_get_buffer(pr, @@ -72,11 +127,10 @@ static int probe_raid0(blkid_probe pr, off_t off) uuid.ints[2] = swab32(mdp0->set_uuid2); uuid.ints[3] = swab32(mdp0->set_uuid3); } - if (blkid_probe_sprintf_version(pr, "%u.%u.%u", - le32_to_cpu(mdp0->major_version), - le32_to_cpu(mdp0->minor_version), - le32_to_cpu(mdp0->patch_version)) != 0) - return -1; + ma = le32_to_cpu(mdp0->major_version); + mi = le32_to_cpu(mdp0->minor_version); + pa = le32_to_cpu(mdp0->patch_version); + size = le32_to_cpu(mdp0->size); } else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) { uuid.ints[0] = mdp0->set_uuid0; @@ -85,14 +139,41 @@ static int probe_raid0(blkid_probe pr, off_t off) uuid.ints[2] = mdp0->set_uuid2; uuid.ints[3] = mdp0->set_uuid3; } - if (blkid_probe_sprintf_version(pr, "%u.%u.%u", - be32_to_cpu(mdp0->major_version), - be32_to_cpu(mdp0->minor_version), - be32_to_cpu(mdp0->patch_version)) != 0) - return -1; + ma = be32_to_cpu(mdp0->major_version); + mi = be32_to_cpu(mdp0->minor_version); + pa = be32_to_cpu(mdp0->patch_version); + size = be32_to_cpu(mdp0->size); } else - return -1; + return 1; + + size <<= 10; /* convert KiB to bytes */ + + if (pr->size < size + MD_RESERVED_BYTES) + /* device is too small */ + return 1; + + if (off < size) + /* no space before superblock */ + return 1; + + /* + * Check for collisions between RAID and partition table + * + * For example the superblock is at the end of the last partition, it's + * the same possition as at the end of the disk... + */ + if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && + blkid_probe_is_covered_by_pt(pr, + off - size, /* min. start */ + size + MD_RESERVED_BYTES)) { /* min. length */ + /* ignore this superblock, it's within any partition and + * we are working with whole-disk now */ + return 1; + } + + if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0) + return -1; if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0) return -1; if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic), @@ -115,8 +196,13 @@ static int probe_raid1(blkid_probe pr, off_t off) return -1; if (le32_to_cpu(mdp1->major_version) != 1) return -1; + if (le64_to_cpu(mdp1->super_offset) != off >> 9) + return -1; if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0) return -1; + if (blkid_probe_set_uuid_as(pr, + (unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0) + return -1; if (blkid_probe_set_label(pr, mdp1->set_name, sizeof(mdp1->set_name)) != 0) return -1; diff --git a/shlibs/blkid/src/superblocks/lsi_raid.c b/shlibs/blkid/src/superblocks/lsi_raid.c index 3010eb6b..5217a009 100644 --- a/shlibs/blkid/src/superblocks/lsi_raid.c +++ b/shlibs/blkid/src/superblocks/lsi_raid.c @@ -30,6 +30,8 @@ static int probe_lsiraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200) - 1) * 0x200; lsi = (struct lsi_metadata *) diff --git a/shlibs/blkid/src/superblocks/nilfs.c b/shlibs/blkid/src/superblocks/nilfs.c new file mode 100644 index 00000000..bf169182 --- /dev/null +++ b/shlibs/blkid/src/superblocks/nilfs.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 by Jiro SEKIBA <jir@unicus.jp> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License + */ +#include <stddef.h> +#include <string.h> + +#include "superblocks.h" +#include "crc32.h" + +struct nilfs_super_block { + uint32_t s_rev_level; + uint16_t s_minor_rev_level; + uint16_t s_magic; + + uint16_t s_bytes; + + uint16_t s_flags; + uint32_t s_crc_seed; + uint32_t s_sum; + + uint32_t s_log_block_size; + + uint64_t s_nsegments; + uint64_t s_dev_size; + uint64_t s_first_data_block; + uint32_t s_blocks_per_segment; + uint32_t s_r_segments_percentage; + + uint64_t s_last_cno; + uint64_t s_last_pseg; + uint64_t s_last_seq; + uint64_t s_free_blocks_count; + + uint64_t s_ctime; + + uint64_t s_mtime; + uint64_t s_wtime; + uint16_t s_mnt_count; + uint16_t s_max_mnt_count; + uint16_t s_state; + uint16_t s_errors; + uint64_t s_lastcheck; + + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + uint32_t s_first_ino; + + uint16_t s_inode_size; + uint16_t s_dat_entry_size; + uint16_t s_checkpoint_size; + uint16_t s_segment_usage_size; + + uint8_t s_uuid[16]; + char s_volume_name[80]; + + uint32_t s_c_interval; + uint32_t s_c_block_max; + uint32_t s_reserved[192]; +}; + +/* nilfs2 magic string */ +#define NILFS_SB_MAGIC "\x34\x34" +/* nilfs2 super block offset */ +#define NILFS_SB_OFF 0x400 +/* nilfs2 super block offset in kB */ +#define NILFS_SB_KBOFF (NILFS_SB_OFF >> 10) +/* nilfs2 magic string offset within super block */ +#define NILFS_MAG_OFF 6 + +static int probe_nilfs2(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct nilfs_super_block *sb; + static unsigned char sum[4]; + const int sumoff = offsetof(struct nilfs_super_block, s_sum); + size_t bytes; + uint32_t crc; + + sb = blkid_probe_get_sb(pr, mag, struct nilfs_super_block); + if (!sb) + return -1; + + bytes = le32_to_cpu(sb->s_bytes); + crc = crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff); + crc = crc32(crc, sum, 4); + crc = crc32(crc, (unsigned char *)sb + sumoff + 4, bytes - sumoff - 4); + + if (crc != le32_to_cpu(sb->s_sum)) + return -1; + + if (strlen(sb->s_volume_name)) + blkid_probe_set_label(pr, (unsigned char *) sb->s_volume_name, + sizeof(sb->s_volume_name)); + + blkid_probe_set_uuid(pr, sb->s_uuid); + blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(sb->s_rev_level)); + + return 0; +} + +const struct blkid_idinfo nilfs2_idinfo = +{ + .name = "nilfs2", + .usage = BLKID_USAGE_FILESYSTEM, + .probefunc = probe_nilfs2, + .magics = + { + { + .magic = NILFS_SB_MAGIC, + .len = 2, + .kboff = NILFS_SB_KBOFF, + .sboff = NILFS_MAG_OFF + }, + { NULL } + } +}; diff --git a/shlibs/blkid/src/superblocks/nvidia_raid.c b/shlibs/blkid/src/superblocks/nvidia_raid.c index e75bec84..c3db949a 100644 --- a/shlibs/blkid/src/superblocks/nvidia_raid.c +++ b/shlibs/blkid/src/superblocks/nvidia_raid.c @@ -32,6 +32,8 @@ static int probe_nvraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200) - 2) * 0x200; nv = (struct nv_metadata *) diff --git a/shlibs/blkid/src/superblocks/promise_raid.c b/shlibs/blkid/src/superblocks/promise_raid.c index 1cc70e65..0e91d3c2 100644 --- a/shlibs/blkid/src/superblocks/promise_raid.c +++ b/shlibs/blkid/src/superblocks/promise_raid.c @@ -33,6 +33,8 @@ static int probe_pdcraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x40000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; for (i = 0; sectors[i] != 0; i++) { uint64_t off; diff --git a/shlibs/blkid/src/superblocks/silicon_raid.c b/shlibs/blkid/src/superblocks/silicon_raid.c index 11277ad6..b72b7276 100644 --- a/shlibs/blkid/src/superblocks/silicon_raid.c +++ b/shlibs/blkid/src/superblocks/silicon_raid.c @@ -48,6 +48,8 @@ static int probe_silraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200) - 1) * 0x200; diff --git a/shlibs/blkid/src/superblocks/superblocks.c b/shlibs/blkid/src/superblocks/superblocks.c index 1d952737..3d66d98c 100644 --- a/shlibs/blkid/src/superblocks/superblocks.c +++ b/shlibs/blkid/src/superblocks/superblocks.c @@ -71,7 +71,6 @@ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn); static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn); -static void superblocks_free(blkid_probe pr, void *data); static int blkid_probe_set_usage(blkid_probe pr, int usage); @@ -140,7 +139,9 @@ static const struct blkid_idinfo *idinfos[] = &ubifs_idinfo, &bfs_idinfo, &vmfs_fs_idinfo, - &befs_idinfo + &befs_idinfo, + &nilfs2_idinfo, + &exfat_idinfo }; /* @@ -156,23 +157,8 @@ const struct blkid_chaindrv superblocks_drv = { .has_fltr = TRUE, .probe = superblocks_probe, .safeprobe = superblocks_safeprobe, - .free_data = superblocks_free }; -/* - * Private chain data - * - * TODO: export this data by binary interface (see topology.c or partitions.c - * for more details) by blkid_probe_get_superblock() or so. - */ -struct blkid_struct_superblock { - blkid_loff_t magic_off; /* offset of the magic string */ - int usage; -}; - -/* TODO: move to blkid.h */ -typedef struct blkid_struct_superblock *blkid_superblock; - /** * blkid_probe_enable_superblocks: * @pr: probe @@ -311,39 +297,6 @@ int blkid_known_fstype(const char *fstype) return 0; } -/* init and returns private data */ -static blkid_superblock superblocks_init_data(blkid_probe pr, - struct blkid_chain *chn) -{ - DBG(DEBUG_LOWPROBE, printf("initialize superblocks binary data\n")); - - if (chn->data) - memset(chn->data, 0, - sizeof(struct blkid_struct_superblock)); - else { - chn->data = calloc(1, - sizeof(struct blkid_struct_superblock)); - if (!chn->data) - return NULL; - } - return chn->data; -} - -static void superblocks_free(blkid_probe pr, void *data) -{ - free(data); -} - -static blkid_superblock superblocks_copy_data(blkid_superblock dest, - blkid_superblock src) -{ - if (!src || !dest) - return NULL; - - memcpy(dest, src, sizeof(struct blkid_struct_superblock)); - return dest; -} - /* * The blkid_do_probe() backend. */ @@ -365,9 +318,6 @@ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn) * is 1 byte */ goto nothing; - if (chn->binary) - superblocks_init_data(pr, chn); - i = chn->idx + 1; for ( ; i < ARRAY_SIZE(idinfos); i++) { @@ -472,44 +422,37 @@ nothing: */ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) { - blkid_superblock sb = NULL; - struct blkid_struct_superblock sb_buff; - struct blkid_prval vals[BLKID_NVALS_SUBLKS]; int nvals = BLKID_NVALS_SUBLKS; int idx = -1; int count = 0; int intol = 0; - int rc, bin_org = chn->binary; - - chn->binary = TRUE; + int rc; while ((rc = superblocks_probe(pr, chn)) == 0) { - if (blkid_probe_is_tiny(pr) && !count) { + if (blkid_probe_is_tiny(pr) && !count) /* floppy or so -- returns the first result. */ - chn->binary = bin_org; return 0; - } - if (!count) { - /* save the first result */ - nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals); - idx = chn->idx; - if (chn->data) - sb = superblocks_copy_data(&sb_buff, chn->data); - } + count++; if (idinfos[chn->idx]->usage & (BLKID_USAGE_RAID | BLKID_USAGE_CRYPTO)) break; + if (!(idinfos[chn->idx]->flags & BLKID_IDINFO_TOLERANT)) intol++; - } - chn->binary = bin_org; + if (count == 1) { + /* save the first result */ + nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals); + idx = chn->idx; + } + } if (rc < 0) return rc; /* error */ + if (count > 1 && intol) { DBG(DEBUG_LOWPROBE, printf("ERROR: superblocks chain: " @@ -520,27 +463,11 @@ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) if (!count) return 1; /* nothing detected */ - /* restore the first result */ - blkid_probe_chain_reset_vals(pr, chn); - blkid_probe_append_vals(pr, vals, nvals); - if (sb && chn->data) - superblocks_copy_data(chn->data, sb); - chn->idx = idx; - - /* - * Check for collisions between RAID and partition table - */ - if (sb && sb->usage == BLKID_USAGE_RAID && - sb->magic_off > pr->size / 2 && - (S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && - blkid_probe_is_covered_by_pt(pr, sb->magic_off, 0x200)) { - /* - * Ignore the result if the detected RAID superblock is - * within some existing partition (for example RAID on - * the last partition). - */ + if (idx != -1) { + /* restore the first result */ blkid_probe_chain_reset_vals(pr, chn); - return 1; + blkid_probe_append_vals(pr, vals, nvals); + chn->idx = idx; } /* @@ -548,7 +475,7 @@ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) * where the partition table is visible from underlaying devices. We * have to ignore such partition tables. */ - if (sb && sb->usage == BLKID_USAGE_RAID) + if (chn->idx >= 0 && idinfos[chn->idx]->usage & BLKID_USAGE_RAID) pr->prob_flags |= BLKID_PARTS_IGNORE_PT; return 0; @@ -567,10 +494,6 @@ int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset, rc = blkid_probe_sprintf_value(pr, "SBMAGIC_OFFSET", "%llu", offset); } - if (!rc && chn->data) { - blkid_superblock sb = (blkid_superblock) chn->data; - sb->magic_off = offset; - } return rc; } @@ -604,11 +527,6 @@ static int blkid_probe_set_usage(blkid_probe pr, int usage) struct blkid_chain *chn = blkid_probe_get_chain(pr); char *u = NULL; - if (chn->data) { - blkid_superblock sb = (blkid_superblock) chn->data; - sb->usage = usage; - } - if (!(chn->flags & BLKID_SUBLKS_USAGE)) return 0; diff --git a/shlibs/blkid/src/superblocks/superblocks.h b/shlibs/blkid/src/superblocks/superblocks.h index b1fa49d5..a79d7cb6 100644 --- a/shlibs/blkid/src/superblocks/superblocks.h +++ b/shlibs/blkid/src/superblocks/superblocks.h @@ -65,6 +65,8 @@ extern const struct blkid_idinfo vmfs_volume_idinfo; extern const struct blkid_idinfo vmfs_fs_idinfo; extern const struct blkid_idinfo drbd_idinfo; extern const struct blkid_idinfo befs_idinfo; +extern const struct blkid_idinfo nilfs2_idinfo; +extern const struct blkid_idinfo exfat_idinfo; /* * superblock functions diff --git a/shlibs/blkid/src/superblocks/udf.c b/shlibs/blkid/src/superblocks/udf.c index 55c96adb..78ddda68 100644 --- a/shlibs/blkid/src/superblocks/udf.c +++ b/shlibs/blkid/src/superblocks/udf.c @@ -136,6 +136,9 @@ anchor: blkid_probe_set_utf8label(pr, vd->type.primary.ident.c, 31, BLKID_ENC_UTF16BE); + + if (clen == 8 || clen == 16) + break; } } diff --git a/shlibs/blkid/src/superblocks/via_raid.c b/shlibs/blkid/src/superblocks/via_raid.c index 58650454..20131380 100644 --- a/shlibs/blkid/src/superblocks/via_raid.c +++ b/shlibs/blkid/src/superblocks/via_raid.c @@ -52,6 +52,8 @@ static int probe_viaraid(blkid_probe pr, const struct blkid_idmag *mag) if (pr->size < 0x10000) return -1; + if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) + return -1; off = ((pr->size / 0x200)-1) * 0x200; diff --git a/shlibs/blkid/src/topology/evms.c b/shlibs/blkid/src/topology/evms.c index baab16ed..574d9f55 100644 --- a/shlibs/blkid/src/topology/evms.c +++ b/shlibs/blkid/src/topology/evms.c @@ -23,6 +23,11 @@ #include "topology.h" #define EVMS_MAJOR 117 + +#ifndef _IOT__IOTBASE_u_int32_t +#define _IOT__IOTBASE_u_int32_t IOT_SIMPLE(u_int32_t) +#endif +#define _IOT_evms_stripe_info _IOT (_IOTS(u_int32_t), 2, 0, 0, 0, 0) #define EVMS_GET_STRIPE_INFO _IOR(EVMS_MAJOR, 0xF0, struct evms_stripe_info) struct evms_stripe_info { diff --git a/shlibs/blkid/src/topology/md.c b/shlibs/blkid/src/topology/md.c index a761e170..d7275edd 100644 --- a/shlibs/blkid/src/topology/md.c +++ b/shlibs/blkid/src/topology/md.c @@ -26,6 +26,10 @@ #define MD_MAJOR 9 #endif +#ifndef _IOT__IOTBASE_uint32_t +#define _IOT__IOTBASE_uint32_t IOT_SIMPLE(uint32_t) +#endif +#define _IOT_md_array_info _IOT (_IOTS(uint32_t), 18, 0, 0, 0, 0) #define GET_ARRAY_INFO _IOR (MD_MAJOR, 0x11, struct md_array_info) struct md_array_info { diff --git a/shlibs/mount/.gitignore b/shlibs/mount/.gitignore new file mode 100644 index 00000000..1c7879de --- /dev/null +++ b/shlibs/mount/.gitignore @@ -0,0 +1,2 @@ +test_* +mount.h diff --git a/shlibs/mount/COPYING.libmount b/shlibs/mount/COPYING.libmount new file mode 100644 index 00000000..89d4489c --- /dev/null +++ b/shlibs/mount/COPYING.libmount @@ -0,0 +1,508 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/shlibs/mount/Makefile.am b/shlibs/mount/Makefile.am new file mode 100644 index 00000000..3fefce6e --- /dev/null +++ b/shlibs/mount/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/config/include-Makefile.am + +SUBDIRS = src + +if ENABLE_GTK_DOC +SUBDIRS += docs +endif + +# pkg-config stuff +pkgconfigdir = $(usrlib_execdir)/pkgconfig +pkgconfig_DATA = mount.pc + +EXTRA_DIST = COPYING.libmount mount.pc.in diff --git a/shlibs/mount/docs/.gitignore b/shlibs/mount/docs/.gitignore new file mode 100644 index 00000000..917c8481 --- /dev/null +++ b/shlibs/mount/docs/.gitignore @@ -0,0 +1,17 @@ +*.args +*.bak +*-decl-list.txt +*-decl.txt +*.hierarchy +html/* +*.interfaces +*-overrides.txt +*.prerequisites +*.signals +*.stamp +tmpl/* +*-undeclared.txt +*-undocumented.txt +*-unused.txt +version.xml +xml/* diff --git a/shlibs/mount/docs/Makefile.am b/shlibs/mount/docs/Makefile.am new file mode 100644 index 00000000..13e1c655 --- /dev/null +++ b/shlibs/mount/docs/Makefile.am @@ -0,0 +1,101 @@ +include $(top_srcdir)/config/include-Makefile.am + +## Process this file with automake to produce Makefile.in + +# We require automake 1.10 at least. +AUTOMAKE_OPTIONS = 1.10 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=libmount + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +#DOC_MODULE_VERSION=2 + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../src + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space mount + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(ul_libmount_incdir)/mount.h +CFILE_GLOB=$(ul_libmount_srcdir)/*.c + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES=mountP.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files = $(builddir)/version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS= +GTKDOC_LIBS= + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/config/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) +#TESTS = $(GTKDOC_CHECK) +endif diff --git a/shlibs/mount/docs/libmount-docs.xml b/shlibs/mount/docs/libmount-docs.xml new file mode 100644 index 00000000..c1371d61 --- /dev/null +++ b/shlibs/mount/docs/libmount-docs.xml @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" +[ + <!ENTITY version SYSTEM "version.xml"> +]> +<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> + <bookinfo> + <title>libmount Reference Manual</title> + <releaseinfo>for libmount version &version;</releaseinfo> + <copyright> + <year>2010</year> + <holder>Karel Zak <kzak@redhat.com></holder> + </copyright> + </bookinfo> + + <part id="gtk"> + <title>libmount Overview</title> + <partintro> + <para> +The libmount library is used to parse /etc/fstab, /etc/mtab and +/proc/self/mountinfo files, manage the mtab file, evaluate mount options, etc. + </para> + <para> +The library is part of the util-linux-ng package since version 2.18 and is +available from ftp://ftp.kernel.org/pub/linux/utils/util-linux-ng/. + </para> + </partintro> + </part> + + <part> + <title>Files parsing</title> + <xi:include href="xml/tab.xml"/> + <xi:include href="xml/fs.xml"/> + </part> + <part> + <title>Mount options</title> + <xi:include href="xml/optstr.xml"/> + <xi:include href="xml/optls.xml"/> + <xi:include href="xml/optent.xml"/> + <xi:include href="xml/optmap.xml"/> + </part> + <part> + <title>Mtab management</title> + <xi:include href="xml/lock.xml"/> + </part> + <part> + <title>Misc</title> + <xi:include href="xml/init.xml"/> + <xi:include href="xml/cache.xml"/> + <xi:include href="xml/iter.xml"/> + <xi:include href="xml/utils.xml"/> + <xi:include href="xml/version.xml"/> + </part> + + <index id="api-index-full"> + <title>API Index</title> + <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> + </index> +</book> diff --git a/shlibs/mount/docs/libmount-sections.txt b/shlibs/mount/docs/libmount-sections.txt new file mode 100644 index 00000000..59cdf47d --- /dev/null +++ b/shlibs/mount/docs/libmount-sections.txt @@ -0,0 +1,181 @@ +<SECTION> +<FILE>init</FILE> +mnt_init_debug +</SECTION> + +<SECTION> +<FILE>version</FILE> +mnt_parse_version_string +mnt_get_library_version +</SECTION> + +<SECTION> +<FILE>utils</FILE> +mnt_fstype_is_netfs +mnt_fstype_is_pseudofs +mnt_match_fstype +mnt_match_options +</SECTION> + +<SECTION> +<FILE>cache</FILE> +mnt_cache +mnt_new_cache +mnt_free_cache +mnt_cache_find_path +mnt_cache_find_tag +mnt_cache_read_tags +mnt_cache_device_has_tag +mnt_cache_find_tag_value +mnt_resolve_path +mnt_resolve_tag +mnt_resolve_spec +</SECTION> + +<SECTION> +<FILE>optstr</FILE> +mnt_optstr_next_option +mnt_optstr_append_option +mnt_optstr_get_option +mnt_optstr_set_option +mnt_optstr_remove_option +</SECTION> + +<SECTION> +<FILE>iter</FILE> +mnt_iter +mnt_new_iter +mnt_free_iter +mnt_reset_iter +mnt_iter_get_direction +</SECTION> + +<SECTION> +<FILE>optmap</FILE> +mnt_optmap +mnt_get_builtin_optmap +</SECTION> + +<SECTION> +<FILE>optent</FILE> +mnt_optent +mnt_optent_get_map +mnt_optent_get_mapent +mnt_optent_get_type +mnt_optent_set_value +mnt_optent_has_value +mnt_optent_require_value +mnt_optent_is_inverted +mnt_optent_strtoul_value +mnt_optent_strtol_value +mnt_optent_strtoull_value +mnt_optent_get_value +mnt_optent_strlen_value +mnt_optent_snprintf_value +mnt_optent_dup_value +mnt_optent_get_name +mnt_optent_get_mask +mnt_optent_get_id +mnt_optent_get_flag +mnt_optent_is_unknown +mnt_optent_print_debug +</SECTION> + +<SECTION> +<FILE>optls</FILE> +mnt_optls +mnt_new_optls +mnt_free_optls +mnt_optls_add_map +mnt_optls_add_builtin_map +mnt_optls_add_option +mnt_optls_parse_optstr +mnt_optls_remove_option +mnt_optls_remove_option_by_flags +mnt_optls_remove_option_by_iflags +mnt_optls_next_option +mnt_optls_get_option +mnt_optls_get_ids +mnt_optls_create_mountflags +mnt_optls_create_mountdata +mnt_optls_create_mtab_optstr +mnt_optls_create_userspace_optstr +mnt_optls_print_debug +</SECTION> + + +<SECTION> +<FILE>lock</FILE> +mnt_lock +mnt_new_lock +mnt_free_lock +mnt_lock_get_lockfile +mnt_lock_get_linkfile +mnt_unlock_file +mnt_lock_file +</SECTION> + + +<SECTION> +<FILE>fs</FILE> +mnt_fs +mnt_new_fs +mnt_free_fs +mnt_fs_get_userdata +mnt_fs_set_userdata +mnt_fs_get_source +mnt_fs_set_source +mnt_fs_get_srcpath +mnt_fs_get_tag +mnt_fs_get_target +mnt_fs_set_target +mnt_fs_get_fstype +mnt_fs_set_fstype +mnt_fs_get_optstr +mnt_fs_set_optstr +mnt_fs_get_fs_optstr +mnt_fs_get_vfs_optstr +mnt_fs_get_freq +mnt_fs_set_freq +mnt_fs_get_passno +mnt_fs_set_passno +mnt_fs_get_id +mnt_fs_get_parent_id +mnt_fs_get_devno +mnt_fs_get_option +mnt_fs_match_target +mnt_fs_match_source +mnt_fs_match_fstype +mnt_fs_match_options +mnt_fprintf_line +mnt_fs_fprintf +mnt_fs_print_debug +</SECTION> + +<SECTION> +<FILE>tab</FILE> +mnt_tab +mnt_new_tab_from_file +mnt_tab_parse_file +mnt_tab_strerror +mnt_tab_get_nerrs +mnt_new_tab +mnt_free_tab +mnt_tab_get_nents +mnt_tab_set_cache +mnt_tab_get_cache +mnt_tab_get_name +mnt_tab_add_fs +mnt_tab_remove_fs +mnt_tab_next_fs +mnt_tab_next_child_fs +mnt_tab_get_root_fs +mnt_tab_set_iter +mnt_tab_find_target +mnt_tab_find_srcpath +mnt_tab_find_tag +mnt_tab_find_source +mnt_tab_find_next_fs +mnt_tab_fprintf +mnt_tab_update_file +</SECTION> diff --git a/shlibs/mount/docs/libmount.types b/shlibs/mount/docs/libmount.types new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/shlibs/mount/docs/libmount.types diff --git a/shlibs/mount/docs/version.xml.in b/shlibs/mount/docs/version.xml.in new file mode 100644 index 00000000..d78bda93 --- /dev/null +++ b/shlibs/mount/docs/version.xml.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/shlibs/mount/mount.pc.in b/shlibs/mount/mount.pc.in new file mode 100644 index 00000000..94051b8b --- /dev/null +++ b/shlibs/mount/mount.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@usrlib_execdir@ +includedir=@includedir@ + +Name: mount +Description: mount library +Version: @LIBMOUNT_VERSION@ +Requires.private: blkid +Cflags: -I${includedir}/mount +Libs: -L${libdir} -lmount diff --git a/shlibs/mount/src/Makefile.am b/shlibs/mount/src/Makefile.am new file mode 100644 index 00000000..aebd3cc5 --- /dev/null +++ b/shlibs/mount/src/Makefile.am @@ -0,0 +1,56 @@ +include $(top_srcdir)/config/include-Makefile.am + +AM_CPPFLAGS += -I$(ul_libmount_incdir) \ + -I$(ul_libmount_srcdir) \ + -I$(ul_libblkid_incdir) + +# includes +mountincdir = $(includedir)/mount +nodist_mountinc_HEADERS = mount.h + +usrlib_exec_LTLIBRARIES = libmount.la +libmount_la_SOURCES = mountP.h version.c utils.c test.c init.c cache.c \ + optstr.c optmap.c optent.c optls.c iter.c lock.c \ + fs.c tab.c tab_parse.c \ + $(mountinc_HEADERS) \ + $(top_srcdir)/include/list.h \ + $(top_srcdir)/lib/mangle.c \ + $(top_srcdir)/lib/canonicalize.c + +nodist_libmount_la_SOURCES = mountP.h + +libmount_la_LIBADD = $(ul_libblkid_la) + +libmount_la_DEPENDENCIES = $(libmount_la_LIBADD) mount.sym mount.h.in + +libmount_la_LDFLAGS = -Wl,--version-script=$(ul_libmount_srcdir)/mount.sym \ + -version-info $(LIBMOUNT_VERSION_INFO) + +EXTRA_DIST = mount.sym mount.h.in +CLEANFILES = $(tests) + +# move lib from $(usrlib_execdir) to $(libdir) if needed +install-exec-hook: + if test "$(usrlib_execdir)" != "$(libdir)"; then \ + mkdir -p $(DESTDIR)$(libdir); \ + mv $(DESTDIR)$(usrlib_execdir)/libmount.so.* $(DESTDIR)$(libdir); \ + so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libmount.so); \ + so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \ + (cd $(DESTDIR)$(usrlib_execdir) && \ + rm -f libmount.so && \ + $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libmount.so); \ + fi + +uninstall-hook: + rm -f $(DESTDIR)$(libdir)/libmount.so* + + +tests = test_version test_cache test_optstr test_optls test_lock \ + test_tab test_utils + +tests: all $(tests) +test_%: %.c + $(COMPILE) -DTEST_PROGRAM $< .libs/libmount.a \ + $(ul_libblkid_builddir)/.libs/libblkid.a -o $@ \ + $(UUID_LIBS) + diff --git a/shlibs/mount/src/cache.c b/shlibs/mount/src/cache.c new file mode 100644 index 00000000..027fda44 --- /dev/null +++ b/shlibs/mount/src/cache.c @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: cache + * @title: Cache + * @short_description: paths and tags (UUID/LABEL) caching + * + * The cache is a very simple API for work with tags (LABEL, UUID, ...) and + * paths. The cache uses libblkid as a backend from TAGs resolution. + * + * All returned paths are always canonicalized. + */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <blkid.h> + +#include "canonicalize.h" +#include "mountP.h" + +/* + * Canonicalized (resolved) paths & tags cache + */ +#define MNT_CACHE_CHUNKSZ 128 + +#define MNT_CACHE_ISTAG (1 << 1) /* entry is TAG */ +#define MNT_CACHE_ISPATH (1 << 2) /* entry is path */ +#define MNT_CACHE_TAGREAD (1 << 3) /* tag read by mnt_cache_read_tags() */ + +/* path cache entry */ +struct mnt_cache_entry { + char *native; /* the original path */ + char *real; /* canonicalized path */ + int flag; +}; + +struct _mnt_cache { + struct mnt_cache_entry *ents; + size_t nents; + size_t nallocs; + + /* blkid_evaluate_tag() works in two ways: + * + * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks, + * then the blkid_cache is NULL. + * + * 2/ all tags are read from /etc/blkid.tab and verified by /dev + * scanning, then the blkid_cache is not NULL and then it's + * better to reuse the blkid_cache. + */ + blkid_cache bc; +}; + +/** + * mnt_new_cache: + * + * Returns: new mnt_cache instance or NULL in case of ENOMEM error. + */ +mnt_cache *mnt_new_cache(void) +{ + return calloc(1, sizeof(struct _mnt_cache)); +} + +/** + * mnt_free_cache: + * @cache: pointer to mnt_cache instance + * + * Deallocates the cache. + */ +void mnt_free_cache(mnt_cache *cache) +{ + int i; + + if (!cache) + return; + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (e->real != e->native) + free(e->real); + free(e->native); + } + free(cache->ents); + if (cache->bc) + blkid_put_cache(cache->bc); + free(cache); +} + +/* note that the @native could be tha same pointer as @real */ +static int mnt_cache_add_entry(mnt_cache *cache, char *native, + char *real, int flag) +{ + struct mnt_cache_entry *e; + + assert(cache); + assert(real); + assert(native); + + if (cache->nents == cache->nallocs) { + size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ; + + e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry)); + if (!e) + return -1; + cache->ents = e; + cache->nallocs = sz; + } + + e = &cache->ents[cache->nents]; + e->native = native; + e->real = real; + e->flag = flag; + cache->nents++; + + DBG(DEBUG_CACHE, + printf("cache: add entry[%2zd] (%s): %s: %s\n", + cache->nents, + (flag & MNT_CACHE_ISPATH) ? "path" : "tag", + real, native)); + return 0; +} + +/* add tag to the cache, @real has to be allocated string */ +static int mnt_cache_add_tag(mnt_cache *cache, const char *token, + const char *value, char *real, int flag) +{ + size_t tksz, vlsz; + char *native; + + assert(cache); + assert(real); + assert(token); + assert(value); + + /* add into cache -- cache format for TAGs is + * native = "NAME\0VALUE\0" + * real = "/dev/foo" + */ + tksz = strlen(token); + vlsz = strlen(value); + + native = malloc(tksz + vlsz + 2); + if (!native) + goto error; + + memcpy(native, token, tksz + 1); /* include '\0' */ + memcpy(native + tksz + 1, value, vlsz + 1); + + if (mnt_cache_add_entry(cache, native, real, flag | MNT_CACHE_ISTAG)) + goto error; + return 0; +error: + free(native); + return -1; +} + + +/** + * mnt_cache_find_path: + * @cache: pointer to mnt_cache instance + * @path: "native" (non-canonicalized) path + * + * Returns: cached canonicalized path or NULL. + */ +const char *mnt_cache_find_path(mnt_cache *cache, const char *path) +{ + int i; + + assert(cache); + assert(path); + + if (!cache || !path) + return NULL; + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISPATH)) + continue; + if (strcmp(path, e->native) == 0) + return e->real; + } + return NULL; +} + +/** + * mnt_cache_find_tag: + * @cache: pointer to mnt_cache instance + * @token: tag name + * @value: tag value + * + * Returns: cached path or NULL. + */ +const char *mnt_cache_find_tag(mnt_cache *cache, + const char *token, const char *value) +{ + int i; + size_t tksz; + + assert(cache); + assert(token); + assert(value); + + if (!cache || !token || !value) + return NULL; + + tksz = strlen(token); + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISTAG)) + continue; + if (strcmp(token, e->native) == 0 && + strcmp(value, e->native + tksz + 1) == 0) + return e->real; + } + return NULL; +} + +/** + * mnt_cache_read_tags + * @cache: pointer to mnt_cache instance + * @devname: path device + * + * Reads @devname LABEL and UUID to the @cache. + * + * Returns: 0 if at least one tag was added, 1 if no tag was added or + * -1 in case of error. + */ +int mnt_cache_read_tags(mnt_cache *cache, const char *devname) +{ + int i, ntags = 0; + static blkid_probe pr; + const char *tags[] = { "LABEL", "UUID" }; + + assert(cache); + assert(devname); + + if (!cache || !devname) + return -1; + + DBG(DEBUG_CACHE, printf("cache: tags for %s requested\n", devname)); + + /* check is device is already cached */ + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_TAGREAD)) + continue; + if (strcmp(e->real, devname) == 0) + /* tags has been already read */ + return 0; + } + + DBG(DEBUG_CACHE, + printf("cache: reading tags for: %s\n", devname)); + + pr = blkid_new_probe_from_filename(devname); + if (!pr) + return -1; + + blkid_probe_enable_superblocks(pr, 1); + + blkid_probe_set_superblocks_flags(pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID); + + if (blkid_do_safeprobe(pr)) + goto error; + + for (i = 0; i < ARRAY_SIZE(tags); i++) { + const char *data; + char *dev; + + if (blkid_probe_lookup_value(pr, tags[i], &data, NULL)) + continue; + if (mnt_cache_find_tag(cache, tags[i], data)) + continue; /* already cached */ + + dev = strdup(devname); + if (!dev) + goto error; + if (mnt_cache_add_tag(cache, tags[i], data, dev, + MNT_CACHE_TAGREAD)) { + free(dev); + goto error; + } + ntags++; + } + + return ntags ? 0 : 1; +error: + blkid_free_probe(pr); + return -1; +} + +/** + * mnt_cache_device_has_tag: + * @cache: paths cache + * @devname: path to the device + * @token: tag name (e.g "LABEL") + * @value: tag value + * + * Look up @cache to check it @tag+@value are associated with @devname. + * + * Returns: 1 on success or 0. + */ +int mnt_cache_device_has_tag(mnt_cache *cache, const char *devname, + const char *token, const char *value) +{ + const char *path = mnt_cache_find_tag(cache, token, value); + + if (path && strcmp(path, devname) == 0) + return 1; + return 0; +} + +/** + * mnt_cache_find_tag_value: + * @cache: cache for results + * @devname: device name + * @token: tag name ("LABEL" or "UUID") + * + * Returns: LABEL or UUID for the @devname or NULL in case of error. + */ +char *mnt_cache_find_tag_value(mnt_cache *cache, + const char *devname, const char *token) +{ + int i; + + if (!cache || !devname || !token) + return NULL; + + if (mnt_cache_read_tags(cache, devname) != 0) + return NULL; + + for (i = 0; i < cache->nents; i++) { + struct mnt_cache_entry *e = &cache->ents[i]; + if (!(e->flag & MNT_CACHE_ISTAG)) + continue; + if (strcmp(e->real, devname) == 0 && /* dev name */ + strcmp(token, e->native) == 0) /* tag name */ + return e->native + strlen(token) + 1; /* tag value */ + } + + return NULL; +} + +/** + * mnt_resolve_path: + * @path: "native" path + * @cache: cache for results or NULL + * + * Returns: absolute path or NULL in case of error. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_resolve_path(const char *path, mnt_cache *cache) +{ + char *p = NULL; + char *native = NULL; + char *real = NULL; + + assert(path); + + if (!path) + return NULL; + if (cache) + p = (char *) mnt_cache_find_path(cache, path); + + if (!p) { + p = canonicalize_path(path); + + if (p && cache) { + native = strdup(path); + real = strcmp(path, p) == 0 ? native : p; + + if (!native || !real) + goto error; + + if (mnt_cache_add_entry(cache, native, real, + MNT_CACHE_ISPATH)) + goto error; + } + } + + return p; +error: + if (real != native) + free(real); + free(native); + return NULL; +} + +/** + * mnt_resolve_tag: + * @token: tag name + * @value: tag value + * @cache: for results or NULL + * + * Returns: device name or NULL in case of error. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_resolve_tag(const char *token, const char *value, mnt_cache *cache) +{ + char *p = NULL; + + assert(token); + assert(value); + + if (!token || !value) + return NULL; + + if (cache) + p = (char *) mnt_cache_find_tag(cache, token, value); + + if (!p) { + /* returns newly allocated string */ + p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL); + + if (p && cache && + mnt_cache_add_tag(cache, token, value, p, 0)) + goto error; + } + + return p; +error: + free(p); + return NULL; +} + + + +/** + * mnt_resolve_spec: + * @spec: path or tag + * @cache: paths cache + * + * Returns: canonicalized path or NULL. The result has to be + * deallocated by free() if @cache is NULL. + */ +char *mnt_resolve_spec(const char *spec, mnt_cache *cache) +{ + char *cn = NULL; + + if (!spec) + return NULL; + + if (strchr(spec, '=')) { + char *tag, *val; + + if (!blkid_parse_tag_string(spec, &tag, &val)) { + cn = mnt_resolve_tag(tag, val, cache); + + free(tag); + free(val); + } + } else + cn = mnt_resolve_path(spec, cache); + + return cn; +} + + +#ifdef TEST_PROGRAM + +int test_resolve_path(struct mtest *ts, int argc, char *argv[]) +{ + char line[BUFSIZ]; + mnt_cache *cache; + + cache = mnt_new_cache(); + if (!cache) + return -1; + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); + char *p; + + if (line[sz - 1] == '\n') + line[sz - 1] = '\0'; + + p = mnt_resolve_path(line, cache); + printf("%s : %s\n", line, p); + } + mnt_free_cache(cache); + return 0; +} + +int test_resolve_spec(struct mtest *ts, int argc, char *argv[]) +{ + char line[BUFSIZ]; + mnt_cache *cache; + + cache = mnt_new_cache(); + if (!cache) + return -1; + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); + char *p; + + if (line[sz - 1] == '\n') + line[sz - 1] = '\0'; + + p = mnt_resolve_spec(line, cache); + printf("%s : %s\n", line, p); + } + mnt_free_cache(cache); + return 0; +} + +int test_read_tags(struct mtest *ts, int argc, char *argv[]) +{ + char line[BUFSIZ]; + mnt_cache *cache; + + cache = mnt_new_cache(); + if (!cache) + return -1; + + while(fgets(line, sizeof(line), stdin)) { + size_t sz = strlen(line); + + if (line[sz - 1] == '\n') + line[sz - 1] = '\0'; + + if (*line == '/') { + if (mnt_cache_read_tags(cache, line) < 0) + fprintf(stderr, "%s: read tags faild\n", line); + + } else if (strchr(line, '=')) { + char *tag, *val; + const char *cn = NULL; + + if (!blkid_parse_tag_string(line, &tag, &val)) { + cn = mnt_cache_find_tag(cache, tag, val); + + free(tag); + free(val); + } + if (cn) + printf("%s: %s\n", line, cn); + else + printf("%s: not cached\n", line); + } + } + mnt_free_cache(cache); + return 0; + +} + +int main(int argc, char *argv[]) +{ + struct mtest ts[] = { + { "--resolve-path", test_resolve_path, " resolve paths from stdin" }, + { "--resolve-spec", test_resolve_spec, " evaluate specs from stdin" }, + { "--read-tags", test_read_tags, " read devname or TAG from stdin" }, + { NULL } + }; + + return mnt_run_test(ts, argc, argv); +} +#endif diff --git a/shlibs/mount/src/fs.c b/shlibs/mount/src/fs.c new file mode 100644 index 00000000..63da2f20 --- /dev/null +++ b/shlibs/mount/src/fs.c @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: fs + * @title: Filesystem + * @short_description: mnt_fs represents one entry in fstab/mtab/mountinfo + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <blkid.h> + +#include "nls.h" +#include "mountP.h" + +/** + * mnt_new_fs: + * + * Returns: newly allocated mnt_file fs. + */ +mnt_fs *mnt_new_fs(void) +{ + mnt_fs *fs = calloc(1, sizeof(struct _mnt_fs)); + if (!fs) + return NULL; + + INIT_LIST_HEAD(&fs->ents); + return fs; +} + +/** + * mnt_free_fs: + * @fs: fs pointer + * + * Deallocates the fs. + */ +void mnt_free_fs(mnt_fs *fs) +{ + if (!fs) + return; + list_del(&fs->ents); + + free(fs->source); + free(fs->tagname); + free(fs->tagval); + free(fs->mntroot); + free(fs->target); + free(fs->fstype); + free(fs->optstr); + free(fs->vfs_optstr); + free(fs->fs_optstr); + + free(fs); +} + +/** + * mnt_fs_get_userdata: + * @fs: mnt_file instance + * + * Returns: private data set by mnt_fs_set_userdata() or NULL. + */ +void *mnt_fs_get_userdata(mnt_fs *fs) +{ + return fs ? fs->userdata : NULL; +} + +/** + * mnt_fs_set_userdata: + * @fs: mnt_file instance + * + * The "userdata" are library independent data. + * + * Returns: 0 or -1 in case of error (if @fs is NULL). + */ +int mnt_fs_set_userdata(mnt_fs *fs, void *data) +{ + if (!fs) + return -1; + fs->userdata = data; + return 0; +} + +/** + * mnt_fs_get_srcpath: + * @fs: mnt_file (fstab/mtab/mountinfo) fs + * + * The mount "source path" is: + * - a directory for 'bind' mounts (in fstab or mtab only) + * - a device name for standard mounts + * + * See also mnt_fs_get_tag() and mnt_fs_get_source(). + * + * Returns: mount source path or NULL in case of error or when the path + * is not defined. + */ +const char *mnt_fs_get_srcpath(mnt_fs *fs) +{ + assert(fs); + if (!fs) + return NULL; + + /* fstab-like fs */ + if (fs->tagname) + return NULL; /* the source contains a "NAME=value" */ + return fs->source; +} + +/** + * mnt_fs_get_source: + * @fs: mnt_file (fstab/mtab/mountinfo) fs + * + * Returns: mount source. Note that the source could be unparsed TAG + * (LABEL/UUID). See also mnt_fs_get_srcpath() and mnt_fs_get_tag(). + */ +const char *mnt_fs_get_source(mnt_fs *fs) +{ + return fs ? fs->source : NULL; +} + +/* Used by parser mnt_file ONLY (@source has to be allocated) */ +int __mnt_fs_set_source(mnt_fs *fs, char *source) +{ + assert(fs); + + if (!source) + return -1; + + if (strchr(source, '=')) { + char *name, *val; + + if (blkid_parse_tag_string(source, &name, &val) != 0) + return -1; + + fs->tagval = val; + fs->tagname = name; + } + + fs->source = source; + return 0; +} + +/** + * mnt_fs_set_source: + * @fs: fstab/mtab/mountinfo entry + * @source: new source + * + * This function creates a private copy (strdup()) of @source. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_set_source(mnt_fs *fs, const char *source) +{ + char *p; + + if (!fs && !source) + return -1; + + p = strdup(source); + if (!p) + return -1; + + free(fs->tagval); + free(fs->tagname); + free(fs->source); + fs->tagval = fs->tagname = fs->source = NULL; + + return __mnt_fs_set_source(fs, p); +} + +/** + * mnt_fs_get_tag: + * @fs: fs + * @name: returns pointer to NAME string + * @value: returns pointer to VALUE string + * + * "TAG" is NAME=VALUE (e.g. LABEL=foo) + * + * The TAG is the first column in the fstab file. The TAG or "srcpath" has to + * be always set for all entries. + * + * See also mnt_fs_get_source(). + * + * <informalexample> + * <programlisting> + * char *src; + * mnt_fs *fs = mnt_tab_find_target(tb, "/home", MNT_ITER_FORWARD); + * + * if (!fs) + * goto err; + * + * src = mnt_fs_get_srcpath(fs); + * if (!src) { + * char *tag, *val; + * if (mnt_fs_get_tag(fs, &tag, &val) == 0) + * printf("%s: %s\n", tag, val); // LABEL or UUID + * } else + * printf("device: %s\n", src); // device or bind path + * </programlisting> + * </informalexample> + * + * Returns: 0 on success or -1 in case that a TAG is not defined. + */ +int mnt_fs_get_tag(mnt_fs *fs, const char **name, const char **value) +{ + if (fs == NULL || !fs->tagname) + return -1; + if (name) + *name = fs->tagname; + if (value) + *value = fs->tagval; + return 0; +} + +/** + * mnt_fs_get_target: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to mountpoint path or NULL + */ +const char *mnt_fs_get_target(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->target : NULL; +} + +/** + * mnt_fs_set_target: + * @fs: fstab/mtab/mountinfo entry + * @target: mountpoint + * + * This function creates a private copy (strdup()) of @target. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_set_target(mnt_fs *fs, const char *target) +{ + char *p; + + assert(fs); + + if (!fs || !target) + return -1; + + p = strdup(target); + if (!p) + return -1; + free(fs->target); + fs->target = p; + + return 0; +} + +/** + * mnt_fs_get_fstype: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to filesystem type. + */ +const char *mnt_fs_get_fstype(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->fstype : NULL; +} + +/* Used by mnt_file parser only */ +int __mnt_fs_set_fstype(mnt_fs *fs, char *fstype) +{ + assert(fs); + + if (!fstype) + return -1; + + fs->fstype = fstype; + fs->flags &= ~MNT_FS_PSEUDO; + fs->flags &= ~MNT_FS_NET; + + /* save info about pseudo filesystems */ + if (mnt_fstype_is_pseudofs(fs->fstype)) + fs->flags |= MNT_FS_PSEUDO; + else if (mnt_fstype_is_netfs(fs->fstype)) + fs->flags |= MNT_FS_NET; + + return 0; +} + +/** + * mnt_fs_set_fstype: + * @fs: fstab/mtab/mountinfo entry + * @fstype: filesystem type + * + * This function creates a private copy (strdup()) of @fstype. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_set_fstype(mnt_fs *fs, const char *fstype) +{ + char *p; + + if (!fs || !fstype) + return -1; + + p = strdup(fstype); + if (!p) + return -1; + free(fs->fstype); + + return __mnt_fs_set_fstype(fs, p); +} + +/** + * mnt_fs_get_optstr: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: pointer to mount option string with all options (FS and VFS) + */ +const char *mnt_fs_get_optstr(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->optstr : NULL; +} + +/** + * mnt_fs_set_optstr: + * @fs: fstab/mtab/mountinfo entry + * @optstr: options string + * + * This function creates a private copy (strdup()) of @optstr. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_set_optstr(mnt_fs *fs, const char *optstr) +{ + char *p; + + assert(fs); + + if (!fs || !optstr) + return -1; + p = strdup(optstr); + if (!p) + return -1; + + free(fs->optstr); + free(fs->fs_optstr); + free(fs->vfs_optstr); + fs->fs_optstr = fs->vfs_optstr = NULL; + + /* TODO: it would be possible to use built-in maps of options + * and differentiate between VFS and FS options, then we can + * set fs_optstr and vfs_optstr */ + + fs->optstr = p; + + return 0; +} + +/** + * mnt_fs_get_fs_optstr: + * @fs: fstab/mtab/mountinfo entry pointer + * + * This function works for "mountinfo" files only. + * + * Returns: pointer to superblock (fs-depend) mount option string or NULL. + */ +const char *mnt_fs_get_fs_optstr(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->fs_optstr : NULL; +} + +/** + * mnt_fs_get_vfs_optstr: + * @fs: fstab/mtab entry pointer + * + * This function works for "mountinfo" files only. + * + * Returns: pointer to fs-independent (VFS) mount option string or NULL. + */ +const char *mnt_fs_get_vfs_optstr(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->vfs_optstr : NULL; +} + + +/** + * mnt_fs_get_freq: + * @fs: fstab/mtab/mountinfo entry pointer + * + * Returns: dump frequency in days. + */ +int mnt_fs_get_freq(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->freq : 0; +} + +/** + * mnt_fs_set_freq: + * @fs: fstab/mtab entry pointer + * @freq: dump frequency in days + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_set_freq(mnt_fs *fs, int freq) +{ + assert(fs); + if (!fs) + return -1; + fs->freq = freq; + return 0; +} + +/** + * mnt_fs_get_passno: + * @fs: fstab/mtab entry pointer + * + * Returns: "pass number on parallel fsck". + */ +int mnt_fs_get_passno(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->passno: 0; +} + +/** + * mnt_fs_set_passno: + * @fs: fstab/mtab entry pointer + * @passno: pass number + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_set_passno(mnt_fs *fs, int passno) +{ + assert(fs); + if (!fs) + return -1; + fs->passno = passno; + return 0; +} + +/** + * mnt_fs_get_id: + * @fs: /proc/self/mountinfo entry + * + * Returns: mount ID (unique identifier of the mount) or -1 if ID undefined + * (for example if @fs is not mountinfo entry). + */ +int mnt_fs_get_id(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->id : -1; +} + +/** + * mnt_fs_get_parent_id: + * @fs: /proc/self/mountinfo entry + * + * Returns: parent mount ID or -1 if ID undefined (for example if @fs is not + * mountinfo entry). + */ +int mnt_fs_get_parent_id(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->parent : -1; +} + +/** + * mnt_fs_get_devno: + * @fs: /proc/self/mountinfo + * + * Returns: value of st_dev for files on filesystem or 0 in case of error. + */ +dev_t mnt_fs_get_devno(mnt_fs *fs) +{ + assert(fs); + return fs ? fs->devno : 0; +} + +/** + * mnt_fs_get_option: + * @fs: fstab/mtab/mountinfo entry pointer + * @name: option name + * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL + * @valsz: returns size of options value or 0 + * + * Returns: 0 on success, 1 when not found the @name or -1 in case of error. + */ +int mnt_fs_get_option(mnt_fs *fs, const char *name, + char **value, size_t *valsz) +{ + char *optstr = (char *) mnt_fs_get_optstr(fs); + return optstr ? mnt_optstr_get_option(optstr, name, value, valsz) : 1; +} + +/** + * mnt_fs_match_target: + * @fs: filesystem + * @target: mountpoint path + * @cache: tags/paths cache or NULL + * + * Possible are three attempts: + * 1) compare @target with @fs->target + * 2) realpath(@target) with @fs->target + * 3) realpath(@target) with realpath(@fs->target). + * + * The 2nd and 3rd attempts are not performed when @cache is NULL. + * + * Returns: 1 if @fs target is equal to @target else 0. + */ +int mnt_fs_match_target(mnt_fs *fs, const char *target, mnt_cache *cache) +{ + int rc = 0; + + if (!fs || !target || !fs->target) + return 0; + + /* 1) native paths */ + rc = !strcmp(target, fs->target); + + if (!rc && cache) { + /* 2) - canonicalized and non-canonicalized */ + char *cn = mnt_resolve_path(target, cache); + rc = (cn && strcmp(cn, fs->target) == 0); + + /* 3) - canonicalized and canonicalized */ + if (!rc && cn) { + char *tcn = mnt_resolve_path(fs->target, cache); + rc = (tcn && strcmp(cn, tcn) == 0); + } + } + + return rc; +} + +/** + * mnt_fs_match_source: + * @fs: filesystem + * @source: tag or path (device or so) + * @cache: tags/paths cache or NULL + * + * Possible are four attempts: + * 1) compare @source with @fs->source + * 2) compare realpath(@source) with @fs->source + * 3) compare realpath(@source) with realpath(@fs->source) + * 4) compare realpath(@source) with evaluated tag from @fs->source + * + * The 2nd, 3rd and 4th attempts are not performed when @cache is NULL. The + * 2nd and 3rd attempts are not performed if @fs->source is tag. + * + * Returns: 1 if @fs source is equal to @source else 0. + */ +int mnt_fs_match_source(mnt_fs *fs, const char *source, mnt_cache *cache) +{ + char *cn; + const char *src, *t, *v; + + if (!fs || !source || !fs->source) + return 0; + + /* 1) native paths/tags */ + if (!strcmp(source, fs->source)) + return 1; + + if (!cache) + return 0; + if (fs->flags & (MNT_FS_NET | MNT_FS_PSEUDO)) + return 0; + + cn = mnt_resolve_spec(source, cache); + if (!cn) + return 0; + + /* 2) canonicalized and native */ + src = mnt_fs_get_srcpath(fs); + if (src && !strcmp(cn, src)) + return 1; + + /* 3) canonicalized and canonicalized */ + if (src) { + src = mnt_resolve_path(src, cache); + if (src && !strcmp(cn, src)) + return 1; + } + if (src || mnt_fs_get_tag(fs, &t, &v)) + /* src path does not match and tag is not defined */ + return 0; + + /* read @source's tags to the cache */ + if (mnt_cache_read_tags(cache, cn) < 0) { + if (errno == EACCES) { + /* we don't have permissions to read TAGs from + * @source, but can translate @fs tag to devname. + * + * (because libblkid uses udev symlinks and this is + * accessible for non-root uses) + */ + char *x = mnt_resolve_tag(t, v, cache); + if (x && !strcmp(x, cn)) + return 1; + } + return 0; + } + + /* 4) has the @source a tag that matches with tag from @fs ? */ + if (mnt_cache_device_has_tag(cache, cn, t, v)) + return 1; + + return 0; +} + +/** + * mnt_fs_match_fstype: + * @fs: filesystem + * @types: filesystem name or comma delimited list of filesystems + * + * For more details see mnt_match_fstype(). + * + * Returns: 1 if @fs type is matching to @types else 0. The function returns + * 0 when types is NULL. + */ +int mnt_fs_match_fstype(mnt_fs *fs, const char *types) +{ + return mnt_match_fstype(fs->fstype, types); +} + +/** + * mnt_fs_match_options: + * @fs: filesystem + * @options: comma delimited list of options (and nooptions) + * + * For more details see mnt_match_options(). + * + * Returns: 1 if @fs type is matching to @options else 0. The function returns + * 0 when types is NULL. + */ +int mnt_fs_match_options(mnt_fs *fs, const char *options) +{ + return mnt_match_options(fs->optstr, options); +} + +/* Unfortunately the classical Unix /etc/mtab and /etc/fstab + do not handle directory names containing spaces. + Here we mangle them, replacing a space by \040. + What do other Unices do? */ + +static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' }; + +static char *mangle(const char *s) +{ + char *ss, *sp; + int n; + + n = strlen(s); + ss = sp = malloc(4*n+1); + if (!sp) + return NULL; + while(1) { + for (n = 0; n < sizeof(need_escaping); n++) { + if (*s == need_escaping[n]) { + *sp++ = '\\'; + *sp++ = '0' + ((*s & 0300) >> 6); + *sp++ = '0' + ((*s & 070) >> 3); + *sp++ = '0' + (*s & 07); + goto next; + } + } + *sp++ = *s; + if (*s == 0) + break; + next: + s++; + } + return ss; +} + +/** + * mnt_fprintf_line: + * @f: FILE + * @fmt: printf-like format string (see MNT_TAB_PRINTFMT) + * @source: (spec) device name or tag=value + * @target: mountpoint + * @fstype: filesystem type + * @options: mount options + * @freq: dump frequency in days + * @passno: pass number on parallel fsck + * + * It's recommended to use this function rather than directly call fprintf() to + * write an entry to mtab/fstab. All data in these files has to be properly + * formatted (for example space within paths/tags has to be escaped, see + * fstab(5) for more details). + * + * Returns: return value from fprintf(). + */ +int mnt_fprintf_line( FILE *f, + const char *fmt, + const char *source, + const char *target, + const char *fstype, + const char *options, + int freq, + int passno) +{ + char *m1 = NULL, *m2 = NULL, *m3 = NULL, *m4 = NULL; + int rc = -1; + + if (!f || !fmt || !source || !target || !fstype || !options) + return -1; + + m1 = mangle(source); + m2 = mangle(target); + m3 = mangle(fstype); + m4 = mangle(options); + + if (!m1 || !m2 || !m3 || !m4) + goto done; + + rc = fprintf(f, fmt, m1, m2, m3, m4, freq, passno); +done: + free(m1); + free(m2); + free(m3); + free(m4); + + return rc; +} + +/** + * mnt_fs_fprintf: + * @fs: fstab/mtab/mountinfo entry + * @f: FILE + * @fmt: printf-like format string (see MNT_TAB_PRINTFMT) + * + * Returns: return value from fprintf(). + */ +int mnt_fs_fprintf(mnt_fs *fs, FILE *f, const char *fmt) +{ + assert(fs); + assert(f); + assert(fmt); + + if (!fs || !f) + return -1; + + return mnt_fprintf_line(f, fmt, + mnt_fs_get_source(fs), + mnt_fs_get_target(fs), + mnt_fs_get_fstype(fs), + mnt_fs_get_optstr(fs), + mnt_fs_get_freq(fs), + mnt_fs_get_passno(fs)); +} + +/** + * mnt_fs_print_debug + * @fs: fstab/mtab/mountinfo entry + * @file: output + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_fs_print_debug(mnt_fs *fs, FILE *file) +{ + if (!fs) + return -1; + fprintf(file, "------ fs: %p\n", fs); + fprintf(file, "source: %s\n", mnt_fs_get_source(fs)); + fprintf(file, "target: %s\n", mnt_fs_get_target(fs)); + fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs)); + fprintf(file, "optstr: %s\n", mnt_fs_get_optstr(fs)); + fprintf(file, "freq: %d\n", mnt_fs_get_freq(fs)); + fprintf(file, "pass: %d\n", mnt_fs_get_passno(fs)); + fprintf(file, "id: %d\n", mnt_fs_get_id(fs)); + fprintf(file, "parent: %d\n", mnt_fs_get_parent_id(fs)); + fprintf(file, "devno: %d:%d\n", major(mnt_fs_get_devno(fs)), + minor(mnt_fs_get_devno(fs))); + + + return 0; +} diff --git a/shlibs/mount/src/init.c b/shlibs/mount/src/init.c new file mode 100644 index 00000000..68c13f94 --- /dev/null +++ b/shlibs/mount/src/init.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: init + * @title: Library initialization + * @short_description: initialize debuging + */ + +#include <stdlib.h> + +#include "mountP.h" + +int libmount_debug_mask; + +/** + * mnt_init_debug: + * @mask: debug mask (0xffff to enable full debuging) + * + * If the @mask is not specified then this function reads + * LIBMOUNT_DEBUG environment variable to get the mask. + * + * Already initialized debugging stuff cannot be changed. It does not + * have effect to call this function twice. + */ +void mnt_init_debug(int mask) +{ + if (libmount_debug_mask & DEBUG_INIT) + return; + if (!mask) { + char *str = mnt_getenv_safe("LIBMOUNT_DEBUG"); + if (str) + libmount_debug_mask = strtoul(str, 0, 0); + } else + libmount_debug_mask = mask; + + if (libmount_debug_mask) + printf("libmount: debug mask set to 0x%04x.\n", + libmount_debug_mask); + libmount_debug_mask |= DEBUG_INIT; +} diff --git a/shlibs/mount/src/iter.c b/shlibs/mount/src/iter.c new file mode 100644 index 00000000..5c02c2a7 --- /dev/null +++ b/shlibs/mount/src/iter.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: iter + * @title: Iterator + * @short_description: unified iterator + * + * The iterator keeps direction and last position for access to the internal + * library tables/lists. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "mountP.h" + +/** + * mnt_new_iter: + * @direction: MNT_INTER_{FOR,BACK}WARD direction + * + * Returns: newly allocated generic libmount iterator. + */ +mnt_iter *mnt_new_iter(int direction) +{ + mnt_iter *itr = calloc(1, sizeof(struct _mnt_iter)); + if (!itr) + return NULL; + itr->direction = direction; + return itr; +} + +/** + * mnt_free_iter: + * @itr: iterator pointer + * + * Deallocates iterator. + */ +void mnt_free_iter(mnt_iter *itr) +{ + free(itr); +} + +/** + * mnt_reset_iter: + * @itr: iterator pointer + * @direction: MNT_INTER_{FOR,BACK}WARD or -1 to keep the derection unchanged + * + * Resets iterator. + */ +void mnt_reset_iter(mnt_iter *itr, int direction) +{ + assert(itr); + + if (direction == -1) + direction = itr->direction; + + if (itr) { + memset(itr, 0, sizeof(struct _mnt_iter)); + itr->direction = direction; + } +} + +/** + * mnt_iter_get_direction: + * @itr: iterator pointer + * + * Returns: MNT_INTER_{FOR,BACK}WARD or -1 in case of error. + */ +int mnt_iter_get_direction(mnt_iter *itr) +{ + assert(itr); + return itr ? itr->direction : -1; +} diff --git a/shlibs/mount/src/lock.c b/shlibs/mount/src/lock.c new file mode 100644 index 00000000..0f35aa3b --- /dev/null +++ b/shlibs/mount/src/lock.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: lock + * @title: Mtab locking + * @short_description: locking methods for work with /etc/mtab + * + * The lock is backwardly compatible with the standard linux /etc/mtab locking. + * Note, it's necessary to use the same locking schema in all application that + * access the file. + */ +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> +#include <fcntl.h> +#include <limits.h> + +#include "pathnames.h" +#include "nls.h" + +#include "mountP.h" + +/* + * lock handler + */ +struct _mnt_lock { + pid_t id; /* getpid() or so (see linkfile)... */ + char *lockfile; /* path to lock file (e.g. /etc/mtab~) */ + char *linkfile; /* path to link file (e.g. /etc/mtab~.<id>) */ + int lockfile_fd; /* lock file descriptor */ + int locked; /* do we own the lock? */ +}; + + +/** + * mnt_new_lock: + * @lockfile: path to lockfile or NULL (default is _PATH_MOUNTED_LOCK) + * @id: unique linkfile identifier or 0 (default is getpid()) + * + * Returns: newly allocated lock handler or NULL on case of error. + */ +mnt_lock *mnt_new_lock(const char *lockfile, pid_t id) +{ + mnt_lock *ml = calloc(1, sizeof(struct _mnt_lock)); + + if (!ml) + return NULL; + + ml->lockfile_fd = -1; + ml->id = id; + if (lockfile) { + ml->lockfile = strdup(lockfile); + if (!ml->lockfile) { + free(ml); + return NULL; + } + } + return ml; +} + +/** + * mnt_free_lock: + * @ml: mnt_lock handler + * + * Deallocates mnt_lock. + */ +void mnt_free_lock(mnt_lock *ml) +{ + if (!ml) + return; + free(ml->lockfile); + free(ml->linkfile); + free(ml); +} + +/** + * mnt_lock_get_lockfile: + * @ml: mnt_lock handler + * + * Returns: path to lockfile. + */ +const char *mnt_lock_get_lockfile(mnt_lock *ml) +{ + if (!ml) + return NULL; + if (ml->lockfile) + return ml->lockfile; + return _PATH_MOUNTED_LOCK; +} + +/** + * mnt_lock_get_linkfile: + * @ml: mnt_lock handler + * + * Returns: unique (per process) path to linkfile. + */ +const char *mnt_lock_get_linkfile(mnt_lock *ml) +{ + if (!ml) + return NULL; + + if (!ml->linkfile) { + const char *lf = mnt_lock_get_lockfile(ml); + size_t sz; + + if (!lf) + return NULL; + sz = strlen(lf) + 32; + + ml->linkfile = malloc(sz); + if (ml->linkfile) + snprintf(ml->linkfile, sz, "%s.%d", + lf, ml->id ? ml->id : getpid()); + } + return ml->linkfile; +} + +static void mnt_lockalrm_handler(int sig) +{ + /* do nothing, say nothing, be nothing */ +} + +/* + * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt + * fcntl() to avoid never ending waiting. + * + * Returns: 0 on success, 1 on timeout, -errno on error. + */ +static int mnt_wait_lock(mnt_lock *ml, struct flock *fl, time_t maxtime) +{ + struct timeval now; + struct sigaction sa, osa; + int ret = 0; + + gettimeofday(&now, NULL); + + if (now.tv_sec >= maxtime) + return 1; /* timeout */ + + /* setup ALARM handler -- we don't want to wait forever */ + sa.sa_flags = 0; + sa.sa_handler = mnt_lockalrm_handler; + sigfillset (&sa.sa_mask); + + sigaction(SIGALRM, &sa, &osa); + + DBG(DEBUG_LOCKS, fprintf(stderr, + "LOCK: (%d) waiting for F_SETLKW.\n", getpid())); + + alarm(maxtime - now.tv_sec); + if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1) + ret = errno == EINTR ? 1 : -errno; + alarm(0); + + /* restore old sigaction */ + sigaction(SIGALRM, &osa, NULL); + + DBG(DEBUG_LOCKS, fprintf(stderr, + "LOCK: (%d) leaving mnt_wait_setlkw(), rc=%d.\n", getpid(), ret)); + return ret; +} + +/* + * Create the lock file. + * + * The old code here used flock on a lock file /etc/mtab~ and deleted + * this lock file afterwards. However, as rgooch remarks, that has a + * race: a second mount may be waiting on the lock and proceed as + * soon as the lock file is deleted by the first mount, and immediately + * afterwards a third mount comes, creates a new /etc/mtab~, applies + * flock to that, and also proceeds, so that the second and third mount + * now both are scribbling in /etc/mtab. + * + * The new code uses a link() instead of a creat(), where we proceed + * only if it was us that created the lock, and hence we always have + * to delete the lock afterwards. Now the use of flock() is in principle + * superfluous, but avoids an arbitrary sleep(). + * + * Where does the link point to? Obvious choices are mtab and mtab~~. + * HJLu points out that the latter leads to races. Right now we use + * mtab~.<pid> instead. + * + * + * The original mount locking code has used sleep(1) between attempts and + * maximal number of attempts has been 5. + * + * There was very small number of attempts and extremely long waiting (1s) + * that is useless on machines with large number of mount processes. + * + * Now we wait few thousand microseconds between attempts and we have a global + * time limit (30s) rather than limit for number of attempts. The advantage + * is that this method also counts time which we spend in fcntl(F_SETLKW) and + * number of attempts is not restricted. + * -- kzak@redhat.com [Mar-2007] + * + * + * This mtab locking code has been refactored and moved to libmount. The mtab + * locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and + * backwardly compatible code. Don't forget that this code has to be compatible + * with 3rd party mounts (/sbin/mount.<foo>) and has to work with NFS. + * -- kzak@redhat.com [May-2009] + */ + +/* maximum seconds between first and last attempt */ +#define MOUNTLOCK_MAXTIME 30 + +/* sleep time (in microseconds, max=999999) between attempts */ +#define MOUNTLOCK_WAITTIME 5000 + +/* Remove lock file. */ +void mnt_unlock_file(mnt_lock *ml) +{ + if (!ml) + return; + + DBG(DEBUG_LOCKS, fprintf(stderr, "LOCK: (%d) unlocking/cleaning.\n", getpid())); + + if (ml->locked == 0 && ml->lockfile && ml->linkfile) + { + /* We have (probably) all files, but we don't own the lock, + * Really? Check it! Maybe ml->locked wasn't set properly + * because code was interrupted by signal. Paranoia? Yes. + * + * We own the lock when linkfile == lockfile. + */ + struct stat lo, li; + + if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) && + lo.st_dev == li.st_dev && lo.st_ino == li.st_ino) + ml->locked = 1; + } + if (ml->linkfile) + unlink(ml->linkfile); + if (ml->lockfile_fd >= 0) + close(ml->lockfile_fd); + if (ml->locked == 1 && ml->lockfile) + unlink(ml->lockfile); + + ml->locked = 0; + ml->lockfile_fd = -1; +} + +/** + * mnt_lock_file + * @ml: pointer to mnt_lock instance + * + * Creates lock file (e.g. /etc/mtab~). Note that this function uses + * alarm(). + * + * Your application has to always call mnt_unlock_file() before exit. + * + * Locking scheme: + * + * 1. create linkfile (e.g. /etc/mtab~.$PID) + * 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~) + * + * 3. a) link() success: setups F_SETLK lock (see fcnlt(2)) + * b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2. + * + * Example: + * + * <informalexample> + * <programlisting> + * mnt_lock *ml; + * + * void unlock_fallback(void) + * { + * if (!ml) + * return; + * mnt_unlock_file(ml); + * mnt_free_lock(ml); + * } + * + * int update_mtab() + * { + * int sig = 0; + * + * atexit(unlock_fallback); + * + * ml = mnt_new_lock(NULL, 0); + * + * if (mnt_lock_file(ml) != 0) { + * printf(stderr, "cannot create %s lockfile\n", + * mnt_lock_get_lockfile(ml)); + * return -1; + * } + * + * ... modify mtab ... + * + * mnt_unlock_file(ml); + * mnt_free_lock(ml); + * ml = NULL; + * return 0; + * } + * </programlisting> + * </informalexample> + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_lock_file(mnt_lock *ml) +{ + int i; + struct timespec waittime; + struct timeval maxtime; + const char *lockfile, *linkfile; + + if (!ml) + return -1; + if (ml->locked) + return 0; + + lockfile = mnt_lock_get_lockfile(ml); + if (!lockfile) + return -1; + linkfile = mnt_lock_get_linkfile(ml); + if (!linkfile) + return -1; + + i = open(linkfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); + if (i < 0) + /* linkfile does not exist (as a file) + and we cannot create it. Read-only filesystem? + Too many files open in the system? + Filesystem full? */ + goto failed; + + close(i); + + gettimeofday(&maxtime, NULL); + maxtime.tv_sec += MOUNTLOCK_MAXTIME; + + waittime.tv_sec = 0; + waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME); + + /* Repeat until it was us who made the link */ + while (ml->locked == 0) { + struct timeval now; + struct flock flock; + int j; + + j = link(linkfile, lockfile); + if (j == 0) + ml->locked = 1; + + if (j < 0 && errno != EEXIST) + goto failed; + + ml->lockfile_fd = open(lockfile, O_WRONLY); + + if (ml->lockfile_fd < 0) { + /* Strange... Maybe the file was just deleted? */ + int errsv = errno; + gettimeofday(&now, NULL); + if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) { + ml->locked = 0; + continue; + } + goto failed; + } + + flock.l_type = F_WRLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + + if (ml->locked) { + /* We made the link. Now claim the lock. */ + if (fcntl (ml->lockfile_fd, F_SETLK, &flock) == -1) { + DBG(DEBUG_LOCKS, fprintf(stderr, + "%s: can't F_SETLK lockfile, errno=%d\n", + lockfile, errno)); + /* proceed, since it was us who created the lockfile anyway */ + } + break; + } else { + /* Someone else made the link. Wait. */ + int err = mnt_wait_lock(ml, &flock, maxtime.tv_sec); + + if (err == 1) { + DBG(DEBUG_LOCKS, fprintf(stderr, + "%s: can't create link: time out (perhaps " + "there is a stale lock file?)", lockfile)); + goto failed; + + } else if (err < 0) + goto failed; + + nanosleep(&waittime, NULL); + close(ml->lockfile_fd); + ml->lockfile_fd = -1; + } + } + DBG(DEBUG_LOCKS, fprintf(stderr, + "LOCK: %s: (%d) successfully locked\n", + ml->lockfile, getpid())); + unlink(linkfile); + return 0; + +failed: + mnt_unlock_file(ml); + return -1; +} + +#ifdef TEST_PROGRAM +#include <err.h> + +mnt_lock *lock; + +/* + * read number from @filename, increment the number and + * write the number back to the file + */ +void increment_data(const char *filename, int verbose, int loopno) +{ + long num; + FILE *f; + char buf[256]; + + if (!(f = fopen(filename, "r"))) + err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); + + if (!fgets(buf, sizeof(buf), f)) + err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename); + + fclose(f); + num = atol(buf) + 1; + + if (!(f = fopen(filename, "w"))) + err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename); + + fprintf(f, "%ld", num); + fclose(f); + + if (verbose) + fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(), + filename, num - 1, num, loopno); +} + +void clean_lock(void) +{ + fprintf(stderr, "%d: cleaning\n", getpid()); + if (!lock) + return; + mnt_unlock_file(lock); + mnt_free_lock(lock); +} + +void sig_handler(int sig) +{ + errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig)); +} + +int test_lock(struct mtest *ts, int argc, char *argv[]) +{ + const char *lockfile, *datafile; + int verbose = 0, loops, l; + + if (argc < 4) + return -1; + + lockfile = argv[1]; + datafile = argv[2]; + loops = atoi(argv[3]); + + if (argc == 5 && strcmp(argv[4], "--verbose") == 0) + verbose = 1; + + atexit(clean_lock); + + /* be paranoid and call exit() (=clean_lock()) for all signals */ + { + int sig = 0; + struct sigaction sa; + + sa.sa_handler = sig_handler; + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + + while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD) + sigaction (sig, &sa, (struct sigaction *) 0); + } + + for (l = 0; l < loops; l++) { + lock = mnt_new_lock(lockfile, 0); + + if (mnt_lock_file(lock) == -1) { + fprintf(stderr, "%d: failed to create lock file: %s\n", + getpid(), lockfile); + return -1; + } + + increment_data(datafile, verbose, l); + + mnt_unlock_file(lock); + mnt_free_lock(lock); + lock = NULL; + } + + return 0; +} + +/* + * Note that this test should be executed from a script that creates many + * parallel processes, otherwise this test does not make sense. + */ +int main(int argc, char *argv[]) +{ + struct mtest tss[] = { + { "--lock", test_lock, " <lockfile> <datafile> <loops> [--verbose] increment number in datafile" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/mount.h.in b/shlibs/mount/src/mount.h.in new file mode 100644 index 00000000..cd9159cc --- /dev/null +++ b/shlibs/mount/src/mount.h.in @@ -0,0 +1,407 @@ +/* + * mount.h - libmount API + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LIBMOUNT_MOUNT_H +#define _LIBMOUNT_MOUNT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef USE_UNSTABLE_LIBMOUNT_API +# warning libmount API is not stable yet! +#endif + +#include <stdio.h> + + +#define LIBMOUNT_VERSION "@LIBMOUNT_VERSION@" + +/** + * mnt_cache: + * + * Stores canonicalized paths and evaluated tags + */ +typedef struct _mnt_cache mnt_cache; + +/** + * mnt_lock: + * + * Stores information about locked file (e.g. /etc/mtab) + */ +typedef struct _mnt_lock mnt_lock; + +/** + * mnt_iter: + * + * Generic iterator (stores state about lists) + */ +typedef struct _mnt_iter mnt_iter; + +/** + * mnt_optls: + * + * Mount options list (stores parsed mount options) + */ +typedef struct _mnt_optls mnt_optls; + +/** + * mnt_optent: + * + * Parsed mount option - "mnt_optls" entry + */ +typedef struct _mnt_optent mnt_optent; + +/** + * mnt_optmap: + * + * Mount options description (map) + */ +struct mnt_optmap +{ + const char *name; /* option name[=%<type>] (e.g. "loop[=%s]") */ + int id; /* option ID or MS_* flags (e.g MS_RDONLY) */ + int mask; /* MNT_{MFLAG,MDATA,INVMASK,...} mask */ +}; + +/* + * mount options map masks + */ +#define MNT_MFLAG (1 << 1) /* use the mask as mount(2) flag */ +#define MNT_MDATA (1 << 2) /* use the option as mount(2) data */ +#define MNT_INVERT (1 << 3) /* invert the mountflag */ +#define MNT_NOMTAB (1 << 4) /* skip in the mtab option string */ + +/** + * mnt_fs: + * + * Parsed fstab/mtab/mountinfo entry + */ +typedef struct _mnt_fs mnt_fs; + +/** + * mnt_tab: + * + * List of mnt_fs entries (parsed fstab/mtab/mountinfo) + */ +typedef struct _mnt_tab mnt_tab; + +/* init.c */ +extern void mnt_init_debug(int mask); + +/* version.c */ +extern int mnt_parse_version_string(const char *ver_string); +extern int mnt_get_library_version(const char **ver_string); + +/* utils.c */ +extern int mnt_fstype_is_netfs(const char *type); +extern int mnt_fstype_is_pseudofs(const char *type); +extern int mnt_match_fstype(const char *type, const char *pattern); +extern int mnt_match_options(const char *optstr, const char *pattern); + +/* cache.c */ +extern mnt_cache *mnt_new_cache(void); +extern void mnt_free_cache(mnt_cache *cache); +extern const char *mnt_cache_find_path(mnt_cache *cache, const char *path); +extern const char *mnt_cache_find_tag(mnt_cache *cache, + const char *token, const char *value); +extern int mnt_cache_read_tags(mnt_cache *cache, const char *devname); +extern int mnt_cache_device_has_tag(mnt_cache *cache, const char *devname, + const char *token, const char *value); + +extern char *mnt_cache_find_tag_value(mnt_cache *cache, + const char *devname, const char *token); + +extern char *mnt_resolve_path(const char *path, mnt_cache *cache); +extern char *mnt_resolve_tag(const char *token, const char *value, mnt_cache *cache); +extern char *mnt_resolve_spec(const char *spec, mnt_cache *cache); + +/* optstr.c */ +extern int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz, + char **value, size_t *valuesz); +extern int mnt_optstr_append_option(char **optstr, const char *name, + const char *value); +extern int mnt_optstr_get_option(char *optstr, const char *name, + char **value, size_t *valsz); +extern int mnt_optstr_set_option(char **optstr, const char *name, + const char *value); +extern int mnt_optstr_remove_option(char **optstr, const char *name); + +/* iter.c */ +enum { + + MNT_ITER_FORWARD = 0, + MNT_ITER_BACKWARD +}; +extern mnt_iter *mnt_new_iter(int direction); +extern void mnt_free_iter(mnt_iter *mi); +extern void mnt_reset_iter(mnt_iter *mi, int direction); +extern int mnt_iter_get_direction(mnt_iter *itr); + +/* optmap.c */ +enum { + MNT_LINUX_MAP = 1, + MNT_USERSPACE_MAP +}; +extern const struct mnt_optmap *mnt_get_builtin_optmap(int id); + +/* optent.c */ +extern const struct mnt_optmap *mnt_optent_get_map(mnt_optent *op); +extern const struct mnt_optmap *mnt_optent_get_mapent(mnt_optent *op); +extern const char *mnt_optent_get_type(mnt_optent *op); +extern int mnt_optent_set_value(mnt_optent *op, const char *data); +extern int mnt_optent_has_value(mnt_optent *op); +extern int mnt_optent_require_value(mnt_optent *op); +extern int mnt_optent_is_inverted(mnt_optent *op); +extern int mnt_optent_strtoul_value(mnt_optent *op, unsigned long int *number); +extern int mnt_optent_strtol_value(mnt_optent *op, long int *number); +extern int mnt_optent_strtoull_value(mnt_optent *op, unsigned long long int *number); +extern const char *mnt_optent_get_value(mnt_optent *op); +extern int mnt_optent_strlen_value(mnt_optent *op); +extern int mnt_optent_snprintf_value(mnt_optent *op, char *str, size_t size); +extern char *mnt_optent_dup_value(mnt_optent *op); +extern const char *mnt_optent_get_name(mnt_optent *op); +extern int mnt_optent_get_mask(mnt_optent *op); +extern int mnt_optent_get_id(mnt_optent *op); +extern int mnt_optent_get_flag(mnt_optent *op, int *flags); +extern int mnt_optent_is_unknown(mnt_optent *op); +extern int mnt_optent_print_debug(mnt_optent *op, FILE *file); + +/* optls.c */ +extern mnt_optls *mnt_new_optls(void); +extern void mnt_free_optls(mnt_optls *ls); +extern int mnt_optls_add_map(mnt_optls *ls, const struct mnt_optmap *map); +extern int mnt_optls_add_builtin_map(mnt_optls *ls, int id); +extern mnt_optent *mnt_optls_add_option(mnt_optls *ls, + const char *name, const char *value); +extern int mnt_optls_parse_optstr(mnt_optls *ls, const char *optstr); +extern int mnt_optls_remove_option(mnt_optls *ls, const char *name); +extern int mnt_optls_remove_option_by_flags(mnt_optls *ls, + const struct mnt_optmap *map, const int flags); +extern int mnt_optls_remove_option_by_iflags(mnt_optls *ls, + const struct mnt_optmap *map, const int flags); +extern int mnt_optls_next_option(mnt_optls *ls, mnt_iter *itr, + const struct mnt_optmap *map, mnt_optent **option); +extern mnt_optent *mnt_optls_get_option(mnt_optls *ls, const char *name); +extern int mnt_optls_get_ids(mnt_optls *ls, const struct mnt_optmap *map); +extern int mnt_optls_create_mountflags(mnt_optls *ls); +extern char *mnt_optls_create_mountdata(mnt_optls *ls); +extern char *mnt_optls_create_mtab_optstr(mnt_optls *ls); +extern char *mnt_optls_create_userspace_optstr(mnt_optls *ls); +extern int mnt_optls_print_debug(mnt_optls *ls, FILE *file); + +/* lock.c */ +extern mnt_lock *mnt_new_lock(const char *lockfile, pid_t id); +extern void mnt_free_lock(mnt_lock *ml); +extern const char *mnt_lock_get_lockfile(mnt_lock *ml); +extern const char *mnt_lock_get_linkfile(mnt_lock *ml); +extern void mnt_unlock_file(mnt_lock *ml); +extern int mnt_lock_file(mnt_lock *ml); + +/* fs.c */ +extern mnt_fs *mnt_new_fs(void); +extern void mnt_free_fs(mnt_fs *ent); +extern void *mnt_fs_get_userdata(mnt_fs *fs); +extern int mnt_fs_set_userdata(mnt_fs *fs, void *data); +extern const char *mnt_fs_get_source(mnt_fs *ent); +extern int mnt_fs_set_source(mnt_fs *ent, const char *source); +extern const char *mnt_fs_get_srcpath(mnt_fs *ent); +extern int mnt_fs_get_tag(mnt_fs *ent, const char **name, const char **value); +extern const char *mnt_fs_get_target(mnt_fs *ent); +extern int mnt_fs_set_target(mnt_fs *ent, const char *target); +extern const char *mnt_fs_get_fstype(mnt_fs *ent); +extern int mnt_fs_set_fstype(mnt_fs *ent, const char *fstype); +extern const char *mnt_fs_get_optstr(mnt_fs *ent); +extern int mnt_fs_set_optstr(mnt_fs *ent, const char *optstr); +extern const char *mnt_fs_get_vfs_optstr(mnt_fs *ent); +extern const char *mnt_fs_get_fs_optstr(mnt_fs *ent); +extern int mnt_fs_get_freq(mnt_fs *ent); +extern int mnt_fs_set_freq(mnt_fs *ent, int freq); +extern int mnt_fs_get_passno(mnt_fs *ent); +extern int mnt_fs_set_passno(mnt_fs *ent, int passno); +extern int mnt_fs_get_id(mnt_fs *fs); +extern int mnt_fs_get_parent_id(mnt_fs *fs); +extern dev_t mnt_fs_get_devno(mnt_fs *fs); +extern int mnt_fs_get_option(mnt_fs *ent, const char *name, + char **value, size_t *valsz); + +extern int mnt_fs_match_target(mnt_fs *fs, const char *target, mnt_cache *cache); +extern int mnt_fs_match_source(mnt_fs *fs, const char *source, mnt_cache *cache); +extern int mnt_fs_match_fstype(mnt_fs *fs, const char *types); +extern int mnt_fs_match_options(mnt_fs *fs, const char *options); + +/* mtab/fstab line */ +#define MNT_TAB_PRINTFMT "%s %s %s %s %d %d\n" + +extern int mnt_fprintf_line( + FILE *f, + const char *fmt, + const char *source, + const char *target, + const char *fstype, + const char *options, + int freq, + int passno); + +extern int mnt_fs_fprintf(mnt_fs *ent, FILE *f, const char *fmt); +extern int mnt_fs_print_debug(mnt_fs *ent, FILE *file); + +/* tab-parse.c */ +extern mnt_tab *mnt_new_tab_from_file(const char *filename); +extern int mnt_tab_parse_file(mnt_tab *tb); +extern char *mnt_tab_strerror(mnt_tab *tb, char *buf, size_t buflen); +extern int mnt_tab_get_nerrs(mnt_tab *tb); + +/* tab.c */ +extern mnt_tab *mnt_new_tab(const char *filename); +extern void mnt_free_tab(mnt_tab *tb); +extern int mnt_tab_get_nents(mnt_tab *tb); +extern int mnt_tab_set_cache(mnt_tab *tb, mnt_cache *mpc); +extern mnt_cache *mnt_tab_get_cache(mnt_tab *tb); +extern const char *mnt_tab_get_name(mnt_tab *tb); +extern int mnt_tab_add_fs(mnt_tab *tb, mnt_fs *fs); +extern int mnt_tab_remove_fs(mnt_tab *tb, mnt_fs *fs); +extern int mnt_tab_next_fs(mnt_tab *tb, mnt_iter *itr, mnt_fs **fs); +extern int mnt_tab_next_child_fs(mnt_tab *tb, mnt_iter *itr, + mnt_fs *parent, mnt_fs **chld); +extern int mnt_tab_get_root_fs(mnt_tab *tb, mnt_fs **root); +extern int mnt_tab_set_iter(mnt_tab *tb, mnt_iter *itr, mnt_fs *fs); + +extern mnt_fs *mnt_tab_find_target(mnt_tab *tb, const char *path, int direction); +extern mnt_fs *mnt_tab_find_srcpath(mnt_tab *tb, const char *path, int direction); +extern mnt_fs *mnt_tab_find_tag(mnt_tab *tb, const char *tag, + const char *val, int direction); +extern mnt_fs *mnt_tab_find_source(mnt_tab *tb, const char *source, int direction); + +extern int mnt_tab_find_next_fs(mnt_tab *tb, mnt_iter *itr, + int (*match_func)(mnt_fs *, void *), void *userdata, + mnt_fs **fs); + +extern int mnt_tab_fprintf(mnt_tab *tb, FILE *f, const char *fmt); +extern int mnt_tab_update_file(mnt_tab *tb); + + +/* + * mount(8) userspace options masks (MNT_MAP_USERSPACE map) + */ +#define MNT_MS_DFLTS (1 << 1) +#define MNT_MS_NOAUTO (1 << 2) +#define MNT_MS_USER (1 << 3) +#define MNT_MS_USERS (1 << 4) +#define MNT_MS_OWNER (1 << 5) +#define MNT_MS_GROUP (1 << 6) +#define MNT_MS_NETDEV (1 << 7) +#define MNT_MS_COMMENT (1 << 8) +#define MNT_MS_LOOP (1 << 9) +#define MNT_MS_NOFAIL (1 << 10) + +/* + * mount(2) MS_* masks (MNT_MAP_LINUX map) + */ +#ifndef MS_RDONLY +#define MS_RDONLY 1 /* Mount read-only */ +#endif +#ifndef MS_NOSUID +#define MS_NOSUID 2 /* Ignore suid and sgid bits */ +#endif +#ifndef MS_NODEV +#define MS_NODEV 4 /* Disallow access to device special files */ +#endif +#ifndef MS_NOEXEC +#define MS_NOEXEC 8 /* Disallow program execution */ +#endif +#ifndef MS_SYNCHRONOUS +#define MS_SYNCHRONOUS 16 /* Writes are synced at once */ +#endif +#ifndef MS_REMOUNT +#define MS_REMOUNT 32 /* Alter flags of a mounted FS */ +#endif +#ifndef MS_MANDLOCK +#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */ +#endif +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 /* Directory modifications are synchronous */ +#endif +#ifndef MS_NOATIME +#define MS_NOATIME 0x400 /* 1024: Do not update access times. */ +#endif +#ifndef MS_NODIRATIME +#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */ +#endif +#ifndef MS_BIND +#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */ +#endif +#ifndef MS_MOVE +#define MS_MOVE 0x2000 /* 8192: Atomically move tree */ +#endif +#ifndef MS_REC +#define MS_REC 0x4000 /* 16384: Recursive loopback */ +#endif +#ifndef MS_VERBOSE +#define MS_VERBOSE 0x8000 /* 32768 */ +#endif +#ifndef MS_RELATIME +#define MS_RELATIME 0x200000 /* 200000: Update access times relative + to mtime/ctime */ +#endif +#ifndef MS_UNBINDABLE +#define MS_UNBINDABLE (1<<17) /* 131072 unbindable*/ +#endif +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) /* 262144 Private*/ +#endif +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) /* 524288 Slave*/ +#endif +#ifndef MS_SHARED +#define MS_SHARED (1<<20) /* 1048576 Shared*/ +#endif +#ifndef MS_I_VERSION +#define MS_I_VERSION (1<<23) /* update inode I_version field */ +#endif +#ifndef MS_STRICTATIME +#define MS_STRICTATIME (1<<24) /* strict atime semantics */ +#endif + +/* + * Magic mount flag number. Had to be or-ed to the flag values. + */ +#ifndef MS_MGC_VAL +#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */ +#endif +#ifndef MS_MGC_MSK +#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ +#endif + + +/* Shared-subtree options */ +#define MS_PROPAGATION (MS_SHARED|MS_SLAVE|MS_UNBINDABLE|MS_PRIVATE) + +/* Options that we make ordinary users have by default. */ +#define MS_SECURE (MS_NOEXEC|MS_NOSUID|MS_NODEV) + +/* Options that we make owner-mounted devices have by default */ +#define MS_OWNERSECURE (MS_NOSUID|MS_NODEV) + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMOUNT_MOUNT_H */ diff --git a/shlibs/mount/src/mount.sym b/shlibs/mount/src/mount.sym new file mode 100644 index 00000000..819be0f1 --- /dev/null +++ b/shlibs/mount/src/mount.sym @@ -0,0 +1,134 @@ +/* + * The symbol versioning ensures that a new application requiring symbol foo; + * can't run with old libblkid.so not providing foo; + * version info can't enforce this since we never change the SONAME. + */ +MOUNT_2.18 { +global: + mnt_cache_device_has_tag; + mnt_cache_find_path; + mnt_cache_find_tag; + mnt_cache_find_tag_value; + mnt_cache_read_tags; + mnt_fprintf_line; + mnt_free_cache; + mnt_free_fs; + mnt_free_iter; + mnt_free_lock; + mnt_free_optls; + mnt_free_tab; + mnt_fs_fprintf; + mnt_fs_get_devno; + mnt_fs_get_freq; + mnt_fs_get_fstype; + mnt_fs_get_id; + mnt_fs_get_fs_optstr; + mnt_fs_get_option; + mnt_fs_get_optstr; + mnt_fs_get_vfs_optstr; + mnt_fs_get_parent_id; + mnt_fs_get_passno; + mnt_fs_get_source; + mnt_fs_get_srcpath; + mnt_fs_get_tag; + mnt_fs_get_target; + mnt_fs_get_userdata; + mnt_fs_match_fstype; + mnt_fs_match_options; + mnt_fs_match_source; + mnt_fs_match_target; + mnt_fs_print_debug; + mnt_fs_set_freq; + mnt_fs_set_fstype; + mnt_fs_set_optstr; + mnt_fs_set_passno; + mnt_fs_set_source; + mnt_fs_set_target; + mnt_fs_set_userdata; + mnt_fstype_is_netfs; + mnt_fstype_is_pseudofs; + mnt_get_builtin_optmap; + mnt_get_library_version; + mnt_iter_get_direction; + mnt_lock_file; + mnt_lock_get_linkfile; + mnt_lock_get_lockfile; + mnt_match_fstype; + mnt_match_options; + mnt_new_cache; + mnt_new_fs; + mnt_new_iter; + mnt_new_lock; + mnt_new_optls; + mnt_new_tab; + mnt_new_tab_from_file; + mnt_optent_dup_value; + mnt_optent_get_flag; + mnt_optent_get_id; + mnt_optent_get_map; + mnt_optent_get_mapent; + mnt_optent_get_mask; + mnt_optent_get_name; + mnt_optent_get_type; + mnt_optent_get_value; + mnt_optent_has_value; + mnt_optent_is_inverted; + mnt_optent_is_unknown; + mnt_optent_print_debug; + mnt_optent_require_value; + mnt_optent_set_value; + mnt_optent_snprintf_value; + mnt_optent_strlen_value; + mnt_optent_strtol_value; + mnt_optent_strtoull_value; + mnt_optent_strtoul_value; + mnt_optls_add_builtin_map; + mnt_optls_add_map; + mnt_optls_add_option; + mnt_optls_create_mountdata; + mnt_optls_create_mountflags; + mnt_optls_create_mtab_optstr; + mnt_optls_create_userspace_optstr; + mnt_optls_get_ids; + mnt_optls_get_option; + mnt_optls_next_option; + mnt_optls_parse_optstr; + mnt_optls_print_debug; + mnt_optls_remove_option; + mnt_optls_remove_option_by_flags; + mnt_optls_remove_option_by_iflags; + mnt_optstr_append_option; + mnt_optstr_get_option; + mnt_optstr_next_option; + mnt_optstr_remove_option; + mnt_optstr_set_option; + mnt_parse_version_string; + mnt_reset_iter; + mnt_resolve_path; + mnt_resolve_spec; + mnt_resolve_tag; + mnt_tab_add_fs; + mnt_tab_find_next_fs; + mnt_tab_find_source; + mnt_tab_find_srcpath; + mnt_tab_find_tag; + mnt_tab_find_target; + mnt_tab_fprintf; + mnt_tab_get_cache; + mnt_tab_get_name; + mnt_tab_get_nents; + mnt_tab_get_nerrs; + mnt_tab_get_root_fs; + mnt_tab_next_child_fs; + mnt_tab_next_fs; + mnt_tab_parse_file; + mnt_tab_remove_fs; + mnt_tab_set_cache; + mnt_tab_set_iter; + mnt_tab_strerror; + mnt_tab_update_file; + mnt_unlock_file; + mnt_init_debug; +local: + *; +}; diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h new file mode 100644 index 00000000..ddd86ea2 --- /dev/null +++ b/shlibs/mount/src/mountP.h @@ -0,0 +1,222 @@ +/* + * mountP.h - private library header file + * + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#ifndef _LIBMOUNT_PRIVATE_H +#define _LIBMOUNT_PRIVATE_H + +#include <sys/types.h> + +#define USE_UNSTABLE_LIBMOUNT_API + +#include "mount.h" +#include "list.h" + +/* features */ +#define CONFIG_LIBMOUNT_ASSERT +#define CONFIG_LIBMOUNT_DEBUG + +#ifdef CONFIG_LIBMOUNT_ASSERT +#include <assert.h> +#endif + +/* + * Debug + */ +#if defined(TEST_PROGRAM) && !defined(LIBMOUNT_DEBUG) +#define CONFIG_LIBMOUNT_DEBUG +#endif + +#define DEBUG_INIT (1 << 1) +#define DEBUG_CACHE (1 << 2) +#define DEBUG_OPTIONS (1 << 3) +#define DEBUG_LOCKS (1 << 4) +#define DEBUG_TAB (1 << 5) +#define DEBUG_ALL 0xFFFF + +#ifdef CONFIG_LIBMOUNT_DEBUG +#include <stdio.h> +extern int libmount_debug_mask; +#define DBG(m,x) if ((m) & libmount_debug_mask) x +#else +#define DBG(m,x) +#endif + +#ifdef TEST_PROGRAM +struct mtest { + const char *name; + int (*body)(struct mtest *ts, int argc, char *argv[]); + const char *usage; +}; + +/* utils.c */ +extern int mnt_run_test(struct mtest *tests, int argc, char *argv[]); +#endif + +/* utils.c */ +extern char *mnt_getenv_safe(const char *arg); +#ifndef HAVE_STRNLEN +extern size_t strnlen(const char *s, size_t maxlen); +#endif +#ifndef HAVE_STRNDUP +extern char *strndup(const char *s, size_t n); +#endif +#ifndef HAVE_STRNCHR +extern char *strnchr(const char *s, size_t maxlen, int c); +#endif +extern char *mnt_get_username(const uid_t uid); +extern char *mnt_strconcat3(char *s, const char *t, const char *u); + + +/* + * Generic iterator + */ +struct _mnt_iter { + struct list_head *p; /* current position */ + struct list_head *head; /* start position */ + int direction; /* MNT_ITER_{FOR,BACK}WARD */ +}; + +#define IS_ITER_FORWARD(_i) ((_i)->direction == MNT_ITER_FORWARD) +#define IS_ITER_BACKWARD(_i) ((_i)->direction == MNT_ITER_BACKWARD) + +#define MNT_ITER_INIT(itr, list) \ + do { \ + (itr)->p = IS_ITER_FORWARD(itr) ? \ + (list)->next : (list)->prev; \ + (itr)->head = (list); \ + } while(0) + +#define MNT_ITER_ITERATE(itr, res, restype, member) \ + do { \ + res = list_entry((itr)->p, restype, member); \ + (itr)->p = IS_ITER_FORWARD(itr) ? \ + (itr)->p->next : (itr)->p->prev; \ + } while(0) + + +/* + * mnt_optls entry + */ +struct _mnt_optent { + char *name; /* option name (allcocated when mapent is NULL) */ + char *value; /* option argument value */ + + int mask; /* MNT_{INVMASK,MDATA,MFLAG,NOMTAB,NOSYS} + * modifiable flags (initial value comes from map->mask) + */ + const struct mnt_optmap *mapent;/* the option description (msp entry) */ + const struct mnt_optmap *map; /* head of the map */ + + struct list_head opts; /* list of options */ +}; + +/* + * Container (list) for mount options + */ +struct _mnt_optls { + struct mnt_optmap const **maps; /* array with option maps */ + size_t nmaps; /* number of maps */ + + struct list_head opts; /* list of options */ +}; + +/* + * This struct represents one entry in mtab/fstab/mountinfo file. + */ +struct _mnt_fs { + struct list_head ents; + + int id; /* mountinfo[1]: ID */ + int parent; /* moutninfo[2]: parent */ + dev_t devno; /* moutninfo[3]: st_dev */ + + char *source; /* fstab[1]: mountinfo[10]: + * source dev, file, dir or TAG */ + char *tagname; /* fstab[1]: tag name - "LABEL", "UUID", ..*/ + char *tagval; /* tag value */ + + char *mntroot; /* mountinfo[4]: root of the mount within the FS */ + char *target; /* mountinfo[5], fstab[2]: mountpoint */ + char *fstype; /* mountinfo[9], fstab[3]: filesystem type */ + + char *optstr; /* mountinfo[6,11], fstab[4]: option string */ + char *vfs_optstr; /* mountinfo[6]: fs-independent (VFS) options */ + char *fs_optstr; /* mountinfo[11]: fs-depend options */ + + int freq; /* fstab[5]: dump frequency in days */ + int passno; /* fstab[6]: pass number on parallel fsck */ + + int flags; /* MNT_FS_* flags */ + int lineno; /* line number in the parental file */ + + void *userdata; /* library independent data */ +}; + +/* + * fs flags + */ +#define MNT_FS_ERROR (1 << 1) /* broken entry */ +#define MNT_FS_PSEUDO (1 << 2) /* pseudo filesystem */ +#define MNT_FS_NET (1 << 3) /* network filesystem */ + +/* + * File format + */ +enum { + MNT_FMT_FSTAB = 1, /* /etc/{fs,m}tab */ + MNT_FMT_MOUNTINFO /* /proc/#/mountinfo */ +}; + +/* + * mtab/fstab/mountinfo file + */ +struct _mnt_tab { + char *filename; /* file name or NULL */ + int fmt; /* MNT_FMT_* file format */ + + int nlines; /* number of lines in the file (include commentrys) */ + int nents; /* number of valid entries */ + int nerrs; /* number of broken entries (parse errors) */ + + mnt_cache *cache; /* canonicalized paths/tags cache */ + + struct list_head ents; /* list of entries (mentry) */ +}; + + +/* optmap.c */ +extern const struct mnt_optmap *mnt_optmap_get_entry(struct mnt_optmap const **maps, + int nmaps, const char *name, + size_t namelen, const struct mnt_optmap **mapent); +extern int mnt_optmap_enum_to_number(const struct mnt_optmap *mapent, + const char *rawdata, size_t len); +extern const char *mnt_optmap_get_type(const struct mnt_optmap *mapent); +extern int mnt_optmap_require_value(const struct mnt_optmap *mapent); + +/* optent.c */ + +/* private option masks -- see mount.h.in for the publick masks */ +#define MNT_HASVAL (1 << 10) + +extern mnt_optent *mnt_new_optent(const char *name, size_t namesz, + const char *value, size_t valsz, + struct mnt_optmap const **maps, int nmaps); +extern void mnt_free_optent(mnt_optent *op); +extern mnt_optent *mnt_new_optent_from_optstr(char **optstr, + struct mnt_optmap const **maps, int nmaps); +extern int mnt_optent_assign_map(mnt_optent *op, + struct mnt_optmap const **maps, int nmaps); + +/* fs.c */ +extern int __mnt_fs_set_source(mnt_fs *fs, char *source); +extern int __mnt_fs_set_fstype(mnt_fs *fs, char *fstype); + + + +#endif /* _LIBMOUNT_PRIVATE_H */ diff --git a/shlibs/mount/src/optent.c b/shlibs/mount/src/optent.c new file mode 100644 index 00000000..4b35b2d5 --- /dev/null +++ b/shlibs/mount/src/optent.c @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: optent + * @title: Parsed option + * @short_description: the mnt_optent keeps one parsed mount option + */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "nls.h" +#include "mountP.h" + +static int mnt_init_optent(mnt_optent *op, const char *name, size_t namelen, + struct mnt_optmap const **maps, int nmaps); +static int __mnt_optent_set_value(mnt_optent *op, const char *data, size_t len); + + +/* + * Returns a new optent. + */ +mnt_optent *mnt_new_optent( const char *name, size_t namesz, + const char *value, size_t valsz, + struct mnt_optmap const **maps, int nmaps) +{ + mnt_optent *op; + + op = calloc(1, sizeof(struct _mnt_optent)); + if (!op) + return NULL; + + INIT_LIST_HEAD(&op->opts); + + if (mnt_init_optent(op, name, namesz, maps, nmaps)) + goto err; + + if (value) { + if (__mnt_optent_set_value(op, value, valsz)) + goto err; + } else if (mnt_optent_require_value(op)) + goto err; + + return op; +err: + free(op); + return NULL; +} + +/* + * Deallocates the optent. + */ +void mnt_free_optent(mnt_optent *op) +{ + if (!op) + return; + + if (!op->mapent || op->mapent->name != op->name) + free(op->name); + + free(op->value); + + if (!list_empty(&op->opts)) + list_del(&op->opts); + + free(op); +} + +/* + * initialize or reinitialize the option entry -- note that the option + * name is set to @name and the old name is not free()ed. If the @name + * is NULL the already existing option name is used. + */ +static int mnt_init_optent(mnt_optent *op, const char *name, size_t namelen, + struct mnt_optmap const **maps, int nmaps) +{ + const struct mnt_optmap *mapent = NULL, *map = NULL; + + assert(op); + + if (!op) + return -1; + + if (!name && op->name) { + name = op->name; + namelen = strlen(name); + } + if (!name) + return -1; + + if (nmaps && maps) + map = mnt_optmap_get_entry(maps, nmaps, name, namelen, &mapent); + + if (mapent == NULL || mnt_optmap_get_type(mapent) != NULL) { + /* we allocate the name for uknown options of for options with + * "=%<type>" argument. This is not perfect... */ + if (op->name != name) + op->name = strndup(name, namelen); + } else + op->name = (char *) mapent->name; + + op->mapent = mapent; + op->map = map; + op->mask = mapent ? mapent->mask : 0; + if (op->value) + op->mask |= MNT_HASVAL; + + if (!op->name) + return -1; /* strdup() failed */ + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s: initialized\n", op->name)); + + return 0; +} + +static int mnt_optent_check_value(mnt_optent *op, const char *data, size_t len) +{ + const char *type; + char *end = NULL; + + assert(op); + if (!op) + return -1; + + type = mnt_optent_get_type(op); + if (!type) + goto err; /* value is unexpected */ + + if (!data) { + if (mnt_optent_require_value(op)) + goto err; + } else if (!strncmp(type, "%s", 2)) { + /* string type */ + ; + } else if (*type == '{') { + /* enum type */ + if (mnt_optmap_enum_to_number(op->mapent, data, len) < 0) + goto err; + } else { + /* numbers */ + int n; /* happy gcc */ + + errno = 0; + if (!strncmp(type, "%d", 2) || !strncmp(type, "%ld", 3)) + n = strtol(data, &end, 10); + else if (!strncmp(type, "%u", 2) || !strncmp(type, "%lu", 3)) + n = strtoul(data, &end, 10); + else if (!strncmp(type, "%lld", 4)) + n = strtoll(data, &end, 10); + else if (!strncmp(type, "%llu", 4)) + n = strtoull(data, &end, 10); + else if (!strncmp(type, "%o", 2)) + n = strtoul(data, &end, 8); + else if (!strncmp(type, "%x", 2)) + n = strtoul(data, &end, 16); + + if (errno == EINVAL || errno == ERANGE || end != data + len) + goto err; + } + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s (type=%s): pass check\n", + op->name, type)); + return 0; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s (type=%s): failed to check value %s\n", + op->name, type, data)); + return -1; +} + +/* + * Parses the first mount option from @optstr and move @optstr pointer + * to the next option. + * + * Returns new optent (parsed option) or NULL in case of error. + */ +mnt_optent *mnt_new_optent_from_optstr(char **optstr, + struct mnt_optmap const **maps, int nmaps) +{ + char *name, *value; + size_t nsz, vsz; + + if (mnt_optstr_next_option(optstr, &name, &nsz, &value, &vsz) == 0) + return mnt_new_optent(name, nsz, value, vsz, maps, nmaps); + + return NULL; +} + +/* + * Lookups @maps and tries to found corresponding map entry for the @op option. + * If the map is found the option value is reverified. + * + * Returns 0 on success, 1 if map not found, -1 in case of error (revalidation + * failed or so). + */ +int mnt_optent_assign_map(mnt_optent *op, + struct mnt_optmap const **maps, int nmaps) +{ + char *oldval, *oldname = NULL; + const char *type; + + assert(op); + assert(op->name); + + if (!op || !op->name) + return -1; + + if (op->mapent && op->name != op->mapent->name) + oldname = op->name; /* old name is allocated */ + + op->map = op->mapent = NULL; + oldval = op->value; + + if (mnt_init_optent(op, NULL, 0, maps, nmaps)) + return -1; + + if (op->name != oldname) + free(oldname); + + if (!op->map) + return 1; /* uknown option, it's not error */ + + /* the new type */ + type = mnt_optent_get_type(op); + + if (type == NULL && oldval) + goto err; /* value is unexpected */ + if (mnt_optent_require_value(op) && !oldval) + goto err; /* value is required */ + if (oldval && mnt_optent_check_value(op, oldval, strlen(oldval)) != 0) + goto err; /* bad value */ + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s: assigned to \n", op->name)); + return 0; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s: assign failed\n", op->name)); + return -1; +} + +/** + * mnt_optent_get_map: + * @op: pointer to mnt_optent instance + * + * Returns: pointer to the head of the map that is associated with the option or + * NULL (for "extra options"). + */ +const struct mnt_optmap *mnt_optent_get_map(mnt_optent *op) +{ + assert(op); + return op ? op->map : NULL; +} + +/** + * mnt_optent_get_map_entry: + * @op: pointer to mnt_optent instance + * + * Returns: pointer to the map entry that describes the option or NULL (for + * "extra options"). + */ +const struct mnt_optmap *mnt_optent_get_mapent(mnt_optent *op) +{ + assert(op); + return op ? op->mapent : NULL; +} + +/** + * mnt_optent_get_type: + * @op: mnt_optent instance + * + * Note that the @op has to be associated with any option map + * or the default "%s]" is returned. + * + * Returns: pointer to the begin of type format string or NULL. For example: + * + * "%s" --> string, required argument (definition in the map is: "foo=%s") + * "%s]" --> string, optional argument (definition in the map is: "foo[=%s]") + */ +const char *mnt_optent_get_type(mnt_optent *op) +{ + assert(op); + if (!op) + return NULL; + return op->mapent ? mnt_optmap_get_type(op->mapent) : "%s]"; +} + + + +/** + * mnt_optent_set_value: + * @op: mnt_optent instance + * @data: option argument data or NULL + * + * The function unset (zeroize) the option value if the @data pointer is NULL. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_optent_set_value(mnt_optent *op, const char *data) +{ + return __mnt_optent_set_value(op, data, data ? strlen(data) : 0); +} + +static int __mnt_optent_set_value(mnt_optent *op, const char *data, size_t len) +{ + assert(op); + if (!op) + return -1; + + free(op->value); + op->value = NULL; + op->mask &= ~MNT_HASVAL; + + if (mnt_optent_check_value(op, data, len) != 0) + goto err; + if (data) { + op->value = strndup(data, len); + if (!op->value) + goto err; + op->mask |= MNT_HASVAL; + } + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s: set argument value: %s\n", + op->name, op->value)); + return 0; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s: set argument value failed\n", + op->name)); + return -1; + +} + +/** + * mnt_optent_has_value: + * @option: pointer to mnt_optent instance + * + * Returns: 1 if the option has actually set an argument value, or 0. + */ +int mnt_optent_has_value(mnt_optent *op) +{ + return op && (op->mask & MNT_HASVAL) ? 1 : 0; +} + +/** + * mnt_optent_require_value: + * @op: pointer to mnt_optent instance + * + * Note that the @op has to be associated with any option map + * or 0 is returned. + * + * Returns: 1 if the option requires an argument (option=arg). + */ +int mnt_optent_require_value(mnt_optent *op) +{ + return op && op->mapent ? mnt_optmap_require_value(op->mapent) : 0; +} + +/** + * mnt_optent_is_inverted: + * @op: pointer to mnt_optent instance + * + * Returns: 1 if the option has MNT_INVERT mask or 0. + */ +int mnt_optent_is_inverted(mnt_optent *op) +{ + return (op && (op->mask & MNT_INVERT)); +} + +static int get_number_base(const char *type) +{ + int base = 10; /* default */ + + if (!strncmp(type, "%o", 2)) + base = 8; + else if (!strncmp(type, "%x", 16)) + base = 16; + return base; +} + +/** + * mnt_optent_strtoul_value: + * @op: pointer to mnt_optent instance + * @number: resulting number + * + * Converts an option value to number. The strtoul() base (decimal, octan or + * hex) is determined from (%u, %o or %x) option format type -- default is + * decimal (for unknown options). + * + * The whole option value has to be possible to convert to the number + * (e.g "123ABC" returns -1). + * + * This function also converts {enum0,enumN} type to number 0..N. For more + * details see info about option maps. + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_optent_strtoul_value(mnt_optent *op, unsigned long int *number) +{ + const char *type = NULL; + char *end; + size_t len; + + if (!mnt_optent_has_value(op) || !number) + goto err;; + type = mnt_optent_get_type(op); + if (!type) + goto err; + + if (*type == '{') { + int n; + + if (!op->mapent) + goto err; + n = mnt_optmap_enum_to_number(op->mapent, op->value, + strlen(op->value)); + if (n < 0) + goto err; + *number = n; + } else { + errno = 0; + *number = strtoul(op->value, &end, get_number_base(type)); + + if (errno == EINVAL || errno == ERANGE) + goto err; + len = strlen(op->value); + if (end != op->value + len) + goto err; + } + return 0; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s (type=%s): strtoul failed\n", + op->name, type)); + return -1; +} + +/** + * mnt_optent_strtol_value: + * @op: pointer to mnt_optent instance + * @number: resulting number + * + * Converts an option value to number. The strtol() base (decimal, octan or + * hex) is determined from (%u, %o or %x) option format type -- default is + * decimal. + * + * The whole option value has to be possible to convert to the number + * (e.g "123ABC" returns -1). + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_optent_strtol_value(mnt_optent *op, long int *number) +{ + const char *type; + char *end; + size_t len; + + if (!mnt_optent_has_value(op) || !number) + return -1; + + type = mnt_optent_get_type(op); + if (!type) + goto err; + + errno = 0; + *number = strtol(op->value, &end, get_number_base(type)); + + if (errno == EINVAL || errno == ERANGE) + goto err; + len = strlen(op->value); + if (end != op->value + len) + goto err; + + return 0; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s (type=%s): strtol failed\n", + op->name, type)); + return -1; +} + +/** + * mnt_optent_strtoull_value: + * @op: pointer to mnt_optent instance + * @number: resulting number + * + * Converts an option value to number. The strtoull() base (decimal, octan or + * hex) is determined from (%u, %o or %x) option format type -- default is + * decimal. + * + * The whole option value has to be possible to convert to the number + * (e.g "123ABC" returns -1). + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_optent_strtoull_value(mnt_optent *op, unsigned long long int *number) +{ + const char *type; + char *end; + size_t len; + + if (!mnt_optent_has_value(op) || !number) + return -1; + + type = mnt_optent_get_type(op); + if (!type) + goto err; + + errno = 0; + *number = strtoull(op->value, &end, get_number_base(type)); + + if (errno == EINVAL || errno == ERANGE) + goto err; + len = strlen(op->value); + if (end != op->value + len) + goto err; + return 0; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: option %s (type=%s): strtoull failed\n", + op->name, type)); + return -1; + +} + +/** + * mnt_optent_get_value: + * @op: pointer to mnt_optent instance + * + * See also mnt_optent_has_value(). + * + * Returns: pointer to value or NULL. + */ +const char *mnt_optent_get_value(mnt_optent *op) +{ + return op? op->value : NULL; +} + +/** + * mnt_optent_strlen_value: + * @op: pointer to mnt_optent instance + * + * Returns: length of string that is necessary to print option value or -1 in + * case of error. + */ +int mnt_optent_strlen_value(mnt_optent *op) +{ + assert(op); + + if (!op) + return -1; + if (!mnt_optent_has_value(op)) + return 0; + return strlen(op->value); +} + +/** + * mnt_optent_snprintf_value: + * @op: pointer to mnt_optent instance + * @str: resulting string + * @size: size of string + * + * Returns: number of printed characters or negative number in case of error. + */ +int mnt_optent_snprintf_value(mnt_optent *op, char *str, size_t size) +{ + assert(op); + assert(str); + + if (!op || !str || !size) + return -1; + if (!mnt_optent_has_value(op)) + return -1; + + /* TODO: use extra quotes for SELinux contexts */ + return snprintf(str, size, "%s", op->value); +} + +/** + * mnt_optent_dup_value: + * @op: pointer to mnt_optent instance + * + * Returns: duplicate a option value. + */ +char *mnt_optent_dup_value(mnt_optent *op) +{ + assert(op); + + if (mnt_optent_has_value(op)) + return strdup(op->value); + return NULL; +} + +/** + * mnt_optent_get_name: + * @op: pointer to mnt_optent instance + * + * Returns: option name or NULL in case of error. + */ +const char *mnt_optent_get_name(mnt_optent *op) +{ + assert(op); + return op ? op->name : NULL; +} + +/** + * mnt_optent_get_mask: + * @op: pointer to mnt_optent instance + * + * The initial value of the option mask is a copy from map->mask. + * Note that the mask is NOT a mountflag/ID. + * + * Returns: option mask or 0. + */ +int mnt_optent_get_mask(mnt_optent *op) +{ + assert(op); + return op ? op->mask : 0; +} + +/** + * mnt_optent_get_id: + * @op: pointer to mnt_optent instance + * + * Note that the ID is also mountflag for all options with MNT_MFLAG mask. + * + * WARNING: the ID is usually shared between "option" (e.g. exec) and + * "nooption" (e.g. noexec) -- you have to carefully check for MNT_INVERT in + * the option mask. See mnt_optent_get_flag(). + * + * Returns: option ID/mountflag or 0 for extra options (options with undefined + * options map). + */ +int mnt_optent_get_id(mnt_optent *op) +{ + assert(op); + return op && op->mapent ? op->mapent->id : 0; +} + +/** + * mnt_optent_get_flag: + * @op: pointer to mnt_optent instance + * @flags: resulting flags + * + * Adds option ID to @flags or removes the ID from @flags when the option + * is an inverted option (e.g. "norelatime") + * + * <informalexample> + * <programlisting> + * int flags = 0; + * + * while(mnt_optls_next_option(&itr, opts, map, &op) == 0) + * mnt_optent_get_flag(op, &flags); + * + * if (flags & MS_RELATIME) + * printf("relatime is set\n"); + * </programlisting> + * </informalexample> + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_optent_get_flag(mnt_optent *op, int *flags) +{ + int id; + + assert(op); + if (!op || !flags) + return -1; + + id = mnt_optent_get_id(op); + if (op->mask & MNT_INVERT) + *flags &= ~id; + else + *flags |= id; + return 0; +} + +/** + * mnt_optent_is_unknown: + * @op: pointer to mnt_optent instance + * + * The "extra options" are unknown options (undefined in any option map) + * + * Returns: 1 or 0. + */ +int mnt_optent_is_unknown(mnt_optent *op) +{ + assert(op); + return op && op->mapent ? 0 : 1; +} + +/** + * mnt_optent_print_debug: + * @file: output + * @op: pointer to mnt_optent instance + * + * Prints details about the option. + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_optent_print_debug(mnt_optent *op, FILE *file) +{ + const struct mnt_optmap *map; + const char *type; + + if (!op) + return -1; + + fprintf(file, "------ option %p (%s):\n", op, mnt_optent_get_name(op)); + + fprintf(file, "\tID=0x%x\n", mnt_optent_get_id(op)); + fprintf(file, "\tMASK=%d\n", mnt_optent_get_mask(op)); + + map = mnt_optent_get_map(op); + fprintf(file, "\tMAP=%p\n", map ? map : NULL); + + map = mnt_optent_get_mapent(op); + fprintf(file, "\tMAPENT=%s\n", map ? map->name : NULL); + + fprintf(file, "\tHAS_VALUE=%s\n", + mnt_optent_has_value(op) ? "yes" : "not"); + + type = mnt_optent_get_type(op); + fprintf(file, "\tTYPE=%s\n", type ? : "<none>"); + fprintf(file, "\tVALUE=%s\n", op->value); + return 0; +} diff --git a/shlibs/mount/src/optls.c b/shlibs/mount/src/optls.c new file mode 100644 index 00000000..ff2b912b --- /dev/null +++ b/shlibs/mount/src/optls.c @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: optls + * @title: Options container + * @short_description: high-level API for work with parsed mount options + * + * The optls container allows to work with parsed mount options and generate + * arguments for mount(2) syscall, output to mtab or analyze userspace specific + * options. + */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "nls.h" +#include "mountP.h" + +/** + * mnt_new_optls: + * + * Returns: newly allocated and initialized optls instance. The library + * uses this object as a container for mount options. + */ +mnt_optls *mnt_new_optls(void) +{ + mnt_optls *ls = calloc(1, sizeof(struct _mnt_optls)); + if (!ls) + return NULL; + INIT_LIST_HEAD(&ls->opts); + return ls; +} + +/** + * mnt_free_optls: + * @ls: pointer to mnt_optls instance. + * + * Deallocates mnt_optls and all stored options. + */ +void mnt_free_optls(mnt_optls *ls) +{ + if (!ls) + return; + while (!list_empty(&ls->opts)) { + mnt_optent *o = list_entry(ls->opts.next, mnt_optent, opts); + mnt_free_optent(o); + } + + free(ls->maps); + free(ls); +} + +/** + * mnt_optls_add_map: + * @ls: pointer to mnt_optls instance + * @map: pointer to the custom map + * + * Stores pointer to the custom options map (options description). The map has + * to be accessible all time when the libmount works with options. (The map is + * usually a static array.) + * + * All already stored unknown mount options are reverified against the new map. + * Note, it's recommented to add all maps to the @optls container before options + * parsing. + * + * Example (add new options "foo" and "bar=data"): + * + * <informalexample> + * <programlisting> + * #define MY_MS_FOO (1 << 1) + * #define MY_MS_BAR (1 << 2) + * + * mnt_optmap myoptions[] = { + * { "foo", MY_MS_FOO, MNT_MFLAG }, + * { "nofoo", MY_MS_FOO, MNT_MFLAG | MNT_INVERT }, + * { "bar=%s",MY_MS_BAR, MNT_MDATA }, + * { NULL } + * }; + * + * mnt_optls_add_map(ls, myoptions); + * </programlisting> + * </informalexample> + * + * Returns: 0 on success, 1 on failed verification, or -1 in case of error. + */ +int mnt_optls_add_map(mnt_optls *ls, const struct mnt_optmap *map) +{ + mnt_optent *op; + mnt_iter itr; + + assert(ls); + assert(map || ls->maps == NULL); + + ls->maps = realloc(ls->maps, + sizeof(struct mnt_optmap *) * (ls->nmaps + 1)); + if (!ls->maps) + return -1; + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: optls %p: add map[%zd]", ls, ls->nmaps)); + ls->maps[ls->nmaps] = map; + ls->nmaps++; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) { + if (!mnt_optent_is_unknown(op)) + continue; + if (mnt_optent_assign_map(op, &map, 1) == -1) + return 1; + } + return 0; +} + +/** + * mnt_optls_add_builtin_map: + * @ls: pointer to mnt_optls instance + * @id: built-in map id (see mnt_get_builtin_map()) + * + * Same as mnt_optls_add_map(), but works with libmount built in maps. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_optls_add_builtin_map(mnt_optls *ls, int id) +{ + const struct mnt_optmap *m = mnt_get_builtin_optmap(id); + + assert(ls); + assert(id); + + return m ? mnt_optls_add_map(ls, m) : -1; +} + + +/* + * Append the option to "ls" container. + */ +static void mnt_optls_add_optent(mnt_optls *ls, mnt_optent *op) +{ + assert(ls); + assert(op); + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: add option %s", + ls, mnt_optent_get_name(op))); + + list_add_tail(&op->opts, &ls->opts); +} + +/** + * mnt_optls_add_option: + * @ls: pointer to mnt_optls instance + * @name: option name + * @value: option value + * + * Returns: new option or NULL in case of error. + */ +mnt_optent *mnt_optls_add_option(mnt_optls *ls, + const char *name, const char *value) +{ + mnt_optent *op; + + if (!ls || !name) + return NULL; + + op = mnt_new_optent(name, strlen(name), + value, value ? strlen(value) : 0, + ls->maps, ls->nmaps); + if (op) + mnt_optls_add_optent(ls, op); + return op; +} + +/** + * mnt_optls_parse_optstr: + * @ls: pointer to mnt_optls instance. + * @optstr: zero terminated string with mount options (comma separaed list) + * + * Parses @optstr and all options from @optstr are added to the @optls. It's + * possible to call this function more than once. The new options from @optstr + * will be appended to the container. + * + * The options are accessible by mnt_optls_next_option(). + * + * If the @optls container is associated with any options map(s), all new + * options are verified according to the descriptions from the map(s). + * + * For example: + * + * mnt_optls_parse_optstr(ls, "user=snake,noexec"); + * + * is same like: + * + * mnt_optls_add_option(ls, "user", "snake"); + * mnt_optls_add_option(ls, "noexec", NULL); + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_optls_parse_optstr(mnt_optls *ls, const char *optstr) +{ + char *p = (char *) optstr; + + assert(ls); + assert(optstr); + + if (!ls || !optstr) + return -1; + + while(p && *p) { + mnt_optent *op = mnt_new_optent_from_optstr(&p, + ls->maps, ls->nmaps); + if (!op) + return -1; + mnt_optls_add_optent(ls, op); + } + return 0; +} + +/** + * mnt_optls_remove_option: + * @ls: pointer to mnt_optls instance + * @name: option name + * + * Returns: 0 on success, 1 if @name not found and -1 in case of error. + */ +int mnt_optls_remove_option(mnt_optls *ls, const char *name) +{ + struct list_head *p, *pnext; + + if (!ls || !name) + return -1; + + list_for_each_safe(p, pnext, &ls->opts) { + mnt_optent *op; + const char *n; + + if (!p) + break; + op = list_entry(p, mnt_optent, opts); + n = mnt_optent_get_name(op); + if (n && strcmp(name, n) == 0) { + mnt_free_optent(op); + return 0; + } + } + return 1; +} + + +/** + * mnt_optls_remove_option_by_flags: + * @ls: pointer to mnt_optls instance + * @map: pointer to the map with wanted options or NULL for all options + * @flags: option flags + * + * Removes options which match with @flags. The set of options could + * be restricted by @map. For exmaple: + * + * mnt_optls_remove_option_by_flags(ls, NULL, MS_NOEXEC); + * + * removes "noexec" option from "ls". + * + * Note that this function is useles for options with MNT_INVERT mask (e.g. + * "exec" is inverting MS_NOEXEC flag). + * + * See also mnt_optent_get_flag() and mnt_optls_remove_option_by_iflags(). + * + * Returns: number of removed options or -1 in case of error. + */ +int mnt_optls_remove_option_by_flags(mnt_optls *ls, + const struct mnt_optmap *map, const int flags) +{ + struct list_head *p, *pnext; + int ct = 0; + + if (!ls) + return -1; + + list_for_each_safe(p, pnext, &ls->opts) { + mnt_optent *op; + int fl = 0; + + if (!p) + break; + op = list_entry(p, mnt_optent, opts); + + if (!map || mnt_optent_get_map(op) == map) { + mnt_optent_get_flag(op, &fl); + if (fl & flags) { + mnt_free_optent(op); + ct++; + } + } + } + return ct; +} + +/** + * mnt_optls_remove_option_by_iflags: + * @ls: pointer to mnt_optls instance + * @map: pointer to the map with wanted options or NULL for all options + * @flags: option flags + * + * Removes options which inverting any id from @flags. The set of options could + * be restricted by @map. For exmaple: + * + * mnt_optls_remove_option_by_iflags(ls, NULL, MS_NOEXEC); + * + * removes "exec" option from "ls". + * + * Note that this function is useles for options without MNT_INVERT mask (e.g. + * "noexec"). + * + * See also mnt_optent_get_flag() and mnt_optls_remove_option_by_flags(). + * + * Returns: number of removed options or -1 in case of error. + */ +int mnt_optls_remove_option_by_iflags(mnt_optls *ls, + const struct mnt_optmap *map, const int flags) +{ + struct list_head *p, *pnext; + int ct = 0; + + if (!ls) + return -1; + + list_for_each_safe(p, pnext, &ls->opts) { + mnt_optent *op; + int fl = flags; + + if (!p) + break; + op = list_entry(p, mnt_optent, opts); + + if (!map || mnt_optent_get_map(op) == map) { + int id = mnt_optent_get_id(op); + + if (!(id & fl)) + continue; + + mnt_optent_get_flag(op, &fl); + + if (!(id & fl)) { + mnt_free_optent(op); + ct++; + } + } + } + return ct; +} + +/** + * mnt_optls_next_option: + * @ls: pointer to mnt_optls instance + * @itr: iterator + * @map: pointer to the map of wanted options or NULL for all options + * @option: returns pointer to the option object + * + * Example (print all options): + * <informalexample> + * <programlisting>* + * mnt_optent *option; + * mnt_optls *ls = mnt_optls_new(); + * + * mnt_optls_parse_optstr(ls, "noexec,nodev"); + * + * while(mnt_optls_next_option(ls, itr, NULL, &option)) + * printf("%s\n", mnt_optent_get_name(option))); + * </programlisting> + * </informalexample> + * + * Returns: 0 on succes, -1 in case of error or 1 at end of list. + */ +int mnt_optls_next_option(mnt_optls *ls, mnt_iter *itr, + const struct mnt_optmap *map, mnt_optent **option) +{ + assert(itr); + assert(ls); + assert(option); + + if (!itr || !ls || !option) + return -1; + if (!itr->head) + MNT_ITER_INIT(itr, &ls->opts); + while (itr->p != itr->head) { + MNT_ITER_ITERATE(itr, *option, struct _mnt_optent, opts); + if (map == NULL || (*option)->map == map) + return 0; + } + + return 1; +} + +/** + * mnt_optls_get_option: + * @ls: pointer to mnt_optls instance + * @name: options name + * + * Returns: the option or NULL. + */ +mnt_optent *mnt_optls_get_option(mnt_optls *ls, const char *name) +{ + mnt_optent *op; + mnt_iter itr; + + assert(ls); + assert(name); + + if (!ls || !name) + return NULL; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) { + const char *n = mnt_optent_get_name(op); + + if (n && !strcmp(n, name)) + return op; + } + return NULL; +} + +/** + * mnt_optls_get_ids: + * @ls: pointer to mnt_optls instance + * @map: pointer to the map of wanted options or NULL for all options + * + * Note that ID has to be unique in all maps when the @map is NULL. + * + * Note also that this function works with ALL options -- see also + * mnt_optls_create_mountflags() that returns MNT_MFLAG options + * (mount(2) flags) only. + * + * Returns: IDs from all options. + */ +int mnt_optls_get_ids(mnt_optls *ls, const struct mnt_optmap *map) +{ + int flags = 0; + mnt_iter itr; + mnt_optent *op; + + assert(ls); + if (!ls) + return 0; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, map, &op) == 0) + mnt_optent_get_flag(op, &flags); + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generated IDs 0x%08x", ls, flags)); + return flags; +} + +/** + * mnt_optls_create_mountflags: + * @ls: pointer to mnt_optls instance + * + * The mountflags are IDs from all MNT_MFLAG options. See "struct mnt_optmap". + * For more details about mountflags see mount(2) syscall. + * + * Returns: mount flags or 0. + */ +int mnt_optls_create_mountflags(mnt_optls *ls) +{ + int flags = 0; + mnt_iter itr; + mnt_optent *op; + + assert(ls); + if (!ls) + return 0; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) { + if (!(op->mask & MNT_MFLAG)) + continue; + mnt_optent_get_flag(op, &flags); + } + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generated mountflags 0x%08x", ls, flags)); + return flags; +} + +/** + * mnt_optls_create_mountdata: + * @ls: pointer to mnt_optls instance + * + * For more details about mountdata see mount(2) syscall. + * + * Returns: newly allocated string with mount options or NULL in case of error. + */ +char *mnt_optls_create_mountdata(mnt_optls *ls) +{ + mnt_iter itr; + mnt_optent *op; + char *optstr = NULL; + + assert(ls); + if (!ls) + return NULL; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) { + if (!(op->mask & MNT_MDATA) && !mnt_optent_is_unknown(op)) + continue; + if (mnt_optstr_append_option(&optstr, + mnt_optent_get_name(op), + mnt_optent_get_value(op))) + goto err; + } + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generated mountdata: %s", ls, optstr)); + return optstr; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: ls %p: generate mountdata failed", ls)); + free(optstr); + return NULL; +} + +/** + * mnt_optls_create_mtab_optstr: + * @ls: pointer to mnt_optls instance + * + * Returns: newly allocated string with mount options for mtab. + */ +char *mnt_optls_create_mtab_optstr(mnt_optls *ls) +{ + mnt_iter itr; + mnt_optent *op; + char *optstr = NULL; + + assert(ls); + if (!ls) + return NULL; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) { + if (op->mask & MNT_NOMTAB) + continue; + if (mnt_optstr_append_option(&optstr, + mnt_optent_get_name(op), + mnt_optent_get_value(op))) + goto err; + } + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generated mtab options: %s", ls, optstr)); + return optstr; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generate mtab optstr failed", ls)); + free(optstr); + return NULL; +} + +/** + * mnt_optls_create_userspace_optstr: + * @ls: pointer to mnt_optls instance + * + * Returns: newly allocated string with mount options that are + * userspace specific (e.g. uhelper=,loop=). + */ +char *mnt_optls_create_userspace_optstr(mnt_optls *ls) +{ + mnt_iter itr; + mnt_optent *op; + char *optstr = NULL; + + assert(ls); + if (!ls) + return NULL; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) { + if (mnt_optent_is_unknown(op)) + continue; + if (op->mask & (MNT_MDATA | MNT_MFLAG | MNT_NOMTAB)) + continue; + if (mnt_optstr_append_option(&optstr, + mnt_optent_get_name(op), + mnt_optent_get_value(op))) + goto err; + } + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generated userspace-only options: %s", + ls, optstr)); + return optstr; +err: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: opts %p: generate userspace optstr failed", ls)); + free(optstr); + return NULL; +} + +/** + * mnt_optls_print_debug: + * @file: output + * @ls: pointer to mnt_optls instance + * + * Prints details about options container. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_optls_print_debug(mnt_optls *ls, FILE *file) +{ + mnt_iter itr; + mnt_optent *op; + + if (!ls) + return -1; + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + fprintf(file, "--- opts: %p\n", ls); + while(mnt_optls_next_option(ls, &itr, NULL, &op) == 0) + mnt_optent_print_debug(op, file); + + return 0; +} + +#ifdef TEST_PROGRAM +mnt_optls *mk_optls(const char *optstr) +{ + mnt_optls *ls = mnt_new_optls(); + if (!ls) + goto err; + + mnt_optls_add_builtin_map(ls, MNT_LINUX_MAP); + mnt_optls_add_builtin_map(ls, MNT_USERSPACE_MAP); + + if (mnt_optls_parse_optstr(ls, optstr) != 0) { + fprintf(stderr, "\tfailed to parse: %s\n", optstr); + goto err; + } + return ls; +err: + mnt_free_optls(ls); + return NULL; +} + +int test_parse(struct mtest *ts, int argc, char *argv[]) +{ + mnt_optls *ls = NULL; + int rc = -1; + + if (argc < 1) + goto done; + ls = mk_optls(argv[1]); + if (!ls) + goto done; + + mnt_optls_print_debug(ls, stdout); + rc = 0; +done: + mnt_free_optls(ls); + return rc; +} + +int test_flags(struct mtest *ts, int argc, char *argv[]) +{ + mnt_optls *ls = NULL; + int rc = -1; + int flags; + const struct mnt_optmap *map; + + if (argc < 1) + goto done; + ls = mk_optls(argv[1]); + if (!ls) + goto done; + + flags = mnt_optls_create_mountflags(ls); + printf("\tmount(2) flags: 0x%08x\n", flags); + + map = mnt_get_builtin_optmap(MNT_LINUX_MAP); + flags = mnt_optls_get_ids(ls, map); + printf("\tMNT_MAP_LINUX IDs: 0x%08x (map %p)\n", flags, map); + + map = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); + flags = mnt_optls_get_ids(ls, map); + printf("\tMNT_USERSPACE_MAP IDs: 0x%08x (map %p)\n", flags, map); + + rc = 0; +done: + mnt_free_optls(ls); + return rc; +} + +int test_data(struct mtest *ts, int argc, char *argv[]) +{ + mnt_optls *ls = NULL; + char *optstr; + int rc = -1; + + if (argc < 1) + goto done; + ls = mk_optls(argv[1]); + if (!ls) + goto done; + + optstr = mnt_optls_create_mountdata(ls); + printf("\tmount(2) data: '%s'\n", optstr); + free(optstr); + rc = 0; +done: + mnt_free_optls(ls); + return rc; +} + +int test_mtabstr(struct mtest *ts, int argc, char *argv[]) +{ + mnt_optls *ls = NULL; + char *optstr; + int rc = -1; + + if (argc < 1) + goto done; + ls = mk_optls(argv[1]); + if (!ls) + goto done; + + optstr = mnt_optls_create_mtab_optstr(ls); + printf("\tmtab options: '%s'\n", optstr); + free(optstr); + rc = 0; +done: + mnt_free_optls(ls); + return rc; +} + +int test_reparse(struct mtest *ts, int argc, char *argv[]) +{ + const struct mnt_optmap *map; + mnt_optls *ls = NULL; + char *optstr; + int rc = -1; + + if (argc < 1) + goto done; + optstr = argv[1]; + ls = mnt_new_optls(); + if (!ls) + goto done; + + /* add description for kernel options */ + mnt_optls_add_builtin_map(ls, MNT_LINUX_MAP); + + if (mnt_optls_parse_optstr(ls, optstr) != 0) { + fprintf(stderr, "\tfailed to parse: %s\n", optstr); + goto done; + } + + fprintf(stdout, "------ parse\n"); + mnt_optls_print_debug(ls, stdout); + + /* add description for userspace options */ + map = mnt_get_builtin_optmap(MNT_USERSPACE_MAP); + mnt_optls_add_map(ls, map); + + fprintf(stdout, "------ re-parse\n"); + mnt_optls_print_debug(ls, stdout); + + rc = 0; +done: + mnt_free_optls(ls); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct mtest tss[] = { + { "--parse", test_parse, "<optstr> parse mount options string" }, + { "--ls-data", test_data, "<optstr> parse and generate mountdata" }, + { "--ls-flags", test_flags, "<optstr> parse and generate mountflags" }, + { "--ls-mtabstr",test_mtabstr,"<optstr> parse and generate mtab options" }, + { "--reparse", test_reparse, "<optstr> test extra options reparsing" }, + { NULL } + }; + return mnt_run_test(tss, argc, argv); +} +#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/optmap.c b/shlibs/mount/src/optmap.c new file mode 100644 index 00000000..6eef332c --- /dev/null +++ b/shlibs/mount/src/optmap.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: optmap + * @title: Option maps + * @short_description: description for mount options + * + * The mount(2) linux syscall uses two arguments for mount options: + * + * @mountflags: (see MS_* macros in linux/fs.h) + * + * @mountdata: (usully a comma separated string of options) + * + * The libmount uses options-map(s) to describe mount options. The number of + * maps is unlimited. The libmount options parser could be easily extended + * (e.g. by mnt_optls_add_map()) to work with new options. + * + * The option description (map entry) includes: + * + * @name: and argument type (e.g. "loop[=%s]") + * + * @id: (in the map unique identifier or a mountflags, e.g MS_RDONLY) + * + * @mask: (MNT_INVERT, MNT_MDATA, MNT_MFLAG, MNT_NOMTAB) + * + * The option argument type is defined by: + * + * "=type" -- required argument + * + * "[=type]" -- optional argument + * + * where the 'type' is sscanf() format string or + * + * {item0,item1,...} -- enum (mnt_option_get_number() converts the value + * to 0..N number) + * + * The options argument format is used for parsing only. The library internally + * stores the option argument as a string. The conversion to the data type is + * on-demant by mnt_option_get_value_*() functions. + * + * The library checks options argument according to 'type' format for simple + * formats only: + * + * %s, %d, %ld, %lld, %u, %lu, %llu, %x, %o and {enum} + * + * Example: + * + * <informalexample> + * <programlisting> + * #define MY_MS_FOO (1 << 1) + * #define MY_MS_BAR (1 << 2) + * + * mnt_optmap myoptions[] = { + * { "foo", MY_MS_FOO, MNT_MFLAG }, + * { "nofoo", MY_MS_FOO, MNT_MFLAG | MNT_INVERT }, + * { "bar=%s",MY_MS_BAR, MNT_MDATA }, + * { NULL } + * }; + * </programlisting> + * </informalexample> + * + * The libmount defines two basic built-in options maps: + * + * @MNT_LINUX_MAP: fs-independent kernel mount options (usually MS_* flags) + * + * @MNT_USERSPACE_MAP: userspace specific mount options (e.g. "user", "loop") + * + * For more details about option map struct see "struct mnt_optmap" in + * mount/mount.h. + */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "nls.h" +#include "mountP.h" + +/* + * fs-independent mount flags (built-in MNT_LINUX_MAP) + */ +static const struct mnt_optmap linux_flags_map[] = +{ + { "ro", MS_RDONLY, MNT_MFLAG }, /* read-only */ + { "rw", MS_RDONLY, MNT_MFLAG | MNT_INVERT }, /* read-write */ + { "exec", MS_NOEXEC, MNT_MFLAG | MNT_INVERT }, /* permit execution of binaries */ + { "noexec", MS_NOEXEC, MNT_MFLAG }, /* don't execute binaries */ + { "suid", MS_NOSUID, MNT_MFLAG | MNT_INVERT }, /* honor suid executables */ + { "nosuid", MS_NOSUID, MNT_MFLAG }, /* don't honor suid executables */ + { "dev", MS_NODEV, MNT_MFLAG | MNT_INVERT }, /* interpret device files */ + { "nodev", MS_NODEV, MNT_MFLAG }, /* don't interpret devices */ + + { "sync", MS_SYNCHRONOUS, MNT_MFLAG }, /* synchronous I/O */ + { "async", MS_SYNCHRONOUS, MNT_MFLAG | MNT_INVERT }, /* asynchronous I/O */ + + { "dirsync", MS_DIRSYNC, MNT_MFLAG }, /* synchronous directory modifications */ + { "remount", MS_REMOUNT, MNT_MFLAG }, /* Alter flags of mounted FS */ + { "bind", MS_BIND, MNT_MFLAG }, /* Remount part of tree elsewhere */ + { "rbind", MS_BIND|MS_REC, MNT_MFLAG }, /* Idem, plus mounted subtrees */ +#ifdef MS_NOSUB + { "sub", MS_NOSUB, MNT_MFLAG | MNT_INVERT }, /* allow submounts */ + { "nosub", MS_NOSUB, MNT_MFLAG }, /* don't allow submounts */ +#endif +#ifdef MS_SILENT + { "quiet", MS_SILENT, MNT_MFLAG }, /* be quiet */ + { "loud", MS_SILENT, MNT_MFLAG | MNT_INVERT }, /* print out messages. */ +#endif +#ifdef MS_MANDLOCK + { "mand", MS_MANDLOCK, MNT_MFLAG }, /* Allow mandatory locks on this FS */ + { "nomand", MS_MANDLOCK, MNT_MFLAG | MNT_INVERT },/* Forbid mandatory locks on this FS */ +#endif +#ifdef MS_NOATIME + { "atime", MS_NOATIME, MNT_MFLAG | MNT_INVERT }, /* Update access time */ + { "noatime", MS_NOATIME, MNT_MFLAG }, /* Do not update access time */ +#endif +#ifdef MS_I_VERSION + { "iversion", MS_I_VERSION, MNT_MFLAG }, /* Update inode I_version time */ + { "noiversion", MS_I_VERSION, MNT_MFLAG | MNT_INVERT}, /* Don't update inode I_version time */ +#endif +#ifdef MS_NODIRATIME + { "diratime", MS_NODIRATIME, MNT_MFLAG | MNT_INVERT }, /* Update dir access times */ + { "nodiratime", MS_NODIRATIME, MNT_MFLAG }, /* Do not update dir access times */ +#endif +#ifdef MS_RELATIME + { "relatime", MS_RELATIME, MNT_MFLAG }, /* Update access times relative to mtime/ctime */ + { "norelatime", MS_RELATIME, MNT_MFLAG | MNT_INVERT }, /* Update access time without regard to mtime/ctime */ +#endif +#ifdef MS_STRICTATIME + { "strictatime", MS_STRICTATIME, MNT_MFLAG }, /* Strict atime semantics */ + { "nostrictatime", MS_STRICTATIME, MNT_MFLAG | MNT_INVERT }, /* kernel default atime */ +#endif + { NULL, 0, 0 } +}; + +/* + * userspace mount option (built-in MNT_USERSPACE_MAP) + */ +static const struct mnt_optmap userspace_opts_map[] = +{ + { "defaults", MNT_MS_DFLTS, MNT_NOMTAB }, /* default options */ + + { "auto", MNT_MS_NOAUTO, MNT_INVERT | MNT_NOMTAB }, /* Can be mounted using -a */ + { "noauto", MNT_MS_NOAUTO, MNT_NOMTAB }, /* Can only be mounted explicitly */ + + { "user[=%s]", MNT_MS_USER }, /* Allow ordinary user to mount (mtab) */ + { "nouser", MNT_MS_USER, MNT_INVERT | MNT_NOMTAB }, /* Forbid ordinary user to mount */ + + { "users", MNT_MS_USERS, MNT_NOMTAB }, /* Allow ordinary users to mount */ + { "nousers", MNT_MS_USERS, MNT_INVERT | MNT_NOMTAB }, /* Forbid ordinary users to mount */ + + { "owner", MNT_MS_OWNER, MNT_NOMTAB }, /* Let the owner of the device mount */ + { "noowner", MNT_MS_OWNER, MNT_INVERT | MNT_NOMTAB }, /* Device owner has no special privs */ + + { "group", MNT_MS_GROUP, MNT_NOMTAB }, /* Let the group of the device mount */ + { "nogroup", MNT_MS_GROUP, MNT_INVERT | MNT_NOMTAB }, /* Device group has no special privs */ + + { "_netdev", MNT_MS_NETDEV }, /* Device requires network */ + + { "comment=%s", MNT_MS_COMMENT, MNT_NOMTAB }, /* fstab comment only */ + + { "loop[=%s]", MNT_MS_LOOP }, /* use the loop device */ + + { "nofail", MNT_MS_NOFAIL, MNT_NOMTAB }, /* Do not fail if ENOENT on dev */ + + { NULL, 0, 0 } +}; + +/** + * mnt_get_builtin_map: + * @id: map id -- MNT_LINUX_MAP or MNT_USERSPACE_MAP + * + * MNT_LINUX_MAP - Linux kernel fs-independent mount options + * (usually MS_* flags, see linux/fs.h) + * + * MNT_USERSPACE_MAP - userpace mount(8) specific mount options + * (e.g user=, _netdev, ...) + * + * Returns: static built-in libmount map. + */ +const struct mnt_optmap *mnt_get_builtin_optmap(int id) +{ + assert(id); + + if (id == MNT_LINUX_MAP) + return linux_flags_map; + else if (id == MNT_USERSPACE_MAP) + return userspace_opts_map; + return NULL; +} + +/* + * Lookups for the @name in @maps and returns a map and in @mapent + * returns the map entry + */ +const struct mnt_optmap *mnt_optmap_get_entry( + struct mnt_optmap const **maps, + int nmaps, + const char *name, + size_t namelen, + const struct mnt_optmap **mapent) +{ + int i; + + assert(maps); + assert(nmaps); + assert(name); + assert(namelen); + assert(mapent); + + *mapent = NULL; + + for (i = 0; i < nmaps; i++) { + const struct mnt_optmap *map = maps[i]; + const struct mnt_optmap *ent; + const char *p; + + for (ent = map; ent && ent->name; ent++) { + if (strncmp(ent->name, name, namelen)) + continue; + p = ent->name + namelen; + if (*p == '\0' || *p == '=' || *p == '[') { + *mapent = ent; + return map; + } + } + } + return NULL; +} + + +/* + * Converts @rawdata to number according to enum definition in the @mapent. + */ +int mnt_optmap_enum_to_number(const struct mnt_optmap *mapent, + const char *rawdata, size_t len) +{ + const char *p, *end = NULL, *begin = NULL; + int n = -1; + + if (!rawdata || !*rawdata || !mapent || !len) + return -1; + + p = strrchr(mapent->name, '='); + if (!p || *(p + 1) == '{') + return -1; /* value unexpected or not "enum" */ + p += 2; + if (!*p || *(p + 1) == '}') + return -1; /* hmm... option <type> is "={" or "={}" */ + + /* we cannot use strstr(), @rawdata is not terminated */ + for (; p && *p; p++) { + if (!begin) + begin = p; /* begin of the item */ + if (*p == ',') + end = p; /* terminate the item */ + if (*(p + 1) == '}') + end = p + 1; /* end of enum definition */ + if (!begin || !end) + continue; + if (end <= begin) + return -1; + n++; + if (len == end - begin && strncasecmp(begin, rawdata, len) == 0) + return n; + p = end; + } + + return -1; +} + +/* + * Returns data type defined in the @mapent. + */ +const char *mnt_optmap_get_type(const struct mnt_optmap *mapent) +{ + char *type; + + assert(mapent); + assert(mapent->name); + + type = strrchr(mapent->name, '='); + if (!type) + return NULL; /* value is unexpected */ + if (type == mapent->name) + return NULL; /* wrong format of type definition */ + type++; + if (*type != '%' && *type != '{') + return NULL; /* wrong format of type definition */ + return type ? : NULL; +} + +/* + * Does the option (that is described by @mntent) require any value? (e.g. + * uid=<foo>) + */ +int mnt_optmap_require_value(const struct mnt_optmap *mapent) +{ + char *type; + + assert(mapent); + assert(mapent->name); + + type = strchr(mapent->name, '='); + if (!type) + return 0; /* value is unexpected */ + if (type == mapent->name) + return 0; /* wrong format of type definition */ + if (*(type - 1) == '[') + return 0; /* optional */ + return 1; +} diff --git a/shlibs/mount/src/optstr.c b/shlibs/mount/src/optstr.c new file mode 100644 index 00000000..19efbf3f --- /dev/null +++ b/shlibs/mount/src/optstr.c @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: optstr + * @title: Options string + * @short_description: low-level API for work with mount options + * + * This is simple and low-level API to work with mount options that are stored + * in string. This API is independent on the high-level options container and + * option maps. + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> + +#include "nls.h" +#include "mountP.h" + +/* + * Parses the first option from @optstr. The @optstr pointer is set to begin of + * the next option. + * + * Returns -1 on parse error, 1 at the end of optstr and 0 on success. + */ +static int mnt_optstr_parse_next(char **optstr, char **name, size_t *namesz, + char **value, size_t *valsz) +{ + int open_quote = 0; + char *start = NULL, *stop = NULL, *p, *sep = NULL; + char *optstr0; + + assert(optstr); + assert(*optstr); + + optstr0 = *optstr; + + if (name) + *name = NULL; + if (namesz) + *namesz = 0; + if (value) + *value = NULL; + if (valsz) + *valsz = 0; + + for (p = optstr0; p && *p; p++) { + if (!start) + start = p; /* begin of the option item */ + if (*p == '"') + open_quote ^= 1; /* reverse the status */ + if (open_quote) + continue; /* still in quoted block */ + if (!sep && *p == '=') + sep = p; /* name and value separator */ + if (*p == ',') + stop = p; /* terminate the option item */ + else if (*(p + 1) == '\0') + stop = p + 1; /* end of optstr */ + if (!start || !stop) + continue; + if (stop <= start) + goto error; + + if (name) + *name = start; + if (namesz) + *namesz = sep ? sep - start : stop - start; + *optstr = *stop ? stop + 1 : stop; + + if (sep) { + if (value) + *value = sep + 1; + if (valsz) + *valsz = stop - sep - 1; + } + return 0; + } + + return 1; /* end of optstr */ + +error: + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: parse error: \"%s\"\n", optstr0)); + return -1; +} + +/* + * Locates the first option that match with @name. The @end is set to + * char behind the option (it means ',' or \0). + * + * Returns -1 on parse error, 1 when not found and 0 on success. + */ +static int mnt_optstr_locate_option(char *optstr, const char *name, char **begin, + char **end, char **value, size_t *valsz) +{ + char *n; + size_t namesz, nsz; + int rc; + + assert(name); + assert(optstr); + + namesz = strlen(name); + + do { + rc = mnt_optstr_parse_next(&optstr, &n, &nsz, value, valsz); + if (rc) + break; + + if (namesz == nsz && strncmp(n, name, nsz) == 0) { + if (begin) + *begin = n; + if (end) + *end = *(optstr - 1) == ',' ? + optstr - 1 : optstr; + return 0; + } + } while(1); + + DBG(DEBUG_OPTIONS, fprintf(stderr, + "libmount: can't found '%s' option\n", name)); + return rc; +} + +/** + * mnt_optstr_next_option: + * @optstr: option string, returns position to next option + * @name: returns option name + * @namesz: returns option name length + * @value: returns option value or NULL + * @valuesz: returns option value length or zero + * + * Parses the first option in @optstr or -1 in case of error. + * + * Returns: 0 on success, 1 at the end of @optstr or -1 in case of error. + */ +int mnt_optstr_next_option(char **optstr, char **name, size_t *namesz, + char **value, size_t *valuesz) +{ + if (!optstr || !*optstr) + return -1; + return mnt_optstr_parse_next(optstr, name, namesz, value, valuesz); +} + +/** + * mnt_optstr_append_option: + * @optstr: option string or NULL + * @name: value name + * @value: value + * + * Returns: reallocated (or newly allocated) @optstr with ,name=value + */ +int mnt_optstr_append_option(char **optstr, const char *name, const char *value) +{ + char *p; + size_t sz, vsz, osz, nsz; + + if (!name) + return -1; + + osz = *optstr ? strlen(*optstr) : 0; + nsz = strlen(name); + vsz = value ? strlen(value) : 0; + + sz = osz + nsz + 1; /* 1: '\0' */ + if (osz) + sz++; /* ',' options separator */ + if (vsz) + sz += vsz + 1; /* 1: '=' */ + + p = realloc(*optstr, sz); + if (!p) + return -1; + *optstr = p; + + if (osz) { + p += osz; + *p++ = ','; + } + + memcpy(p, name, nsz); + p += nsz; + + if (vsz) { + *p++ = '='; + memcpy(p, value, vsz); + p += vsz; + } + *p = '\0'; + + return 0; +} + +/** + * mnt_optstr_get_option: + * @optstr: string with comma separated list of options + * @name: requested option name + * @value: returns pointer to the begin of the value (e.g. name=VALUE) or NULL + * @valsz: returns size of the value or 0 + * + * Returns: 0 on success, 1 when not found the @name or -1 in case of error. + */ +int mnt_optstr_get_option(char *optstr, const char *name, + char **value, size_t *valsz) +{ + return mnt_optstr_locate_option(optstr, name, NULL, NULL, value, valsz); +} + +/* Removes substring located between @begin and @end from @str + * -- result never starts or ends with comma or contains two commas + * (e.g. ",aaa,bbb" or "aaa,,bbb" or "aaa,") + */ +static void remove_substring(char *str, char *begin, char *end) +{ + size_t sz = strlen(end); + + if ((begin == str || *(begin - 1) == ',') && *end == ',') + end++; + + memmove(begin, end, sz + 1); + if (!*begin && *(begin - 1) == ',') + *(begin - 1) = '\0'; +} + +/* insert '=substr' to @str on position @pos */ +static int insert_substring(char **str, char *pos, const char *substr) +{ + char *p; + size_t ssz = strlen(substr); /* substring size */ + + p = realloc(*str, strlen(*str) + 1 + ssz); + if (!p) + return -1; + *str = p; + + memmove(pos + ssz + 1, pos, strlen(pos) + 1); + *pos++ = '='; + memcpy(pos, substr, ssz); + return 0; +} + +/** + * mnt_optstr_set_option: + * @optstr: string with comma separated list of options + * @name: requested option + * @value: new value or NULL + * + * Set or unset option @value. + * + * Returns: 0 on success, 1 when not found the @name or -1 in case of error. + */ +int mnt_optstr_set_option(char **optstr, const char *name, const char *value) +{ + char *val = NULL, *begin, *end, *nameend; + size_t valsz; + int rc = 1; + + if (!optstr) + return -1; + if (*optstr) + rc = mnt_optstr_locate_option(*optstr, name, + &begin, &end, &val, &valsz); + if (rc == -1) + /* parse error */ + return -1; + if (rc == 1) + /* not found */ + return mnt_optstr_append_option(optstr, name, value); + + nameend = begin + strlen(name); + + if (value == NULL && val && valsz) + /* remove unwanted "=value" */ + remove_substring(*optstr, nameend, end); + + else if (value && val == NULL) + /* insert "=value" */ + rc = insert_substring(optstr, nameend, value); + + else if (value && val && strlen(value) == valsz) + /* simply replace =value */ + memcpy(val, value, valsz); + + else if (value && val) { + remove_substring(*optstr, nameend, end); + rc = insert_substring(optstr, nameend, value); + } + + + return 0; +} + +/** + * mnt_optstr_remove_option: + * @optstr: string with comma separated list of options + * @name: requested option name + * + * Returns: 0 on success, 1 when not found the @name or -1 in case of error. + */ +int mnt_optstr_remove_option(char **optstr, const char *name) +{ + char *begin, *end; + int rc; + + rc = mnt_optstr_locate_option(*optstr, name, + &begin, &end, NULL, NULL); + if (rc != 0) + return rc; + + remove_substring(*optstr, begin, end); + return 0; +} + +#ifdef TEST_PROGRAM + +int test_append(struct mtest *ts, int argc, char *argv[]) +{ + const char *value = NULL, *name; + char *optstr; + + if (argc < 3) + goto done; + optstr = strdup(argv[1]); + name = argv[2]; + + if (argc == 4) + value = argv[3]; + + if (mnt_optstr_append_option(&optstr, name, value) == 0) { + printf("result: >%s<\n", optstr); + return 0; + } +done: + return -1; +} + +int test_set(struct mtest *ts, int argc, char *argv[]) +{ + const char *value = NULL, *name; + char *optstr; + + if (argc < 3) + goto done; + optstr = strdup(argv[1]); + name = argv[2]; + + if (argc == 4) + value = argv[3]; + + if (mnt_optstr_set_option(&optstr, name, value) == 0) { + printf("result: >%s<\n", optstr); + return 0; + } +done: + return -1; +} + +int test_get(struct mtest *ts, int argc, char *argv[]) +{ + char *optstr; + const char *name; + char *val = NULL; + size_t sz = 0; + int rc; + + if (argc < 2) + goto done; + optstr = argv[1]; + name = argv[2]; + + rc = mnt_optstr_get_option(optstr, name, &val, &sz); + if (rc == 0) { + printf("found; name: %s", name); + if (sz) { + printf(", argument: size=%zd data=", sz); + if (fwrite(val, 1, sz, stdout) != sz) + goto done; + } + printf("\n"); + return 0; + } else if (rc == 1) + printf("%s: not found\n", name); + else + printf("parse error: %s\n", optstr); +done: + return -1; +} + +int test_remove(struct mtest *ts, int argc, char *argv[]) +{ + const char *name; + char *optstr; + + if (argc < 3) + goto done; + optstr = strdup(argv[1]); + name = argv[2]; + + if (mnt_optstr_remove_option(&optstr, name) == 0) { + printf("result: >%s<\n", optstr); + return 0; + } +done: + return -1; +} + + +int main(int argc, char *argv[]) +{ + struct mtest tss[] = { + { "--append", test_append, "<optstr> <name> [<value>] append value to optstr" }, + { "--set", test_set, "<optstr> <name> [<value>] (un)set value" }, + { "--get", test_get, "<optstr> <name> search name in optstr" }, + { "--remove", test_remove, "<optstr> <name> remove name in optstr" }, + { NULL } + }; + return mnt_run_test(tss, argc, argv); +} +#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/tab.c b/shlibs/mount/src/tab.c new file mode 100644 index 00000000..cb6a91d3 --- /dev/null +++ b/shlibs/mount/src/tab.c @@ -0,0 +1,929 @@ +/* + * Copyright (C) 2008-2010 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: tab + * @title: FS container + * @short_description: container for entries from fstab/mtab/mountinfo + * + * + * Note that mnt_tab_find_* functions are mount(8) compatible. These functions + * try to found an entry in more iterations where the first attempt is always + * based on comparison with unmodified (non-canonicalized or un-evaluated) + * paths or tags. For example fstab with two entries: + * <informalexample> + * <programlisting> + * LABEL=foo /foo auto rw + * /dev/foo /foo auto rw + * </programlisting> + * </informalexample> + * + * where both lines are used for the *same* device, then + * <informalexample> + * <programlisting> + * mnt_tab_find_source(tb, "/dev/foo", &fs); + * </programlisting> + * </informalexample> + * will returns the second line, and + * <informalexample> + * <programlisting> + * mnt_tab_find_source(tb, "LABEL=foo", &fs); + * </programlisting> + * </informalexample> + * will returns the first entry, and + * <informalexample> + * <programlisting> + * mnt_tab_find_source(tb, "UUID=anyuuid", &fs); + * </programlisting> + * </informalexample> + * will returns the first entry (if UUID matches with the device). + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <blkid.h> + +#include "nls.h" +#include "mountP.h" +#include "c.h" + +/** + * mnt_new_tab: + * @filename: file name or NULL + * + * The tab is a container for mnt_fs entries that usually represents a fstab, + * mtab or mountinfo file from your system. + * + * Note that this function does not parse the file. See also + * mnt_tab_parse_file(). + * + * Returns: newly allocated tab struct. + */ +mnt_tab *mnt_new_tab(const char *filename) +{ + mnt_tab *tb = NULL; + + tb = calloc(1, sizeof(struct _mnt_tab)); + if (!tb) + goto err; + + if (filename) { + tb->filename = strdup(filename); + if (!tb->filename) + goto err; + } + INIT_LIST_HEAD(&tb->ents); + return tb; +err: + free(tb); + return NULL; +} + +/** + * mnt_free_tab: + * @tb: tab pointer + * + * Deallocates tab struct and all entries. + */ +void mnt_free_tab(mnt_tab *tb) +{ + if (!tb) + return; + free(tb->filename); + + while (!list_empty(&tb->ents)) { + mnt_fs *fs = list_entry(tb->ents.next, mnt_fs, ents); + mnt_free_fs(fs); + } + + free(tb); +} + +/** + * mnt_tab_get_nents: + * @tb: pointer to tab + * + * Returns: number of valid entries in tab. + */ +int mnt_tab_get_nents(mnt_tab *tb) +{ + assert(tb); + return tb ? tb->nents : 0; +} + +/** + * mnt_tab_set_cache: + * @tb: pointer to tab + * @mpc: pointer to mnt_cache instance + * + * Setups a cache for canonicalized paths and evaluated tags (LABEL/UUID). The + * cache is recommended for mnt_tab_find_*() functions. + * + * The cache could be shared between more tabs. Be careful when you share the + * same cache between more threads -- currently the cache does not provide any + * locking method. + * + * See also mnt_new_cache(). + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_tab_set_cache(mnt_tab *tb, mnt_cache *mpc) +{ + assert(tb); + if (!tb) + return -1; + tb->cache = mpc; + return 0; +} + +/** + * mnt_tab_get_cache: + * @tb: pointer to tab + * + * Returns: pointer to mnt_cache instance or NULL. + */ +mnt_cache *mnt_tab_get_cache(mnt_tab *tb) +{ + assert(tb); + return tb ? tb->cache : NULL; +} + +/** + * mnt_tab_get_name: + * @tb: tab pointer + * + * Returns: tab filename or NULL. + */ +const char *mnt_tab_get_name(mnt_tab *tb) +{ + assert(tb); + return tb ? tb->filename : NULL; +} + +/** + * mnt_tab_add_fs: + * @tb: tab pointer + * @fs: new entry + * + * Adds a new entry to tab. + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_tab_add_fs(mnt_tab *tb, mnt_fs *fs) +{ + assert(tb); + assert(fs); + + if (!tb || !fs) + return -1; + + list_add_tail(&fs->ents, &tb->ents); + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: add entry: %s %s\n", + tb->filename, mnt_fs_get_source(fs), + mnt_fs_get_target(fs))); + + if (fs->flags & MNT_FS_ERROR) + tb->nerrs++; + else + tb->nents++; + return 0; +} + +/** + * mnt_tab_remove_fs: + * @tb: tab pointer + * @fs: new entry + * + * Returns: 0 on success or -1 in case of error. + */ +int mnt_tab_remove_fs(mnt_tab *tb, mnt_fs *fs) +{ + assert(tb); + assert(fs); + + if (!tb || !fs) + return -1; + + list_del(&fs->ents); + + if (fs->flags & MNT_FS_ERROR) + tb->nerrs--; + else + tb->nents--; + return 0; +} + +/** + * mnt_tab_get_root_fs: + * @tb: mountinfo file (/proc/self/mountinfo) + * @root: returns pointer to the root filesystem (/) + * + * Returns: 0 on success or -1 case of error. + */ +int mnt_tab_get_root_fs(mnt_tab *tb, mnt_fs **root) +{ + mnt_iter itr; + mnt_fs *fs; + int root_id = 0; + + assert(tb); + assert(root); + + if (!tb || !root) + return -1; + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup root fs\n", tb->filename)); + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + int id = mnt_fs_get_parent_id(fs); + if (!id) + break; /* @tab is not mountinfo file? */ + + if (!*root || id < root_id) { + *root = fs; + root_id = id; + } + } + + return root_id ? 0 : -1; +} + +/** + * mnt_tab_next_child_fs: + * @tb: mountinfo file (/proc/self/mountinfo) + * @itr: iterator + * @parent: parental FS + * @chld: returns the next child filesystem + * + * Note that filesystems are returned in the order how was mounted (according to + * IDs in /proc/self/mountinfo). + * + * Returns: 0 on success, -1 in case of error or 1 at end of list. + */ +int mnt_tab_next_child_fs(mnt_tab *tb, mnt_iter *itr, + mnt_fs *parent, mnt_fs **chld) +{ + mnt_fs *fs; + int parent_id, lastchld_id = 0, chld_id = 0; + + if (!tb || !itr || !parent) + return -1; + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup next child of %s\n", + tb->filename, mnt_fs_get_target(parent))); + + parent_id = mnt_fs_get_id(parent); + if (!parent_id) + return -1; + + /* get ID of the previously returned child */ + if (itr->head && itr->p != itr->head) { + MNT_ITER_ITERATE(itr, fs, struct _mnt_fs, ents); + lastchld_id = mnt_fs_get_id(fs); + } + + *chld = NULL; + + mnt_reset_iter(itr, MNT_ITER_FORWARD); + while(mnt_tab_next_fs(tb, itr, &fs) == 0) { + int id; + + if (mnt_fs_get_parent_id(fs) != parent_id) + continue; + + id = mnt_fs_get_id(fs); + + if ((!lastchld_id || id > lastchld_id) && + (!*chld || id < chld_id)) { + *chld = fs; + chld_id = id; + } + } + + if (!chld_id) + return 1; /* end of iterator */ + + /* set the iterator to the @chld for the next call */ + mnt_tab_set_iter(tb, itr, *chld); + + return 0; +} + +/** + * mnt_tab_next_fs: + * @tb: tab pointer + * @itr: iterator + * @fs: returns the next tab entry + * + * Returns: 0 on success, -1 in case of error or 1 at end of list. + * + * Example (list all mountpoints from fstab in backward order): + * <informalexample> + * <programlisting> + * mnt_fs *fs; + * mnt_tab *tb = mnt_new_tab("/etc/fstab"); + * mnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD); + * + * mnt_tab_parse_file(tb); + * + * while(mnt_tab_next_fs(tb, itr, &fs) == 0) { + * const char *dir = mnt_fs_get_target(fs); + * printf("mount point: %s\n", dir); + * } + * mnt_free_tab(fi); + * </programlisting> + * </informalexample> + */ +int mnt_tab_next_fs(mnt_tab *tb, mnt_iter *itr, mnt_fs **fs) +{ + int rc; + + assert(tb); + assert(itr); + assert(fs); + + if (!tb || !itr || !fs) + return -1; +again: + rc = 1; + if (!itr->head) + MNT_ITER_INIT(itr, &tb->ents); + if (itr->p != itr->head) { + MNT_ITER_ITERATE(itr, *fs, struct _mnt_fs, ents); + rc = 0; + } + + /* ignore broken entries */ + if (*fs && ((*fs)->flags & MNT_FS_ERROR)) + goto again; + + return rc; +} + +/** + * mnt_tab_find_next_fs: + * @tb: table + * @itr: iterator + * @match_func: function returns 1 or 0 + * @userdata: extra data for match_func + * @fs: returns pointer to the next matching table entry + * + * This function allows search in @tb. + * + * Returns: -1 in case of error, 1 at end of table or 0 o success. + */ +int mnt_tab_find_next_fs(mnt_tab *tb, mnt_iter *itr, + int (*match_func)(mnt_fs *, void *), void *userdata, + mnt_fs **fs) +{ + if (!tb || !itr || !fs || !match_func) + return -1; + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup next fs\n", tb->filename)); + + if (!itr->head) + MNT_ITER_INIT(itr, &tb->ents); + + do { + if (itr->p != itr->head) + MNT_ITER_ITERATE(itr, *fs, struct _mnt_fs, ents); + else + break; /* end */ + + if ((*fs)->flags & MNT_FS_ERROR) + continue; + if (match_func(*fs, userdata)) + return 0; + } while(1); + + *fs = NULL; + return 1; +} + +/** + * mnt_tab_set_iter: + * @tb: tab pointer + * @itr: iterator + * @fs: tab entry + * + * Sets @iter to the position of @fs in the file @tb. + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_tab_set_iter(mnt_tab *tb, mnt_iter *itr, mnt_fs *fs) +{ + assert(tb); + assert(itr); + assert(fs); + + if (!tb || !itr || !fs) + return -1; + + MNT_ITER_INIT(itr, &tb->ents); + itr->p = &fs->ents; + + return 0; +} + +/** + * mnt_tab_find_target: + * @tb: tab pointer + * @path: mountpoint directory + * @direction: MNT_ITER_{FORWARD,BACKWARD} + * + * Try to lookup an entry in given tab, possible are three iterations, first + * with @path, second with realpath(@path) and third with realpath(@path) + * against realpath(fs->target). The 2nd and 3rd iterations are not performed + * when @tb cache is not set (see mnt_tab_set_cache()). + * + * Returns: a tab entry or NULL. + */ +mnt_fs *mnt_tab_find_target(mnt_tab *tb, const char *path, int direction) +{ + mnt_iter itr; + mnt_fs *fs = NULL; + char *cn; + + assert(tb); + assert(path); + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup target: %s\n", tb->filename, path)); + + /* native @target */ + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) + if (fs->target && strcmp(fs->target, path) == 0) + return fs; + + if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) + return NULL; + + /* canonicalized paths in mnt_tab */ + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + if (fs->target && strcmp(fs->target, cn) == 0) + return fs; + } + + /* non-canonicaled path in mnt_tab */ + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + char *p; + if (!fs->target) + continue; + p = mnt_resolve_path(fs->target, tb->cache); + if (strcmp(cn, p) == 0) + return fs; + } + return NULL; +} + +/** + * mnt_tab_find_srcpath: + * @tb: tab pointer + * @path: source path (devname or dirname) + * @direction: MNT_ITER_{FORWARD,BACKWARD} + * + * Try to lookup an entry in given tab, possible are four iterations, first + * with @path, second with realpath(@path), third with tags (LABEL, UUID, ..) + * from @path and fourth with realpath(@path) against realpath(entry->srcpath). + * + * The 2nd, 3rd and 4th iterations are not performed when @tb cache is not + * set (see mnt_tab_set_cache()). + * + * Returns: a tab entry or NULL. + */ +mnt_fs *mnt_tab_find_srcpath(mnt_tab *tb, const char *path, int direction) +{ + mnt_iter itr; + mnt_fs *fs = NULL; + int ntags = 0; + char *cn; + const char *p; + + assert(tb); + assert(path); + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup srcpath: %s\n", tb->filename, path)); + + /* native paths */ + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + p = mnt_fs_get_srcpath(fs); + if (p && strcmp(p, path) == 0) + return fs; + if (!p) + /* mnt_fs_get_srcpath() returs nothing, it's TAG */ + ntags++; + } + + if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) + return NULL; + + /* canonicalized paths in mnt_tab */ + if (ntags < mnt_tab_get_nents(tb)) { + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + p = mnt_fs_get_srcpath(fs); + if (p && strcmp(p, cn) == 0) + return fs; + } + } + + /* evaluated tag */ + if (ntags) { + int rc = mnt_cache_read_tags(tb->cache, cn); + + mnt_reset_iter(&itr, direction); + + if (rc == 0) { + /* @path's TAGs are in the cache */ + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + const char *t, *v; + + if (mnt_fs_get_tag(fs, &t, &v)) + continue; + + if (mnt_cache_device_has_tag(tb->cache, cn, t, v)) + return fs; + } + } else if (rc < 0 && errno == EACCES) { + /* @path is unaccessible, try evaluate all TAGs in @tb + * by udev symlinks -- this could be expensive on systems + * with huge fstab/mtab */ + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + const char *t, *v, *x; + if (mnt_fs_get_tag(fs, &t, &v)) + continue; + x = mnt_resolve_tag(t, v, tb->cache); + if (x && !strcmp(x, cn)) + return fs; + } + } + } + + /* non-canonicalized paths in mnt_tab */ + if (ntags <= mnt_tab_get_nents(tb)) { + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + p = mnt_fs_get_srcpath(fs); + if (p) + p = mnt_resolve_path(p, tb->cache); + if (p && strcmp(cn, p) == 0) + return fs; + } + } + + return NULL; +} + + +/** + * mnt_tab_find_tag: + * @tb: tab pointer + * @tag: tag name (e.g "LABEL", "UUID", ...) + * @val: tag value + * @direction: MNT_ITER_{FORWARD,BACKWARD} + * + * Try to lookup an entry in given tab, first attempt is lookup by @tag and + * @val, for the second attempt the tag is evaluated (converted to the device + * name) and mnt_tab_find_srcpath() is preformed. The second attempt is not + * performed when @tb cache is not set (see mnt_tab_set_cache()). + + * Returns: a tab entry or NULL. + */ +mnt_fs *mnt_tab_find_tag(mnt_tab *tb, const char *tag, + const char *val, int direction) +{ + mnt_iter itr; + mnt_fs *fs = NULL; + + assert(tb); + assert(tag); + assert(val); + + if (!tb || !tag || !val) + return NULL; + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup by TAG: %s %s\n", tb->filename, tag, val)); + + /* look up by TAG */ + mnt_reset_iter(&itr, direction); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + if (fs->tagname && fs->tagval && + strcmp(fs->tagname, tag) == 0 && + strcmp(fs->tagval, val) == 0) + return fs; + } + + if (tb->cache) { + /* look up by device name */ + char *cn = mnt_resolve_tag(tag, val, tb->cache); + if (cn) + return mnt_tab_find_srcpath(tb, cn, direction); + } + return NULL; +} + +/** + * mnt_tab_find_source: + * @tb: tab pointer + * @source: TAG or path + * @direction: MNT_ITER_{FORWARD,BACKWARD} + * + * This is high-level API for mnt_tab_find_{srcpath,tag}. You needn't to care + * about @source format (device, LABEL, UUID, ...). This function parses @source + * and calls mnt_tab_find_tag() or mnt_tab_find_srcpath(). + * + * Returns: a tab entry or NULL. + */ +mnt_fs *mnt_tab_find_source(mnt_tab *tb, const char *source, int direction) +{ + mnt_fs *fs = NULL; + + assert(tb); + assert(source); + + if (!tb || !source) + return NULL; + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: lookup SOURCE: %s\n", tb->filename, source)); + + if (strchr(source, '=')) { + char *tag, *val; + + if (blkid_parse_tag_string(source, &tag, &val) == 0) { + + fs = mnt_tab_find_tag(tb, tag, val, direction); + + free(tag); + free(val); + } + } else + fs = mnt_tab_find_srcpath(tb, source, direction); + + return fs; +} + + +/** + * mnt_tab_fprintf: + * @tb: tab pointer + * @f: FILE + * @fmt: per line printf-like format string (see MNT_TAB_PRINTFMT) + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_tab_fprintf(mnt_tab *tb, FILE *f, const char *fmt) +{ + mnt_iter itr; + mnt_fs *fs; + + assert(f); + assert(fmt); + assert(tb); + + if (!f || !fmt || !tb) + return -1; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { + if (mnt_fs_fprintf(fs, f, fmt) == -1) + return -1; + } + + return 0; +} + +/** + * mnt_tab_update_file + * @tb: tab pointer + * + * Writes tab to disk. Don't forget to lock the file (see mnt_lock()). + * + * Returns: 0 on success, -1 in case of error. + */ +int mnt_tab_update_file(mnt_tab *tb) +{ + FILE *f = NULL; + char tmpname[PATH_MAX]; + const char *filename; + struct stat st; + int fd; + + assert(tb); + if (!tb) + goto error; + + filename = mnt_tab_get_name(tb); + if (!filename) + goto error; + + if (snprintf(tmpname, sizeof(tmpname), "%s.tmp", filename) + >= sizeof(tmpname)) + goto error; + + f = fopen(tmpname, "w"); + if (!f) + goto error; + + if (mnt_tab_fprintf(tb, f, MNT_TAB_PRINTFMT) != 0) + goto error; + + fd = fileno(f); + + if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) + goto error; + + /* Copy uid/gid from the present file before renaming. */ + if (stat(filename, &st) == 0) { + if (fchown(fd, st.st_uid, st.st_gid) < 0) + goto error; + } + + fclose(f); + f = NULL; + + if (rename(tmpname, filename) < 0) + goto error; + + return 0; +error: + if (f) + fclose(f); + return -1; +} + +#ifdef TEST_PROGRAM +int test_strerr(struct mtest *ts, int argc, char *argv[]) +{ + char buf[BUFSIZ]; + mnt_tab *tb; + int i; + + tb = mnt_new_tab("-test-"); + if (!tb) + goto err; + + for (i = 0; i < 10; i++) { + mnt_fs *fs = mnt_new_fs(); + if (!fs) + goto err; + if (i % 2) + fs->flags |= MNT_FS_ERROR; /* mark entry as broken */ + fs->lineno = i+1; + mnt_tab_add_fs(tb, fs); + } + + printf("\tadded %d valid lines\n", mnt_tab_get_nents(tb)); + printf("\tadded %d broken lines\n", mnt_tab_get_nerrs(tb)); + + if (!mnt_tab_get_nerrs(tb)) /* report broken entries */ + goto err; + mnt_tab_strerror(tb, buf, sizeof(buf)); + printf("\t%s\n", buf); + + mnt_free_tab(tb); + return 0; +err: + return -1; +} + +mnt_tab *create_tab(const char *file) +{ + mnt_tab *tb; + + if (!file) + return NULL; + tb = mnt_new_tab(file); + if (!tb) + goto err; + if (mnt_tab_parse_file(tb) != 0) + goto err; + if (mnt_tab_get_nerrs(tb)) { + char buf[BUFSIZ]; + mnt_tab_strerror(tb, buf, sizeof(buf)); + fprintf(stderr, "%s\n", buf); + goto err; + } + return tb; +err: + mnt_free_tab(tb); + return NULL; +} + +int test_parse(struct mtest *ts, int argc, char *argv[]) +{ + mnt_tab *tb; + mnt_iter *itr; + mnt_fs *fs; + + tb = create_tab(argv[1]); + if (!tb) + return -1; + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) + goto err; + while(mnt_tab_next_fs(tb, itr, &fs) == 0) + mnt_fs_print_debug(fs, stdout); +err: + mnt_free_iter(itr); + mnt_free_tab(tb); + return 0; +} + +int test_find(struct mtest *ts, int argc, char *argv[], int dr) +{ + mnt_tab *tb; + mnt_fs *fs = NULL; + mnt_cache *mpc; + const char *file, *find, *what; + + if (argc != 4) { + fprintf(stderr, "try --help\n"); + goto err; + } + + file = argv[1], find = argv[2], what = argv[3]; + + tb = create_tab(file); + if (!tb) + goto err; + + /* create a cache for canonicalized paths */ + mpc = mnt_new_cache(); + if (!mpc) + goto err; + mnt_tab_set_cache(tb, mpc); + + if (strcasecmp(find, "source") == 0) + fs = mnt_tab_find_source(tb, what, dr); + else if (strcasecmp(find, "target") == 0) + fs = mnt_tab_find_target(tb, what, dr); + + if (!fs) + fprintf(stderr, "%s: not found %s '%s'\n", file, find, what); + else { + const char *s = mnt_fs_get_srcpath(fs); + if (s) + printf("%s", s); + else { + const char *tag, *val; + mnt_fs_get_tag(fs, &tag, &val); + printf("%s=%s", tag, val); + } + printf("|%s|%s\n", mnt_fs_get_target(fs), + mnt_fs_get_optstr(fs)); + } + mnt_free_tab(tb); + mnt_free_cache(mpc); + return 0; +err: + return -1; +} + +int test_find_bw(struct mtest *ts, int argc, char *argv[]) +{ + return test_find(ts, argc, argv, MNT_ITER_BACKWARD); +} + +int test_find_fw(struct mtest *ts, int argc, char *argv[]) +{ + return test_find(ts, argc, argv, MNT_ITER_FORWARD); +} + +int main(int argc, char *argv[]) +{ + struct mtest tss[] = { + { "--strerror", test_strerr, " test tab error reporting" }, + { "--parse", test_parse, "<file> parse and print tab" }, + { "--find-forward", test_find_fw, "<file> <source|target> <string>" }, + { "--find-backward", test_find_bw, "<file> <source|target> <string>" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/tab_parse.c b/shlibs/mount/src/tab_parse.c new file mode 100644 index 00000000..680e1fc1 --- /dev/null +++ b/shlibs/mount/src/tab_parse.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> + +#include "nls.h" +#include "mangle.h" +#include "mountP.h" + +static inline char *skip_spaces(char *s) +{ + assert(s); + + while (*s == ' ' || *s == '\t') + s++; + return s; +} + +static inline char *skip_nonspaces(char *s) +{ + assert(s); + + while (*s && !(*s == ' ' || *s == '\t')) + s++; + return s; +} + +static size_t next_word_size(char *s, char **start, char **end) +{ + char *e; + + assert(s); + + s = skip_spaces(s); + if (!*s) + return 0; + e = skip_nonspaces(s); + + if (start) + *start = s; + if (end) + *end = e; + + return e - s; +} + +static char *next_word(char **s) +{ + size_t sz; + char *res, *end; + + assert(s); + + sz = next_word_size(*s, s, &end) + 1; + if (sz == 1) + return NULL; + + res = malloc(sz); + if (!res) + return NULL; + + unmangle_to_buffer(*s, res, sz); + *s = end + 1; + return res; +} + +static int next_word_skip(char **s) +{ + *s = skip_spaces(*s); + if (!**s) + return 1; + *s = skip_nonspaces(*s); + return 0; +} + +static int next_number(char **s, int *num) +{ + char *end = NULL; + + assert(num); + assert(s); + + *s = skip_spaces(*s); + if (!**s) + return -1; + *num = strtol(*s, &end, 10); + if (end == NULL || *s == end) + return -1; + + *s = end; + + /* valid end of number is space or terminator */ + if (*end == ' ' || *end == '\t' || *end == '\0') + return 0; + return -1; +} + +/* + * Parses one line from {fs,m}tab + */ +static int mnt_tab_parse_file_line(mnt_fs *fs, char *s) +{ + /* SOURCE */ + if (__mnt_fs_set_source(fs, next_word(&s)) != 0) + return 1; + + /* TARGET */ + fs->target = next_word(&s); + if (!fs->target) + return 1; + + /* TYPE */ + if (__mnt_fs_set_fstype(fs, next_word(&s)) != 0) + return 1; + + /* OPTS */ + fs->optstr = next_word(&s); + if (!fs->optstr) + return 1; + /* default */ + fs->passno = fs->freq = 0; + + /* FREQ (optional) */ + if (next_number(&s, &fs->freq) != 0) { + if (*s) + return 1; + + /* PASSNO (optional) */ + } else if (next_number(&s, &fs->passno) != 0 && *s) + return 1; + + return 0; +} + +/* + * Parses one line from mountinfo file + */ +static int mnt_parse_mountinfo_line(mnt_fs *fs, char *s) +{ + unsigned int maj, min; + + /* ID */ + if (next_number(&s, &fs->id) != 0) + return 1; + + /* PARENT */ + if (next_number(&s, &fs->parent) != 0) + return 1; + + /* <maj>:<min> */ + s = skip_spaces(s); + if (!*s || sscanf(s, "%u:%u", &maj, &min) != 2) + return 1; + fs->devno = makedev(maj, min); + next_word_skip(&s); + + /* MOUNTROOT */ + fs->mntroot = next_word(&s); + if (!fs->mntroot) + return 1; + + /* TARGET (mountpoit) */ + fs->target = next_word(&s); + if (!fs->target) + return 1; + + /* OPTIONS (fs-independent) */ + fs->vfs_optstr = next_word(&s); + if (!fs->vfs_optstr) + return 1; + + /* optional fields (ignore) */ + do { + s = skip_spaces(s); + if (s && *s == '-' && + (*(s + 1) == ' ' || *(s + 1) == '\t')) { + s++; + break; + } + if (s && next_word_skip(&s) != 0) + return 1; + } while (s); + + /* FSTYPE */ + if (__mnt_fs_set_fstype(fs, next_word(&s)) != 0) + return 1; + + /* SOURCE or "none" */ + if (__mnt_fs_set_source(fs, next_word(&s)) != 0) + return 1; + + /* OPTIONS (fs-dependent) */ + fs->fs_optstr = next_word(&s); + if (!fs->fs_optstr) + return 1; + + return 0; +} + +/* + * Returns {m,fs}tab or mountinfo file format (MNT_FMT_*) + * + * The "mountinfo" format is always: "<number> <number> ... " + */ +static int detect_fmt(char *line) +{ + int num; + + /* ID */ + if (next_number(&line, &num) != 0) + return MNT_FMT_FSTAB; + + /* PARENT */ + if (next_number(&line, &num) != 0) + return MNT_FMT_FSTAB; + + return MNT_FMT_MOUNTINFO; +} + + +/* + * Merges @vfs and @fs options strings into a new string. + * This function cares about 'ro/rw' options. The 'ro' is + * always used if @vfs or @fs is read-only. + * For example: + * + * mnt_merge_optstr("rw,noexec", "ro,journal=update") + * + * returns: "ro,noexec,journal=update" + * + * mnt_merge_optstr("rw,noexec", "rw,journal=update") + * + * returns: "rw,noexec,journal=update" + + * We need this function for /proc/self/mountinfo parsing. + */ +static char *merge_optstr(const char *vfs, const char *fs) +{ + char *res, *p; + size_t sz; + int ro = 0, rw = 0; + + if (!vfs && !fs) + return NULL; + if (!vfs || !fs) + return strdup(fs ? fs : vfs); + if (!strcmp(vfs, fs)) + return strdup(vfs); /* e.g. "aaa" and "aaa" */ + + /* leave space for leading "r[ow],", "," and trailing zero */ + sz = strlen(vfs) + strlen(fs) + 5; + res = malloc(sz); + if (!res) + return NULL; + p = res + 3; /* make a room for rw/ro flag */ + + snprintf(p, sz - 3, "%s,%s", vfs, fs); + + /* remove 'rw' flags */ + rw += !mnt_optstr_remove_option(&p, "rw"); /* from vfs */ + rw += !mnt_optstr_remove_option(&p, "rw"); /* from fs */ + + /* remove 'ro' flags if necessary */ + if (rw != 2) { + ro += !mnt_optstr_remove_option(&p, "ro"); + if (ro + rw < 2) + ro += !mnt_optstr_remove_option(&p, "ro"); + } + + if (!strlen(p)) + memcpy(res, ro ? "ro" : "rw", 3); + else + memcpy(res, ro ? "ro," : "rw,", 3); + return res; +} + +/* + * Read and parse the next line from {fs,m}tab or mountinfo + */ +static int mnt_tab_parse_next(mnt_tab *tb, FILE *f, mnt_fs *fs) +{ + char buf[BUFSIZ]; + char *s; + + assert(tb); + assert(f); + assert(fs); + + /* read the next non-blank non-comment line */ + do { + if (fgets(buf, sizeof(buf), f) == NULL) + return -1; + tb->nlines++; + s = index (buf, '\n'); + if (!s) { + /* Missing final newline? Otherwise extremely */ + /* long line - assume file was corrupted */ + if (feof(f)) { + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: WARNING: no final newline at the end of %s\n", + tb->filename)); + s = index (buf, '\0'); + } else { + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: %d: missing newline at line\n", + tb->filename, tb->nlines)); + goto err; + } + } + *s = '\0'; + if (--s >= buf && *s == '\r') + *s = '\0'; + s = skip_spaces(buf); + } while (*s == '\0' || *s == '#'); + + DBG(DEBUG_TAB, fprintf(stderr, "libmount: %s:%d: %s\n", + tb->filename, tb->nlines, s)); + + if (!tb->fmt) + tb->fmt = detect_fmt(s); + + if (tb->fmt == MNT_FMT_FSTAB) { + /* parse /etc/{fs,m}tab */ + if (mnt_tab_parse_file_line(fs, s) != 0) + goto err; + } else if (tb->fmt == MNT_FMT_MOUNTINFO) { + /* parse /proc/self/mountinfo */ + if (mnt_parse_mountinfo_line(fs, s) != 0) + goto err; + } + + /* merge fs_optstr and vfs_optstr into optstr (necessary for "mountinfo") */ + if (!fs->optstr && (fs->vfs_optstr || fs->fs_optstr)) { + fs->optstr = merge_optstr(fs->vfs_optstr, fs->fs_optstr); + if (!fs->optstr) + goto err; + } + + fs->lineno = tb->nlines; + + DBG(DEBUG_TAB, fprintf(stderr, + "libmount: %s: %d: SOURCE:%s, MNTPOINT:%s, TYPE:%s, " + "OPTS:%s, FREQ:%d, PASSNO:%d\n", + tb->filename, fs->lineno, + fs->source, fs->target, fs->fstype, + fs->optstr, fs->freq, fs->passno)); + + return 0; +err: + /* we don't report parse errors to caller; caller has to check + * errors by mnt_tab_get_nerrs() or internaly by MNT_ENTRY_ERR flag + */ + fs->lineno = tb->nlines; + fs->flags |= MNT_FS_ERROR; + return 0; +} + +/** + * mnt_tab_parse_file: + * @tb: tab pointer + * + * Parses whole table (e.g. /etc/fstab). + * + * <informalexample> + * <programlisting> + * mnt_tab *tb = mnt_new_tab("/etc/fstab"); + * int rc; + * + * rc = mnt_tab_parse_file(tb); + * if (rc) { + * if (mnt_tab_get_nerrs(tb)) { / * parse error * / + * mnt_tab_strerror(tb, buf, sizeof(buf)); + * fprintf(stderr, "%s: %s\n", progname, buf); + * } else + * perror(mnt_tab_get_name(tb)); / * system error * / + * } else + * mnt_fprintf_tab(tb, stdout, NULL); + * + * mnt_free_tab(tb); + * </programlisting> + * </informalexample> + * + * Returns: 0 on success and -1 in case of error. The parse errors is possible + * to detect by mnt_tab_get_nerrs() and error message is possible to create by + * mnt_tab_strerror(). + */ +int mnt_tab_parse_file(mnt_tab *tb) +{ + FILE *f; + + assert(tb); + assert(tb->filename); + + if (!tb->filename) + return -1; + + f = fopen(tb->filename, "r"); + if (!f) + return -1; + + while (!feof(f)) { + int rc; + mnt_fs *fs = mnt_new_fs(); + if (!fs) + goto error; + + rc = mnt_tab_parse_next(tb, f, fs); + if (!rc) + rc = mnt_tab_add_fs(tb, fs); + else if (feof(f)) { + mnt_free_fs(fs); + break; + } + if (rc) { + mnt_free_fs(fs); + goto error; + } + } + + fclose(f); + return 0; +error: + fclose(f); + return -1; +} + +/** + * mnt_new_tab_parse: + * @filename: /etc/{m,fs}tab or /proc/self/mountinfo path + * + * Same as mnt_new_tab() + mnt_tab_parse_file(). Note that this function does + * not provide details (by mnt_tab_strerror()) about failed parsing -- so you + * should not to use this function for user-writeable files like /etc/fstab. + * + * Returns: newly allocated tab on success and NULL in case of error. + */ +mnt_tab *mnt_new_tab_from_file(const char *filename) +{ + mnt_tab *tb; + + assert(filename); + + if (!filename) + return NULL; + tb = mnt_new_tab(filename); + if (tb && mnt_tab_parse_file(tb) != 0) { + mnt_free_tab(tb); + tb = NULL; + } + return tb; +} + +/** + * mnt_tab_get_nerrs: + * @tb: pointer to table + * + * Returns: number of broken (parse error) entries in the table. + */ +int mnt_tab_get_nerrs(mnt_tab *tb) +{ + assert(tb); + return tb->nerrs; +} + +/** + * mnt_tab_strerror: + * @tb: pointer to table + * @buf: buffer to return error message + * @buflen: lenght of the buf + * + * Returns: error message for table (file) parse errors. For example: + * + * "/etc/fstab: parse error at line(s): 1, 2 and 3." + */ +char *mnt_tab_strerror(mnt_tab *tb, char *buf, size_t buflen) +{ + struct list_head *p; + int last = -1; + char *b = buf; + char *end = buf + buflen - 1; + + assert(tb); + assert(buf); + assert(buflen); + + if (!tb || !tb->nerrs || !buf || buflen <=0) + return NULL; + + if (tb->filename) { + snprintf(b, end - b, "%s: ", tb->filename); + b += strnlen(b, end - b); + } + + if (tb->nerrs > 1) + strncpy(b, _("parse error at lines: "), end - b); + else + strncpy(b, _("parse error at line: "), end - b); + b += strnlen(b, end - b); + *b = '\0'; + + list_for_each(p, &tb->ents) { + mnt_fs *fs = list_entry(p, mnt_fs, ents); + if (b == end) + goto done; + if (fs->flags & MNT_FS_ERROR) { + if (last != -1) { + snprintf(b, end - b, "%d, ", last); + b += strnlen(b, end - b); + } + last = fs->lineno; + } + } + + if (tb->nerrs == 1) + snprintf(b, end - b, "%d.", last); + else + snprintf(b - 1, end - b, _(" and %d."), last); +done: + return buf; +} + diff --git a/shlibs/mount/src/test.c b/shlibs/mount/src/test.c new file mode 100644 index 00000000..eb4f2d1d --- /dev/null +++ b/shlibs/mount/src/test.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Routines for TEST_PROGRAMs + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifndef TEST_PROGRAM +#define TEST_PROGRAM +#endif + +#include "mountP.h" + +int mnt_run_test(struct mtest *tests, int argc, char *argv[]) +{ + int rc = -1; + struct mtest *ts; + + assert(tests); + assert(argc); + assert(argv); + + if (argc < 2 || + strcmp(argv[1], "--help") == 0 || + strcmp(argv[1], "-h") == 0) + goto usage; + + mnt_init_debug(0); + + for (ts = tests; ts->name; ts++) { + if (strcmp(ts->name, argv[1]) == 0) { + rc = ts->body(ts, argc - 1, argv + 1); + if (rc) + printf("FAILED [rc=%d]", rc); + break; + } + } + + if (rc == -1 && ts->name == NULL) + goto usage; + + return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +usage: + printf("\nUsage:\n\t%s <test> [testoptions]\nTests:\n", + program_invocation_short_name); + for (ts = tests; ts->name; ts++) { + printf("\t%-15s", ts->name); + if (ts->usage) + printf(" %s\n", ts->usage); + } + printf("\n"); + return EXIT_FAILURE; +} diff --git a/shlibs/mount/src/utils.c b/shlibs/mount/src/utils.c new file mode 100644 index 00000000..f487383a --- /dev/null +++ b/shlibs/mount/src/utils.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: utils + * @title: Utils + * @short_description: misc utils. + */ +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#else +#define PR_GET_DUMPABLE 3 +#endif +#if (!defined(HAVE_PRCTL) && defined(linux)) +#include <sys/syscall.h> +#endif +#include <sys/stat.h> +#include <ctype.h> +#include <sys/types.h> +#include <fcntl.h> +#include <pwd.h> + +#include "mountP.h" + +char *mnt_getenv_safe(const char *arg) +{ + if ((getuid() != geteuid()) || (getgid() != getegid())) + return NULL; +#if HAVE_PRCTL + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) + return NULL; +#else +#if (defined(linux) && defined(SYS_prctl)) + if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) + return NULL; +#endif +#endif + +#ifdef HAVE___SECURE_GETENV + return __secure_getenv(arg); +#else + return getenv(arg); +#endif +} + +/* TODO: move strn<...> functions to top-level lib/strn.c */ +#ifndef HAVE_STRNLEN +size_t strnlen(const char *s, size_t maxlen) +{ + int i; + + for (i = 0; i < maxlen; i++) { + if (s[i] == '\0') + return i + 1; + } + return maxlen; +} +#endif + +#ifndef HAVE_STRNCHR +char *strnchr(const char *s, size_t maxlen, int c) +{ + for (; maxlen-- && *s != '\0'; ++s) + if (*s == (char)c) + return (char *)s; + return NULL; +} +#endif + +#ifndef HAVE_STRNDUP +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen (s, n); + char *new = (char *) malloc (len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return (char *) memcpy (new, s, len); +} +#endif + + +/** + * mnt_fstype_is_pseudofs: + * @type: filesystem name + * + * Returns: 1 for filesystems like proc, sysfs, ... or 0. + */ +int mnt_fstype_is_pseudofs(const char *type) +{ + if (!type) + return 0; + if (strcmp(type, "none") == 0 || + strcmp(type, "proc") == 0 || + strcmp(type, "tmpfs") == 0 || + strcmp(type, "sysfs") == 0 || + strcmp(type, "devpts") == 0|| + strcmp(type, "cgroups") == 0 || + strcmp(type, "devfs") == 0 || + strcmp(type, "dlmfs") == 0 || + strcmp(type, "cpuset") == 0 || + strcmp(type, "spufs") == 0) + return 1; + return 0; +} + +/** + * mnt_fstype_is_netfs: + * @type: filesystem name + * + * Returns: 1 for filesystems like cifs, nfs, ... or 0. + */ +int mnt_fstype_is_netfs(const char *type) +{ + if (!type) + return 0; + if (strcmp(type, "cifs") == 0 || + strcmp(type, "smbfs") == 0 || + strncmp(type, "nfs", 3) == 0 || + strcmp(type, "afs") == 0 || + strcmp(type, "ncpfs") == 0) + return 1; + return 0; +} + +/** + * mnt_match_fstype: + * @type: filesystem type + * @pattern: filesystem name or comma delimitted list of names + * + * The @pattern list of filesystem can be prefixed with a global + * "no" prefix to invert matching of the whole list. The "no" could + * also used for individual items in the @pattern list. So, + * "nofoo,bar" has the same meaning as "nofoo,nobar". + * + * "bar" : "nofoo,bar" -> False (global "no" prefix) + * + * "bar" : "foo,bar" -> True + * + * "bar" : "foo,nobar" -> False + * + * Returns: 1 if type is matching, else 0. This function also returns + * 0 if @pattern is NULL and @type is non-NULL. + */ +int mnt_match_fstype(const char *type, const char *pattern) +{ + int no = 0; /* negated types list */ + int len; + const char *p; + + if (!pattern && !type) + return 1; + if (!pattern) + return 0; + + if (!strncmp(pattern, "no", 2)) { + no = 1; + pattern += 2; + } + + /* Does type occur in types, separated by commas? */ + len = strlen(type); + p = pattern; + while(1) { + if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) && + (p[len+2] == 0 || p[len+2] == ',')) + return 0; + if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ',')) + return !no; + p = strchr(p,','); + if (!p) + break; + p++; + } + return no; +} + + +/* Returns 1 if needle found or noneedle not found in haystack + * Otherwise returns 0 + */ +static int check_option(const char *haystack, size_t len, + const char *needle, size_t needle_len) +{ + const char *p; + int no = 0; + + if (needle_len >= 2 && !strncmp(needle, "no", 2)) { + no = 1; + needle += 2; + needle_len -= 2; + } + + for (p = haystack; p && p < haystack + len; p++) { + char *sep = strchr(p, ','); + size_t plen = sep ? sep - p : len - (p - haystack); + + if (plen == needle_len) { + if (!strncmp(p, needle, plen)) + return !no; /* foo or nofoo was found */ + } + p += plen; + } + + return no; /* foo or nofoo was not found */ +} + +/** + * mnt_match_options: + * @optstr: options string + * @pattern: comma delimitted list of options + * + * The "no" could used for individual items in the @options list. The "no" + * prefix does not have a global meanning. + * + * Unlike fs type matching, nonetdev,user and nonetdev,nouser have + * DIFFERENT meanings; each option is matched explicitly as specified. + * + * "xxx,yyy,zzz" : "nozzz" -> False + * + * "xxx,yyy,zzz" : "xxx,noeee" -> True + * + * Returns: 1 if pattern is matching, else 0. This function also returns 0 + * if @pattern is NULL and @optstr is non-NULL. + */ +int mnt_match_options(const char *optstr, const char *pattern) +{ + const char *p; + size_t len, optstr_len = 0; + + if (!pattern && !optstr) + return 1; + if (!pattern) + return 0; + + len = strlen(pattern); + if (optstr) + optstr_len = strlen(optstr); + + for (p = pattern; p < pattern + len; p++) { + char *sep = strchr(p, ','); + size_t plen = sep ? sep - p : len - (p - pattern); + + if (!plen) + continue; /* if two ',' appear in a row */ + + if (!check_option(optstr, optstr_len, p, plen)) + return 0; /* any match failure means failure */ + + p += plen; + } + + /* no match failures in list means success */ + return 1; +} + +/* + * Reallocates its first arg @s - typical use: s = mnt_strconcat3(s,t,u); + * Returns reallocated @s ion succes or NULL in case of error. + */ +char *mnt_strconcat3(char *s, const char *t, const char *u) +{ + size_t len = 0; + + len = (s ? strlen(s) : 0) + (t ? strlen(t) : 0) + (u ? strlen(u) : 0); + + if (!len) + return NULL; + if (!s) { + s = malloc(len + 1); + *s = '\0'; + } else + s = realloc(s, len + 1); + + if (!s) + return NULL; + if (t) + strcat(s, t); + if (u) + strcat(s, u); + return s; +} + +/* + * Returns allocated string with username or NULL. + */ +char *mnt_get_username(const uid_t uid) +{ + struct passwd pwd; + struct passwd *res; + size_t sz = sysconf(_SC_GETPW_R_SIZE_MAX); + char *buf, *username = NULL; + + if (sz <= 0) + sz = 16384; /* Should be more than enough */ + + buf = malloc(sz); + if (!buf) + return NULL; + + if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res) + username = strdup(pwd.pw_name); + + free(buf); + return username; +} + +#ifdef TEST_PROGRAM +int test_match_fstype(struct mtest *ts, int argc, char *argv[]) +{ + char *type = argv[1]; + char *pattern = argv[2]; + + printf("%s\n", mnt_match_fstype(type, pattern) ? "MATCH" : "NOT-MATCH"); + return 0; +} + +int test_match_options(struct mtest *ts, int argc, char *argv[]) +{ + char *optstr = argv[1]; + char *pattern = argv[2]; + + printf("%s\n", mnt_match_options(optstr, pattern) ? "MATCH" : "NOT-MATCH"); + return 0; +} + + +int main(int argc, char *argv[]) +{ + struct mtest tss[] = { + { "--match-fstype", test_match_fstype, "<type> <pattern> FS types matching" }, + { "--match-options", test_match_options, "<options> <pattern> options matching" }, + { NULL } + }; + + return mnt_run_test(tss, argc, argv); +} + +#endif /* TEST_PROGRAM */ diff --git a/shlibs/mount/src/version.c b/shlibs/mount/src/version.c new file mode 100644 index 00000000..d00a1686 --- /dev/null +++ b/shlibs/mount/src/version.c @@ -0,0 +1,86 @@ +/* + * version.c - Return the version of the blkid library + * + * Copyright (C) 2008 Karel Zak <kzak@redhat.com> + * [Based on libblkid/version.c by Theodore Ts'o] + * + * See COPYING.libmount for the License of this software. + */ + +/** + * SECTION: version + * @title: Version functions + * @short_description: functions to get library version. + */ + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> + +#include "mountP.h" + +static const char *lib_version = LIBMOUNT_VERSION; + +/** + * mnt_parse_version_string: + * @ver_string: version string (e.g "2.18.0") + * + * Returns: release version code. + */ +int mnt_parse_version_string(const char *ver_string) +{ + const char *cp; + int version = 0; + + for (cp = ver_string; *cp; cp++) { + if (*cp == '.') + continue; + if (!isdigit(*cp)) + break; + version = (version * 10) + (*cp - '0'); + } + return version; +} + +/** + * mnt_get_library_version: + * @ver_string: return pointer to the static library version string + * + * Returns: release version number. + */ +int mnt_get_library_version(const char **ver_string) +{ + if (ver_string) + *ver_string = lib_version; + + return mnt_parse_version_string(lib_version); +} + +#ifdef TEST_PROGRAM +int test_version(struct mtest *ts, int argc, char *argv[]) +{ + const char *ver; + + mnt_get_library_version(&ver); + + printf("Library version: %s\n", ver); + printf("Library API version: " LIBMOUNT_VERSION "\n"); + + if (mnt_get_library_version(NULL) == + mnt_parse_version_string(LIBMOUNT_VERSION)) + return 0; + + return -1; +} + +int main(int argc, char *argv[]) +{ + struct mtest ts[] = { + { "--print", test_version, "print versions" }, + { NULL } + }; + + return mnt_run_test(ts, argc, argv); +} +#endif diff --git a/shlibs/uuid/Makefile.am b/shlibs/uuid/Makefile.am index 2bdef334..66fe6909 100644 --- a/shlibs/uuid/Makefile.am +++ b/shlibs/uuid/Makefile.am @@ -6,5 +6,5 @@ SUBDIRS = src man pkgconfigdir = $(usrlib_execdir)/pkgconfig pkgconfig_DATA = uuid.pc -EXTRA_DIST = uuid.pc.in +EXTRA_DIST = COPYING.libuuid uuid.pc.in diff --git a/shlibs/uuid/man/Makefile.am b/shlibs/uuid/man/Makefile.am index ffaf0241..38809522 100644 --- a/shlibs/uuid/man/Makefile.am +++ b/shlibs/uuid/man/Makefile.am @@ -8,5 +8,5 @@ UUID_GENERATE_LINKS = uuid_generate_random.3 uuid_generate_time.3 man_MANS = $(UUID_GENERATE_LINKS) CLEANFILES = $(man_MANS) -$(UUID_GENERATE_LINKS): uuid_generate.3 - echo ".so man3/uuid_generate.3" > $@ +$(UUID_GENERATE_LINKS): + $(AM_V_GEN)echo ".so man3/uuid_generate.3" > $@ diff --git a/shlibs/uuid/man/uuid_generate.3 b/shlibs/uuid/man/uuid_generate.3 index 11f48ffd..e1b8143f 100644 --- a/shlibs/uuid/man/uuid_generate.3 +++ b/shlibs/uuid/man/uuid_generate.3 @@ -59,7 +59,7 @@ a high-quality random number generator (i.e., .IR /dev/urandom ) is not available, in which case a pseudo-random generator will be subsituted. Note that the use of a pseudo-random -generator may compromise the uniqueness of UUID's +generator may compromise the uniqueness of UUIDs generated in this fashion. .sp The diff --git a/shlibs/uuid/src/compare.c b/shlibs/uuid/src/compare.c index f28a7267..8f3437a2 100644 --- a/shlibs/uuid/src/compare.c +++ b/shlibs/uuid/src/compare.c @@ -1,7 +1,7 @@ /* - * compare.c --- compare whether or not two UUID's are the same + * compare.c --- compare whether or not two UUIDs are the same * - * Returns 0 if the two UUID's are different, and 1 if they are the same. + * Returns 0 if the two UUIDs are different, and 1 if they are the same. * * Copyright (C) 1996, 1997 Theodore Ts'o. * diff --git a/shlibs/uuid/src/pack.c b/shlibs/uuid/src/pack.c index 097516d2..6e124766 100644 --- a/shlibs/uuid/src/pack.c +++ b/shlibs/uuid/src/pack.c @@ -1,5 +1,5 @@ /* - * Internal routine for packing UUID's + * Internal routine for packing UUIDs * * Copyright (C) 1996, 1997 Theodore Ts'o. * |