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