Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/copy_file.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2020-2021 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <sys/stat.h>
22
23
#include <stdlib.h>
24
#include <unistd.h>
25
#include <errno.h>
26
27
#include <sudo.h>
28
#include <sudo_edit.h>
29
30
/*
31
* Extend the given fd to the specified size in bytes.
32
* We do this to allocate disk space up-front before overwriting
33
* the original file with the temporary. Otherwise, we could
34
* run out of disk space after truncating the original file.
35
*/
36
static int
37
sudo_extend_file(int fd, const char *name, off_t new_size)
38
{
39
off_t old_size, size;
40
ssize_t nwritten;
41
int serrno;
42
char zeroes[BUFSIZ] = { '\0' };
43
debug_decl(sudo_extend_file, SUDO_DEBUG_UTIL);
44
45
if ((old_size = lseek(fd, 0, SEEK_END)) < 0) {
46
sudo_warn("lseek");
47
debug_return_int(-1);
48
}
49
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending %s from %lld to %lld",
50
__func__, name, (long long)old_size, (long long)new_size);
51
52
for (size = old_size; size < new_size; ) {
53
off_t len = new_size - size;
54
if (len > ssizeof(zeroes))
55
len = ssizeof(zeroes);
56
nwritten = write(fd, zeroes, (size_t)len);
57
if (nwritten < 0 || nwritten > OFF_T_MAX - size) {
58
goto fail;
59
}
60
size += nwritten;
61
}
62
if (lseek(fd, 0, SEEK_SET) < 0) {
63
sudo_warn("lseek");
64
debug_return_int(-1);
65
}
66
67
debug_return_int(0);
68
fail:
69
serrno = errno;
70
if (ftruncate(fd, old_size) != 0) {
71
sudo_debug_printf(
72
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
73
"unable to truncate %s to %lld", name, (long long)old_size);
74
}
75
errno = serrno;
76
debug_return_int(-1);
77
}
78
79
/*
80
* Copy the contents of src_fd into dst_fd.
81
* Returns 0 on success or -1 on error.
82
*/
83
int
84
sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst,
85
int dst_fd, off_t dst_len)
86
{
87
char buf[BUFSIZ];
88
ssize_t nwritten, nread;
89
debug_decl(sudo_copy_file, SUDO_DEBUG_UTIL);
90
91
/* Prompt the user before zeroing out an existing file. */
92
if (dst_len > 0 && src_len == 0) {
93
fprintf(stderr, U_("%s: truncate %s to zero bytes? (y/n) [n] "),
94
getprogname(), dst);
95
if (fgets(buf, sizeof(buf), stdin) == NULL ||
96
(buf[0] != 'y' && buf[0] != 'Y')) {
97
sudo_warnx(U_("not overwriting %s"), dst);
98
debug_return_int(0);
99
}
100
}
101
102
/* Extend the file to the new size if larger before copying. */
103
if (dst_len > 0 && src_len > dst_len) {
104
if (sudo_extend_file(dst_fd, dst, src_len) == -1)
105
goto write_error;
106
}
107
108
/* Overwrite the old file with the new contents. */
109
for (;;) {
110
ssize_t off = 0;
111
nread = read(src_fd, buf, sizeof(buf));
112
if (nread <= 0) {
113
if (nread == 0)
114
break;
115
sudo_warn(U_("unable to read from %s"), src);
116
debug_return_int(-1);
117
}
118
while (nread > off) {
119
nwritten = write(dst_fd, buf + off, (size_t)(nread - off));
120
if (nwritten < 0 || nwritten > SSIZE_MAX - off)
121
goto write_error;
122
off += nwritten;
123
}
124
}
125
126
/* Did the file shrink? */
127
if (src_len < dst_len) {
128
/* We don't open with O_TRUNC so must truncate manually. */
129
if (ftruncate(dst_fd, src_len) != 0) {
130
sudo_debug_printf(
131
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
132
"unable to truncate %s to %lld", dst, (long long)src_len);
133
goto write_error;
134
}
135
}
136
137
debug_return_int(0);
138
write_error:
139
sudo_warn(U_("unable to write to %s"), dst);
140
debug_return_int(-1);
141
}
142
143
bool
144
sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
145
{
146
struct stat sbuf;
147
debug_decl(sudo_check_temp_file, SUDO_DEBUG_UTIL);
148
149
if (sb == NULL)
150
sb = &sbuf;
151
152
if (fstat(tfd, sb) == -1) {
153
sudo_warn(U_("unable to stat %s"), tfile);
154
debug_return_bool(false);
155
}
156
if (!S_ISREG(sb->st_mode)) {
157
sudo_warnx(U_("%s: not a regular file"), tfile);
158
debug_return_bool(false);
159
}
160
if ((sb->st_mode & ALLPERMS) != (S_IRUSR|S_IWUSR)) {
161
sudo_warnx(U_("%s: bad file mode: 0%o"), tfile,
162
(unsigned int)(sb->st_mode & ALLPERMS));
163
debug_return_bool(false);
164
}
165
if (sb->st_uid != uid) {
166
sudo_warnx(U_("%s is owned by uid %u, should be %u"),
167
tfile, (unsigned int)sb->st_uid, (unsigned int)uid);
168
debug_return_bool(false);
169
}
170
debug_return_bool(true);
171
}
172
173