#ifndef UTIL_LINUX_LOOPDEV_H
#define UTIL_LINUX_LOOPDEV_H
+#include "sysfs.h"
+
/*
* loop_info.lo_encrypt_type
*/
char device[128]; /* device path (e.g. /dev/loop<N>) */
char *filename; /* backing file for loopcxt_set_... */
int fd; /* open(/dev/looo<N>) */
+ int mode; /* fd mode O_{RDONLY,RDWR} */
int flags; /* LOOPDEV_FL_* flags */
int has_info:1; /* .info contains data */
* loopdev_cxt.flags
*/
enum {
- LOOPDEV_FL_RDONLY = (1 << 0), /* default */
+ LOOPDEV_FL_RDONLY = (1 << 0), /* open(/dev/loop) mode; default */
LOOPDEV_FL_RDWR = (1 << 1), /* necessary for loop setup only */
LOOPDEV_FL_OFFSET = (1 << 4),
LOOPDEV_FL_NOSYSFS = (1 << 5),
extern char *loopcxt_strdup_device(struct loopdev_cxt *lc);
extern const char *loopcxt_get_device(struct loopdev_cxt *lc);
extern struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc);
+
extern int loopcxt_get_fd(struct loopdev_cxt *lc);
+extern int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode);
extern int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags);
extern int loopcxt_deinit_iterator(struct loopdev_cxt *lc);
extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
extern int loopcxt_is_autoclear(struct loopdev_cxt *lc);
+extern int loopcxt_is_readonly(struct loopdev_cxt *lc);
extern int loopcxt_find_by_backing_file(struct loopdev_cxt *lc,
const char *filename,
uint64_t offset, int flags);
if (lc->fd >= 0)
close(lc->fd);
lc->fd = -1;
+ lc->mode = 0;
lc->has_info = 0;
*lc->device = '\0';
+ memset(&lc->info, 0, sizeof(lc->info));
/* set new */
if (device) {
*
* Initilize loop handler.
*
+ * We have two sets of the flags:
+ *
+ * * LOOPDEV_FL_* flags control loopcxt_* API behavior
+ *
+ * * LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls
+ *
+ * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2)
+ * syscall to open loop device. By default is the device open read-only.
+ *
+ * The expection is loopcxt_setup_device(), where the device is open read-write
+ * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()).
+ *
* Returns: <0 on error, 0 on success.
*/
int loopcxt_init(struct loopdev_cxt *lc, int flags)
int loopcxt_get_fd(struct loopdev_cxt *lc)
{
if (!lc || !*lc->device)
- return -1;
+ return -EINVAL;
- if (lc->fd < 0)
- lc->fd = open(lc->device, lc->flags & LOOPDEV_FL_RDWR ?
- O_RDWR : O_RDONLY);
+ if (lc->fd < 0) {
+ lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+ lc->fd = open(lc->device, lc->mode);
+ }
return lc->fd;
}
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+{
+ if (!lc)
+ return -EINVAL;
+
+ lc->fd = fd;
+ lc->mode = mode;
+ return 0;
+}
+
/*
* @lc: context
* @flags: LOOPITER_FL_* flags
char *res = NULL;
if (sysfs)
+ /*
+ * This is always preffered, the loop_info64
+ * has too small buffer for the filename.
+ */
res = sysfs_strdup(sysfs, "loop/backing_file");
if (!res && loopcxt_ioctl_enabled(lc)) {
return 0;
}
+/*
+ * @lc: context
+ *
+ * Returns: 1 of the readonly flags is set.
+ */
+int loopcxt_is_readonly(struct loopdev_cxt *lc)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ int fl;
+ if (sysfs_read_int(sysfs, "ro", &fl) == 0)
+ return fl;
+ }
+
+ if (loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ return lo->lo_flags & LO_FLAGS_READ_ONLY;
+ }
+ return 0;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset)
{
if (!lc)
return 0;
}
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit)
{
if (!lc)
* @lc: context
* @flags: kernel LO_FLAGS_{READ_ONLY,USE_AOPS,AUTOCLEAR} flags
*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
* Returns: 0 on success, <0 on error.
*/
int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
* @lc: context
* @filename: backing file path (the path will be canonicalized)
*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
* Returns: 0 on success, <0 on error.
*/
int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
* @encryption: encryption name / type (see lopsetup man page)
* @password
*
- * Note that the encyption functionality is deprecated an unmaintained. Use
+ * Note that the encryption functionality is deprecated an unmaintained. Use
* cryptsetup (it also supports AES-loops).
*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
* Returns: 0 on success, <0 on error.
*/
int loopcxt_set_encryption(struct loopdev_cxt *lc,
* Associate the current device (see loopcxt_{set,get}_device()) with
* a file (see loopcxt_set_backing_file()).
*
- * Default is open backing file and device in read-write mode, see
- * LOOPDEV_FL_{RDONLY,RDWR} and loopcxt_init(). The LO_FLAGS_READ_ONLY lo_flag
- * will be set automatically according to LOOPDEV_FL_ flags.
+ * The device is initialized read-write by default. If you want read-only
+ * device then set LO_FLAGS_READ_ONLY by loopcxt_set_flags(). The LOOPDEV_FL_*
+ * flags are ignored and modified according to LO_FLAGS_*.
+ *
+ * If the device is already open by loopcxt_get_fd() then this setup device
+ * function will re-open the device to fix read/write mode.
+ *
+ * The device is also initialized read-only if the backing file is not
+ * possible to open read-write (e.g. read-only FS).
*
* Returns: <0 on error, 0 on success.
*/
int loopcxt_setup_device(struct loopdev_cxt *lc)
{
- int file_fd, dev_fd, mode, rc = -1;
+ int file_fd, dev_fd, mode = O_RDWR, rc = -1;
if (!lc || !*lc->device || !lc->filename)
return -EINVAL;
/*
* Open backing file and device
*/
- mode = (lc->flags & LOOPDEV_FL_RDONLY) ? O_RDONLY : O_RDWR;
+ if (lc->info.lo_flags & LO_FLAGS_READ_ONLY)
+ mode = O_RDONLY;
if ((file_fd = open(lc->filename, mode)) < 0) {
if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
return -errno;
}
+ if (lc->fd != -1 && lc->mode != mode) {
+ close(lc->fd);
+ lc->fd = -1;
+ lc->mode = 0;
+ }
+
if (mode == O_RDONLY) {
- lc->flags |= LOOPDEV_FL_RDONLY;
- lc->info.lo_flags |= LO_FLAGS_READ_ONLY;
- } else
- lc->flags |= LOOPDEV_FL_RDWR;
+ lc->flags |= LOOPDEV_FL_RDONLY; /* open() mode */
+ lc->info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */
+ } else {
+ lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */
+ lc->info.lo_flags &= ~LO_FLAGS_READ_ONLY;
+ lc->flags &= ~LOOPDEV_FL_RDONLY;
+ }
dev_fd = loopcxt_get_fd(lc);
if (dev_fd < 0) {
loopcxt_init(&lc, 0);
- if (loopcxt_set_backing_file(&lc, filename))
- err(EXIT_FAILURE, "failed to set backing file");
-
if (device) {
rc = loopcxt_set_device(&lc, device);
if (rc)
printf("Trying to use '%s'\n", loopcxt_get_device(&lc));
}
+ if (loopcxt_set_backing_file(&lc, filename))
+ err(EXIT_FAILURE, "failed to set backing file");
+
rc = loopcxt_setup_device(&lc);
if (rc == 0)
break; /* success */