Extend the original file before to the new size before updating it.
Instead of opening the original file for writing w/ tuncation, we first extend the file with zeroes (by writing, not seeking), then overwrite it. This should allow sudo to fail early if the disk is out of space before it overwrites the original file.
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-License-Identifier: ISC
|
* SPDX-License-Identifier: ISC
|
||||||
*
|
*
|
||||||
* Copyright (c) 2004-2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
|
* Copyright (c) 2004-2008, 2010-2020 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
@@ -650,6 +650,51 @@ sudo_edit_create_tfiles(struct command_details *command_details,
|
|||||||
debug_return_int(j);
|
debug_return_int(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extend the given fd to the specified size in bytes.
|
||||||
|
* We do this to allocate disk space up-front before overwriting
|
||||||
|
* the original file with the temporary. Otherwise, we could
|
||||||
|
* we run out of disk space after truncating the original file.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sudo_edit_extend_file(int fd, off_t new_size)
|
||||||
|
{
|
||||||
|
off_t old_size, size;
|
||||||
|
ssize_t nwritten;
|
||||||
|
char zeroes[1024] = { '\0' };
|
||||||
|
debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
|
||||||
|
|
||||||
|
if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
|
||||||
|
sudo_warn("lseek");
|
||||||
|
debug_return_int(-1);
|
||||||
|
}
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
|
||||||
|
__func__, (long long)old_size, (long long)new_size);
|
||||||
|
|
||||||
|
for (size = old_size; size < new_size; size += nwritten) {
|
||||||
|
size_t len = new_size - size;
|
||||||
|
if (len > sizeof(zeroes))
|
||||||
|
len = sizeof(zeroes);
|
||||||
|
nwritten = write(fd, zeroes, len);
|
||||||
|
if (nwritten == -1) {
|
||||||
|
int serrno = errno;
|
||||||
|
if (ftruncate(fd, old_size) == -1) {
|
||||||
|
sudo_debug_printf(
|
||||||
|
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
||||||
|
"unable to truncate to %lld", (long long)old_size);
|
||||||
|
}
|
||||||
|
errno = serrno;
|
||||||
|
debug_return_int(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lseek(fd, 0, SEEK_SET) == -1) {
|
||||||
|
sudo_warn("lseek");
|
||||||
|
debug_return_int(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_return_int(0);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the temporary files specified in tf to the originals.
|
* Copy the temporary files specified in tf to the originals.
|
||||||
* Returns the number of copy errors or 0 if completely successful.
|
* Returns the number of copy errors or 0 if completely successful.
|
||||||
@@ -708,38 +753,53 @@ sudo_edit_copy_tfiles(struct command_details *command_details,
|
|||||||
switch_user(command_details->euid, command_details->egid,
|
switch_user(command_details->euid, command_details->egid,
|
||||||
command_details->ngroups, command_details->groups);
|
command_details->ngroups, command_details->groups);
|
||||||
oldmask = umask(command_details->umask);
|
oldmask = umask(command_details->umask);
|
||||||
ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT,
|
ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_CREAT,
|
||||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
|
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
|
||||||
umask(oldmask);
|
umask(oldmask);
|
||||||
switch_user(ROOT_UID, user_details.egid,
|
switch_user(ROOT_UID, user_details.egid,
|
||||||
user_details.ngroups, user_details.groups);
|
user_details.ngroups, user_details.groups);
|
||||||
if (ofd == -1) {
|
if (ofd == -1)
|
||||||
sudo_warn(U_("unable to write to %s"), tf[i].ofile);
|
goto write_error;
|
||||||
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
|
/* Extend the file to the new size if larger before copying. */
|
||||||
close(tfd);
|
if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
|
||||||
errors++;
|
if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
|
||||||
continue;
|
goto write_error;
|
||||||
}
|
}
|
||||||
|
/* Overwrite the old file with the new contents. */
|
||||||
while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
|
while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
|
||||||
if ((nwritten = write(ofd, buf, nread)) != nread) {
|
ssize_t off = 0;
|
||||||
|
do {
|
||||||
|
nwritten = write(ofd, buf + off, nread - off);
|
||||||
if (nwritten == -1)
|
if (nwritten == -1)
|
||||||
sudo_warn("%s", tf[i].ofile);
|
goto write_error;
|
||||||
else
|
off += nwritten;
|
||||||
sudo_warnx(U_("%s: short write"), tf[i].ofile);
|
} while (nread > off);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (nread == 0) {
|
if (nread == 0) {
|
||||||
/* success, got EOF */
|
/* success, read to EOF */
|
||||||
|
if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
|
||||||
|
/* We don't open with O_TRUNC so must truncate manually. */
|
||||||
|
if (ftruncate(ofd, sb.st_size) == -1) {
|
||||||
|
sudo_debug_printf(
|
||||||
|
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
|
||||||
|
"unable to truncate %s to %lld", tf[i].ofile,
|
||||||
|
(long long)sb.st_size);
|
||||||
|
goto write_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
unlink(tf[i].tfile);
|
unlink(tf[i].tfile);
|
||||||
} else if (nread < 0) {
|
} else if (nread < 0) {
|
||||||
sudo_warn(U_("unable to read temporary file"));
|
sudo_warn(U_("unable to read temporary file"));
|
||||||
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
|
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
|
||||||
|
errors++;
|
||||||
} else {
|
} else {
|
||||||
|
write_error:
|
||||||
sudo_warn(U_("unable to write to %s"), tf[i].ofile);
|
sudo_warn(U_("unable to write to %s"), tf[i].ofile);
|
||||||
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
|
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
|
||||||
|
errors++;
|
||||||
}
|
}
|
||||||
close(ofd);
|
if (ofd != -1)
|
||||||
|
close(ofd);
|
||||||
close(tfd);
|
close(tfd);
|
||||||
}
|
}
|
||||||
debug_return_int(errors);
|
debug_return_int(errors);
|
||||||
@@ -1065,6 +1125,7 @@ cleanup:
|
|||||||
for (i = 0; i < nfiles; i++) {
|
for (i = 0; i < nfiles; i++) {
|
||||||
if (tf[i].tfile != NULL)
|
if (tf[i].tfile != NULL)
|
||||||
unlink(tf[i].tfile);
|
unlink(tf[i].tfile);
|
||||||
|
free(tf[i].tfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(tf);
|
free(tf);
|
||||||
|
Reference in New Issue
Block a user