]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2011 New Dream Network | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
31f18b77 FG |
15 | #include "common/safe_io.h" |
16 | #include "include/compat.h" | |
17 | ||
7c673cae FG |
18 | #include <stdio.h> |
19 | #include <string.h> | |
20 | #include <unistd.h> | |
21 | #include <errno.h> | |
22 | #include <fcntl.h> | |
23 | #include <limits.h> | |
24 | ||
7c673cae FG |
25 | ssize_t safe_read(int fd, void *buf, size_t count) |
26 | { | |
27 | size_t cnt = 0; | |
28 | ||
29 | while (cnt < count) { | |
30 | ssize_t r = read(fd, buf, count - cnt); | |
31 | if (r <= 0) { | |
32 | if (r == 0) { | |
33 | // EOF | |
34 | return cnt; | |
35 | } | |
36 | if (errno == EINTR) | |
37 | continue; | |
38 | return -errno; | |
39 | } | |
40 | cnt += r; | |
41 | buf = (char *)buf + r; | |
42 | } | |
43 | return cnt; | |
44 | } | |
45 | ||
46 | ssize_t safe_read_exact(int fd, void *buf, size_t count) | |
47 | { | |
48 | ssize_t ret = safe_read(fd, buf, count); | |
49 | if (ret < 0) | |
50 | return ret; | |
51 | if ((size_t)ret != count) | |
52 | return -EDOM; | |
53 | return 0; | |
54 | } | |
55 | ||
56 | ssize_t safe_write(int fd, const void *buf, size_t count) | |
57 | { | |
58 | while (count > 0) { | |
59 | ssize_t r = write(fd, buf, count); | |
60 | if (r < 0) { | |
61 | if (errno == EINTR) | |
62 | continue; | |
63 | return -errno; | |
64 | } | |
65 | count -= r; | |
66 | buf = (char *)buf + r; | |
67 | } | |
68 | return 0; | |
69 | } | |
70 | ||
71 | ssize_t safe_pread(int fd, void *buf, size_t count, off_t offset) | |
72 | { | |
73 | size_t cnt = 0; | |
74 | char *b = (char*)buf; | |
75 | ||
76 | while (cnt < count) { | |
77 | ssize_t r = pread(fd, b + cnt, count - cnt, offset + cnt); | |
78 | if (r <= 0) { | |
79 | if (r == 0) { | |
80 | // EOF | |
81 | return cnt; | |
82 | } | |
83 | if (errno == EINTR) | |
84 | continue; | |
85 | return -errno; | |
86 | } | |
87 | ||
88 | cnt += r; | |
89 | } | |
90 | return cnt; | |
91 | } | |
92 | ||
93 | ssize_t safe_pread_exact(int fd, void *buf, size_t count, off_t offset) | |
94 | { | |
95 | ssize_t ret = safe_pread(fd, buf, count, offset); | |
96 | if (ret < 0) | |
97 | return ret; | |
98 | if ((size_t)ret != count) | |
99 | return -EDOM; | |
100 | return 0; | |
101 | } | |
102 | ||
103 | ssize_t safe_pwrite(int fd, const void *buf, size_t count, off_t offset) | |
104 | { | |
105 | while (count > 0) { | |
106 | ssize_t r = pwrite(fd, buf, count, offset); | |
107 | if (r < 0) { | |
108 | if (errno == EINTR) | |
109 | continue; | |
110 | return -errno; | |
111 | } | |
112 | count -= r; | |
113 | buf = (char *)buf + r; | |
114 | offset += r; | |
115 | } | |
116 | return 0; | |
117 | } | |
118 | ||
119 | #ifdef CEPH_HAVE_SPLICE | |
120 | ssize_t safe_splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, | |
121 | size_t len, unsigned int flags) | |
122 | { | |
123 | size_t cnt = 0; | |
124 | ||
125 | while (cnt < len) { | |
126 | ssize_t r = splice(fd_in, off_in, fd_out, off_out, len - cnt, flags); | |
127 | if (r <= 0) { | |
128 | if (r == 0) { | |
129 | // EOF | |
130 | return cnt; | |
131 | } | |
132 | if (errno == EINTR) | |
133 | continue; | |
134 | if (errno == EAGAIN) | |
135 | break; | |
136 | return -errno; | |
137 | } | |
138 | cnt += r; | |
139 | } | |
140 | return cnt; | |
141 | } | |
142 | ||
143 | ssize_t safe_splice_exact(int fd_in, off_t *off_in, int fd_out, | |
144 | off_t *off_out, size_t len, unsigned int flags) | |
145 | { | |
146 | ssize_t ret = safe_splice(fd_in, off_in, fd_out, off_out, len, flags); | |
147 | if (ret < 0) | |
148 | return ret; | |
149 | if ((size_t)ret != len) | |
150 | return -EDOM; | |
151 | return 0; | |
152 | } | |
153 | #endif | |
154 | ||
155 | int safe_write_file(const char *base, const char *file, | |
eafe8130 TL |
156 | const char *val, size_t vallen, |
157 | unsigned mode) | |
7c673cae FG |
158 | { |
159 | int ret; | |
160 | char fn[PATH_MAX]; | |
161 | char tmp[PATH_MAX]; | |
162 | int fd; | |
163 | ||
164 | // does the file already have correct content? | |
165 | char oldval[80]; | |
166 | ret = safe_read_file(base, file, oldval, sizeof(oldval)); | |
167 | if (ret == (int)vallen && memcmp(oldval, val, vallen) == 0) | |
168 | return 0; // yes. | |
169 | ||
170 | snprintf(fn, sizeof(fn), "%s/%s", base, file); | |
171 | snprintf(tmp, sizeof(tmp), "%s/%s.tmp", base, file); | |
eafe8130 | 172 | fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, mode); |
7c673cae FG |
173 | if (fd < 0) { |
174 | ret = errno; | |
175 | return -ret; | |
176 | } | |
177 | ret = safe_write(fd, val, vallen); | |
178 | if (ret) { | |
179 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
180 | return ret; | |
181 | } | |
182 | ||
183 | ret = fsync(fd); | |
184 | if (ret < 0) ret = -errno; | |
185 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
186 | if (ret < 0) { | |
187 | unlink(tmp); | |
188 | return ret; | |
189 | } | |
190 | ret = rename(tmp, fn); | |
191 | if (ret < 0) { | |
192 | ret = -errno; | |
193 | unlink(tmp); | |
194 | return ret; | |
195 | } | |
196 | ||
197 | fd = open(base, O_RDONLY); | |
198 | if (fd < 0) { | |
199 | ret = -errno; | |
200 | return ret; | |
201 | } | |
202 | ret = fsync(fd); | |
203 | if (ret < 0) ret = -errno; | |
204 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
205 | ||
206 | return ret; | |
207 | } | |
208 | ||
209 | int safe_read_file(const char *base, const char *file, | |
210 | char *val, size_t vallen) | |
211 | { | |
212 | char fn[PATH_MAX]; | |
213 | int fd, len; | |
214 | ||
215 | snprintf(fn, sizeof(fn), "%s/%s", base, file); | |
216 | fd = open(fn, O_RDONLY); | |
217 | if (fd < 0) { | |
218 | return -errno; | |
219 | } | |
220 | len = safe_read(fd, val, vallen); | |
221 | if (len < 0) { | |
222 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
223 | return len; | |
224 | } | |
225 | // close sometimes returns errors, but only after write() | |
226 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
227 | ||
228 | return len; | |
229 | } |