Fix regression in header backup (1.5.1).
[platform/upstream/cryptsetup.git] / lib / utils.c
1 /*
2  * utils - miscellaneous device utilities for cryptsetup
3  *
4  * Copyright (C) 2004, Christophe Saout <christophe@saout.de>
5  * Copyright (C) 2004-2007, Clemens Fruhwirth <clemens@endorphin.org>
6  * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <sys/mman.h>
27 #include <sys/resource.h>
28
29 #include "internal.h"
30
31 unsigned crypt_getpagesize(void)
32 {
33         long r = sysconf(_SC_PAGESIZE);
34         return r < 0 ? DEFAULT_MEM_ALIGNMENT : r;
35 }
36
37 static int get_alignment(int fd)
38 {
39         int alignment = DEFAULT_MEM_ALIGNMENT;
40
41 #ifdef _PC_REC_XFER_ALIGN
42         alignment = fpathconf(fd, _PC_REC_XFER_ALIGN);
43         if (alignment < 0)
44                 alignment = DEFAULT_MEM_ALIGNMENT;
45 #endif
46         return alignment;
47 }
48
49 static void *aligned_malloc(void **base, int size, int alignment)
50 {
51 #ifdef HAVE_POSIX_MEMALIGN
52         return posix_memalign(base, alignment, size) ? NULL : *base;
53 #else
54 /* Credits go to Michal's padlock patches for this alignment code */
55         char *ptr;
56
57         ptr  = malloc(size + alignment);
58         if(ptr == NULL) return NULL;
59
60         *base = ptr;
61         if(alignment > 1 && ((long)ptr & (alignment - 1))) {
62                 ptr += alignment - ((long)(ptr) & (alignment - 1));
63         }
64         return ptr;
65 #endif
66 }
67
68 ssize_t write_blockwise(int fd, int bsize, void *orig_buf, size_t count)
69 {
70         void *hangover_buf, *hangover_buf_base = NULL;
71         void *buf, *buf_base = NULL;
72         int r, hangover, solid, alignment;
73         ssize_t ret = -1;
74
75         if (fd == -1 || !orig_buf || bsize <= 0)
76                 return -1;
77
78         hangover = count % bsize;
79         solid = count - hangover;
80         alignment = get_alignment(fd);
81
82         if ((long)orig_buf & (alignment - 1)) {
83                 buf = aligned_malloc(&buf_base, count, alignment);
84                 if (!buf)
85                         goto out;
86                 memcpy(buf, orig_buf, count);
87         } else
88                 buf = orig_buf;
89
90         r = write(fd, buf, solid);
91         if (r < 0 || r != solid)
92                 goto out;
93
94         if (hangover) {
95                 hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment);
96                 if (!hangover_buf)
97                         goto out;
98
99                 r = read(fd, hangover_buf, bsize);
100                 if (r < 0 || r < hangover)
101                         goto out;
102
103                 if (r < bsize)
104                         bsize = r;
105
106                 r = lseek(fd, -bsize, SEEK_CUR);
107                 if (r < 0)
108                         goto out;
109                 memcpy(hangover_buf, (char*)buf + solid, hangover);
110
111                 r = write(fd, hangover_buf, bsize);
112                 if (r < 0 || r < hangover)
113                         goto out;
114         }
115         ret = count;
116 out:
117         free(hangover_buf_base);
118         if (buf != orig_buf)
119                 free(buf_base);
120         return ret;
121 }
122
123 ssize_t read_blockwise(int fd, int bsize, void *orig_buf, size_t count) {
124         void *hangover_buf, *hangover_buf_base = NULL;
125         void *buf, *buf_base = NULL;
126         int r, hangover, solid, alignment;
127         ssize_t ret = -1;
128
129         if (fd == -1 || !orig_buf || bsize <= 0)
130                 return -1;
131
132         hangover = count % bsize;
133         solid = count - hangover;
134         alignment = get_alignment(fd);
135
136         if ((long)orig_buf & (alignment - 1)) {
137                 buf = aligned_malloc(&buf_base, count, alignment);
138                 if (!buf)
139                         return -1;
140         } else
141                 buf = orig_buf;
142
143         r = read(fd, buf, solid);
144         if(r < 0 || r != solid)
145                 goto out;
146
147         if (hangover) {
148                 hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment);
149                 if (!hangover_buf)
150                         goto out;
151                 r = read(fd, hangover_buf, bsize);
152                 if (r <  0 || r < hangover)
153                         goto out;
154
155                 memcpy((char *)buf + solid, hangover_buf, hangover);
156         }
157         ret = count;
158 out:
159         free(hangover_buf_base);
160         if (buf != orig_buf) {
161                 memcpy(orig_buf, buf, count);
162                 free(buf_base);
163         }
164         return ret;
165 }
166
167 /*
168  * Combines llseek with blockwise write. write_blockwise can already deal with short writes
169  * but we also need a function to deal with short writes at the start. But this information
170  * is implicitly included in the read/write offset, which can not be set to non-aligned
171  * boundaries. Hence, we combine llseek with write.
172  */
173 ssize_t write_lseek_blockwise(int fd, int bsize, char *buf, size_t count, off_t offset) {
174         char *frontPadBuf;
175         void *frontPadBuf_base = NULL;
176         int r, frontHang;
177         size_t innerCount = 0;
178         ssize_t ret = -1;
179
180         if (fd == -1 || !buf || bsize <= 0)
181                 return -1;
182
183         frontHang = offset % bsize;
184
185         if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
186                 goto out;
187
188         if (frontHang) {
189                 frontPadBuf = aligned_malloc(&frontPadBuf_base,
190                                              bsize, get_alignment(fd));
191                 if (!frontPadBuf)
192                         goto out;
193
194                 r = read(fd, frontPadBuf, bsize);
195                 if (r < 0 || r != bsize)
196                         goto out;
197
198                 innerCount = bsize - frontHang;
199                 if (innerCount > count)
200                         innerCount = count;
201
202                 memcpy(frontPadBuf + frontHang, buf, innerCount);
203
204                 if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
205                         goto out;
206
207                 r = write(fd, frontPadBuf, bsize);
208                 if (r < 0 || r != bsize)
209                         goto out;
210
211                 buf += innerCount;
212                 count -= innerCount;
213         }
214
215         ret = count ? write_blockwise(fd, bsize, buf, count) : 0;
216         if (ret >= 0)
217                 ret += innerCount;
218 out:
219         free(frontPadBuf_base);
220
221         return ret;
222 }
223
224 /* MEMLOCK */
225 #define DEFAULT_PROCESS_PRIORITY -18
226
227 static int _priority;
228 static int _memlock_count = 0;
229
230 // return 1 if memory is locked
231 int crypt_memlock_inc(struct crypt_device *ctx)
232 {
233         if (!_memlock_count++) {
234                 log_dbg("Locking memory.");
235                 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
236                         log_dbg("Cannot lock memory with mlockall.");
237                         _memlock_count--;
238                         return 0;
239                 }
240                 errno = 0;
241                 if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno)
242                         log_err(ctx, _("Cannot get process priority.\n"));
243                 else
244                         if (setpriority(PRIO_PROCESS, 0, DEFAULT_PROCESS_PRIORITY))
245                                 log_dbg("setpriority %d failed: %s",
246                                         DEFAULT_PROCESS_PRIORITY, strerror(errno));
247         }
248         return _memlock_count ? 1 : 0;
249 }
250
251 int crypt_memlock_dec(struct crypt_device *ctx)
252 {
253         if (_memlock_count && (!--_memlock_count)) {
254                 log_dbg("Unlocking memory.");
255                 if (munlockall() == -1)
256                         log_err(ctx, _("Cannot unlock memory.\n"));
257                 if (setpriority(PRIO_PROCESS, 0, _priority))
258                         log_dbg("setpriority %d failed: %s", _priority, strerror(errno));
259         }
260         return _memlock_count ? 1 : 0;
261 }