mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-10 23:07:44 +00:00
save: let iohelper handle inherited fd
Rather than making the iohelper subject to a race in reopening the file, it is nicer to pass an already-open fd by inheritance. The old synopsis form must continue to work - if someone updates their libvirt package and installs a new libvirt_iohelper but without restarting the old libvirtd daemon, then the daemon can still make calls using the old syntax but the new iohelper. * src/util/iohelper.c (runIO): Split code for open... (prepare): ...to new function. (usage): Update synopsis. (main): Allow alternate calling form. * src/fdstream.c (virFDStreamOpenFileInternal): Use alternate form.
This commit is contained in:
parent
38149ec145
commit
1eb6647979
@ -535,9 +535,17 @@ virFDStreamOpenFileInternal(virStreamPtr st,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (offset &&
|
||||||
|
lseek(fd, offset, SEEK_SET) != offset) {
|
||||||
|
virReportSystemError(errno,
|
||||||
|
_("Unable to seek %s to %llu"),
|
||||||
|
path, offset);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Thanks to the POSIX i/o model, we can't reliably get
|
/* Thanks to the POSIX i/o model, we can't reliably get
|
||||||
* non-blocking I/O on block devs/regular files. To
|
* non-blocking I/O on block devs/regular files. To
|
||||||
* support those we need to fork a helper process todo
|
* support those we need to fork a helper process to do
|
||||||
* the I/O so we just have a fifo. Or use AIO :-(
|
* the I/O so we just have a fifo. Or use AIO :-(
|
||||||
*/
|
*/
|
||||||
if ((st->flags & VIR_STREAM_NONBLOCK) &&
|
if ((st->flags & VIR_STREAM_NONBLOCK) &&
|
||||||
@ -545,14 +553,13 @@ virFDStreamOpenFileInternal(virStreamPtr st,
|
|||||||
!S_ISFIFO(sb.st_mode))) {
|
!S_ISFIFO(sb.st_mode))) {
|
||||||
int childfd;
|
int childfd;
|
||||||
|
|
||||||
if ((oflags & O_RDWR) == O_RDWR) {
|
if ((oflags & O_ACCMODE) == O_RDWR) {
|
||||||
streamsReportError(VIR_ERR_INTERNAL_ERROR,
|
streamsReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
_("%s: Cannot request read and write flags together"),
|
_("%s: Cannot request read and write flags together"),
|
||||||
path);
|
path);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
VIR_FORCE_CLOSE(fd);
|
|
||||||
if (pipe(fds) < 0) {
|
if (pipe(fds) < 0) {
|
||||||
virReportSystemError(errno, "%s",
|
virReportSystemError(errno, "%s",
|
||||||
_("Unable to create pipe"));
|
_("Unable to create pipe"));
|
||||||
@ -562,18 +569,9 @@ virFDStreamOpenFileInternal(virStreamPtr st,
|
|||||||
cmd = virCommandNewArgList(LIBEXECDIR "/libvirt_iohelper",
|
cmd = virCommandNewArgList(LIBEXECDIR "/libvirt_iohelper",
|
||||||
path,
|
path,
|
||||||
NULL);
|
NULL);
|
||||||
virCommandAddArgFormat(cmd, "%d", oflags);
|
|
||||||
virCommandAddArgFormat(cmd, "%d", mode);
|
|
||||||
virCommandAddArgFormat(cmd, "%llu", offset);
|
|
||||||
virCommandAddArgFormat(cmd, "%llu", length);
|
virCommandAddArgFormat(cmd, "%llu", length);
|
||||||
virCommandAddArgFormat(cmd, "%u", delete);
|
virCommandTransferFD(cmd, fd);
|
||||||
|
virCommandAddArgFormat(cmd, "%d", fd);
|
||||||
/* when running iohelper we don't want to delete file now,
|
|
||||||
* because a race condition may occur in which we delete it
|
|
||||||
* before iohelper even opens it. We want iohelper to remove
|
|
||||||
* the file instead.
|
|
||||||
*/
|
|
||||||
delete = false;
|
|
||||||
|
|
||||||
if (oflags == O_RDONLY) {
|
if (oflags == O_RDONLY) {
|
||||||
childfd = fds[1];
|
childfd = fds[1];
|
||||||
@ -590,14 +588,6 @@ virFDStreamOpenFileInternal(virStreamPtr st,
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
VIR_FORCE_CLOSE(childfd);
|
VIR_FORCE_CLOSE(childfd);
|
||||||
} else {
|
|
||||||
if (offset &&
|
|
||||||
lseek(fd, offset, SEEK_SET) != offset) {
|
|
||||||
virReportSystemError(errno,
|
|
||||||
_("Unable to seek %s to %llu"),
|
|
||||||
path, offset);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virFDStreamOpenInternal(st, fd, cmd, errfd, length) < 0)
|
if (virFDStreamOpenInternal(st, fd, cmd, errfd, length) < 0)
|
||||||
|
@ -42,19 +42,11 @@
|
|||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
||||||
|
|
||||||
static int runIO(const char *path,
|
static int
|
||||||
int oflags,
|
prepare(const char *path, int oflags, int mode,
|
||||||
int mode,
|
unsigned long long offset)
|
||||||
unsigned long long offset,
|
|
||||||
unsigned long long length)
|
|
||||||
{
|
{
|
||||||
char *buf = NULL;
|
int fd = -1;
|
||||||
size_t buflen = 1024*1024;
|
|
||||||
int fd;
|
|
||||||
int ret = -1;
|
|
||||||
int fdin, fdout;
|
|
||||||
const char *fdinname, *fdoutname;
|
|
||||||
unsigned long long total = 0;
|
|
||||||
|
|
||||||
if (oflags & O_CREAT) {
|
if (oflags & O_CREAT) {
|
||||||
fd = open(path, oflags, mode);
|
fd = open(path, oflags, mode);
|
||||||
@ -70,10 +62,25 @@ static int runIO(const char *path,
|
|||||||
if (lseek(fd, offset, SEEK_SET) < 0) {
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
||||||
virReportSystemError(errno, _("Unable to seek %s to %llu"),
|
virReportSystemError(errno, _("Unable to seek %s to %llu"),
|
||||||
path, offset);
|
path, offset);
|
||||||
|
VIR_FORCE_CLOSE(fd);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
runIO(const char *path, int fd, int oflags, unsigned long long length)
|
||||||
|
{
|
||||||
|
char *buf = NULL;
|
||||||
|
size_t buflen = 1024*1024;
|
||||||
|
int ret = -1;
|
||||||
|
int fdin, fdout;
|
||||||
|
const char *fdinname, *fdoutname;
|
||||||
|
unsigned long long total = 0;
|
||||||
|
|
||||||
if (VIR_ALLOC_N(buf, buflen) < 0) {
|
if (VIR_ALLOC_N(buf, buflen) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -138,61 +145,109 @@ cleanup:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
static const char *program_name;
|
||||||
|
|
||||||
|
ATTRIBUTE_NORETURN static void
|
||||||
|
usage(int status)
|
||||||
|
{
|
||||||
|
if (status) {
|
||||||
|
fprintf(stderr, _("%s: try --help for more details"), program_name);
|
||||||
|
} else {
|
||||||
|
printf(_("Usage: %s FILENAME OFLAGS MODE OFFSET LENGTH DELETE\n"
|
||||||
|
" or: %s FILENAME LENGTH FD\n"),
|
||||||
|
program_name, program_name);
|
||||||
|
}
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *path;
|
const char *path;
|
||||||
virErrorPtr err;
|
virErrorPtr err;
|
||||||
unsigned long long offset;
|
unsigned long long offset;
|
||||||
unsigned long long length;
|
unsigned long long length;
|
||||||
int oflags;
|
int oflags = -1;
|
||||||
int mode;
|
int mode;
|
||||||
unsigned int delete;
|
unsigned int delete = 0;
|
||||||
|
int fd = -1;
|
||||||
|
int lengthIndex = 0;
|
||||||
|
|
||||||
|
program_name = argv[0];
|
||||||
|
|
||||||
if (setlocale(LC_ALL, "") == NULL ||
|
if (setlocale(LC_ALL, "") == NULL ||
|
||||||
bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
|
bindtextdomain(PACKAGE, LOCALEDIR) == NULL ||
|
||||||
textdomain(PACKAGE) == NULL) {
|
textdomain(PACKAGE) == NULL) {
|
||||||
fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
|
fprintf(stderr, _("%s: initialization failed\n"), program_name);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virThreadInitialize() < 0 ||
|
if (virThreadInitialize() < 0 ||
|
||||||
virErrorInitialize() < 0 ||
|
virErrorInitialize() < 0 ||
|
||||||
virRandomInitialize(time(NULL) ^ getpid())) {
|
virRandomInitialize(time(NULL) ^ getpid())) {
|
||||||
fprintf(stderr, _("%s: initialization failed\n"), argv[0]);
|
fprintf(stderr, _("%s: initialization failed\n"), program_name);
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc != 7) {
|
|
||||||
fprintf(stderr, _("%s: syntax FILENAME FLAGS MODE OFFSET LENGTH DELETE\n"), argv[0]);
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
path = argv[1];
|
path = argv[1];
|
||||||
|
|
||||||
if (virStrToLong_i(argv[2], NULL, 10, &oflags) < 0) {
|
if (argc > 1 && STREQ(argv[1], "--help"))
|
||||||
fprintf(stderr, _("%s: malformed file flags %s"), argv[0], argv[2]);
|
usage(EXIT_SUCCESS);
|
||||||
|
if (argc == 7) { /* FILENAME OFLAGS MODE OFFSET LENGTH DELETE */
|
||||||
|
lengthIndex = 5;
|
||||||
|
if (virStrToLong_i(argv[2], NULL, 10, &oflags) < 0) {
|
||||||
|
fprintf(stderr, _("%s: malformed file flags %s"),
|
||||||
|
program_name, argv[2]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (virStrToLong_i(argv[3], NULL, 10, &mode) < 0) {
|
||||||
|
fprintf(stderr, _("%s: malformed file mode %s"),
|
||||||
|
program_name, argv[3]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (virStrToLong_ull(argv[4], NULL, 10, &offset) < 0) {
|
||||||
|
fprintf(stderr, _("%s: malformed file offset %s"),
|
||||||
|
program_name, argv[4]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (argc == 7 && virStrToLong_ui(argv[6], NULL, 10, &delete) < 0) {
|
||||||
|
fprintf(stderr, _("%s: malformed delete flag %s"),
|
||||||
|
program_name, argv[6]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
fd = prepare(path, oflags, mode, offset);
|
||||||
|
} else if (argc == 4) { /* FILENAME LENGTH FD */
|
||||||
|
lengthIndex = 2;
|
||||||
|
if (virStrToLong_i(argv[3], NULL, 10, &fd) < 0) {
|
||||||
|
fprintf(stderr, _("%s: malformed fd %s"),
|
||||||
|
program_name, argv[3]);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#ifdef F_GETFL
|
||||||
|
oflags = fcntl(fd, F_GETFL);
|
||||||
|
#else
|
||||||
|
/* Stupid mingw. */
|
||||||
|
if (fd == STDIN_FILENO)
|
||||||
|
oflags = O_RDONLY;
|
||||||
|
else if (fd == STDOUT_FILENO)
|
||||||
|
oflags = O_WRONLY;
|
||||||
|
#endif
|
||||||
|
if (oflags < 0) {
|
||||||
|
fprintf(stderr, _("%s: unable to determine access mode of fd %d"),
|
||||||
|
program_name, fd);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else { /* unknown argc pattern */
|
||||||
|
usage(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virStrToLong_ull(argv[lengthIndex], NULL, 10, &length) < 0) {
|
||||||
|
fprintf(stderr, _("%s: malformed file length %s"),
|
||||||
|
program_name, argv[lengthIndex]);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virStrToLong_i(argv[3], NULL, 10, &mode) < 0) {
|
if (fd < 0 || runIO(path, fd, oflags, length) < 0)
|
||||||
fprintf(stderr, _("%s: malformed file mode %s"), argv[0], argv[3]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virStrToLong_ull(argv[4], NULL, 10, &offset) < 0) {
|
|
||||||
fprintf(stderr, _("%s: malformed file offset %s"), argv[0], argv[4]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (virStrToLong_ull(argv[5], NULL, 10, &length) < 0) {
|
|
||||||
fprintf(stderr, _("%s: malformed file length %s"), argv[0], argv[5]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if (virStrToLong_ui(argv[6], NULL, 10, &delete) < 0) {
|
|
||||||
fprintf(stderr, _("%s: malformed delete flag %s"), argv[0],argv[6]);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runIO(path, oflags, mode, offset, length) < 0)
|
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (delete)
|
if (delete)
|
||||||
@ -203,9 +258,10 @@ int main(int argc, char **argv)
|
|||||||
error:
|
error:
|
||||||
err = virGetLastError();
|
err = virGetLastError();
|
||||||
if (err) {
|
if (err) {
|
||||||
fprintf(stderr, "%s: %s\n", argv[0], err->message);
|
fprintf(stderr, "%s: %s\n", program_name, err->message);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, _("%s: unknown failure with %s\n"), argv[0], path);
|
fprintf(stderr, _("%s: unknown failure with %s\n"),
|
||||||
|
program_name, path);
|
||||||
}
|
}
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user