+/**
+Reads device names from an fstab/mtab file.
+*/
+char **get_mount_entries(char *filename, int (*predicate)(struct mntent *))
+{
+ FILE *file;
+ struct mntent *me;
+ char **devices = NULL;
+ int n_devices = 0;
+
+ file = setmntent(filename, "r");
+ if(!file)
+ return NULL;
+
+ while((me = getmntent(file)))
+ if(!predicate || predicate(me))
+ {
+ devices = (char **)realloc(devices, (n_devices+2)*sizeof(char *));
+ devices[n_devices] = strdup(me->mnt_fsname);
+ ++n_devices;
+ }
+
+ endmntent(file);
+ if(devices)
+ devices[n_devices] = NULL;
+
+ return devices;
+}
+
+/**
+Returns an array of all currently mounted devices.
+*/
+char **get_mounted_devices(void)
+{
+ return get_mount_entries("/etc/mtab", NULL);
+}
+
+/**
+Checks if an fstab entry has the user option set.
+*/
+int is_user_mountable(struct mntent *me)
+{
+ return hasmntopt(me, "user")!=NULL;
+}
+
+/**
+Returns an array of user-mountable devices listed in fstab.
+*/
+char **get_fstab_devices(void)
+{
+ return get_mount_entries("/etc/fstab", &is_user_mountable);
+}
+
+/**
+Checks if an array of strings contains the specified string.
+*/
+int is_in_array(char **array, char *str)
+{
+ int i;
+ if(!array || !str)
+ return 0;
+ for(i=0; array[i]; ++i)
+ if(!strcmp(str, array[i]))
+ return 1;
+ return 0;
+}
+
+/**
+Frees an array of strings.
+*/
+void free_string_array(char **array)
+{
+ int i;
+ if(!array)
+ return;
+ for(i=0; array[i]; ++i)
+ free(array[i]);
+ free(array);
+}
+
+/**
+Checks if a partition identified by a sysfs path is on a removable device.
+*/
+int is_removable(char *devpath)
+{
+ char fnbuf[256];
+ int len;
+ char *ptr;
+ int fd;
+
+ len = snprintf(fnbuf, sizeof(fnbuf), "/sys%s", devpath);
+ /* Default to not removable if the path was too long. */
+ if(len+10>=(int)sizeof(fnbuf))
+ return 0;
+
+ /* We got a partition as a parameter, but the removable property is on the
+ disk. Replace the last component with "removable". */
+ for(ptr=fnbuf+len; (ptr>fnbuf && *ptr!='/'); --ptr) ;
+ strcpy(ptr, "/removable");
+
+ fd = open(fnbuf, O_RDONLY);
+ if(fd!=-1)
+ {
+ char c;
+ read(fd, &c, 1);
+ close(fd);
+ if(c=='1')
+ {
+ if(verbosity>=2)
+ printf(" Removable\n");
+ return 1;
+ }
+ if(verbosity>=2)
+ printf(" Not removable\n");
+ }
+
+ return 0;
+}
+
+/**
+Checks if a partition's disk or any of its parent devices are connected to any
+of a set of buses. The device is identified by a sysfs path. The bus array
+must be terminated with a NULL entry.
+*/
+int check_buses(char *devpath, char **buses)
+{
+ char fnbuf[256];
+ char *ptr;
+ int len;
+
+ len = snprintf(fnbuf, sizeof(fnbuf), "/sys%s", devpath);
+ /* Default to no match if the path was too long. */
+ if(len+10>=(int)sizeof(fnbuf))
+ return 0;
+
+ for(ptr=fnbuf+len; ptr>fnbuf+12; --ptr)
+ if(*ptr=='/')
+ {
+ char linkbuf[256];
+ /* Replace the last component with "subsystem". */
+ strcpy(ptr, "/subsystem");
+ len = readlink(fnbuf, linkbuf, sizeof(linkbuf)-1);
+
+ if(len!=-1)
+ {
+ linkbuf[len] = 0;
+ /* Extract the last component of the subsystem symlink. */
+ for(; (len>0 && linkbuf[len-1]!='/'); --len) ;
+
+ if(verbosity>=2)
+ {
+ *ptr = 0;
+ printf(" Subsystem of %s is %s\n", fnbuf, linkbuf+len);
+ }
+
+ if(is_in_array(buses, linkbuf+len))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+Check if an array of properties describes a device that can be mounted. An
+array of explicitly allowed devices can be passed in as well. Both arrays must
+be terminated by a NULL entry.
+*/
+int can_mount(Property *props, char **allowed)
+{
+ static char *removable_buses[] = { "usb", "firewire", 0 };
+ char *devname;
+ char *devpath;
+ char *bus;
+
+ devname = get_property_value(props, "DEVNAME");
+ if(is_in_array(allowed, devname))
+ return 1;
+
+ /* Special case for CD devices, since they are not partitions. Only allow
+ mounting if media is inserted. */
+ if(match_property_value(props, "ID_TYPE", "cd") && match_property_value(props, "ID_CDROM_MEDIA", "1"))
+ return 1;
+
+ /* Only allow mounting partitions. */
+ if(!match_property_value(props, "DEVTYPE", "partition"))
+ return 0;
+
+ devpath = get_property_value(props, "DEVPATH");
+ if(is_removable(devpath))
+ return 1;
+
+ /* Certain buses are removable by nature, but devices only advertise
+ themselves as removable if they support removable media, e.g. memory card
+ readers. */
+ bus = get_property_value(props, "ID_BUS");
+ if(is_in_array(removable_buses, bus))
+ return 1;
+
+ return check_buses(devpath, removable_buses);
+}
+
+/**
+Returns an array of all device nodes in a directory. Symbolic links are
+dereferenced.
+*/
+char **get_device_nodes(char *dirname)