2231522762bce81ac83aa69ecb321cadb7ffd0d4
[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         return (unsigned)sysconf(_SC_PAGESIZE);
34 }
35
36 static int get_alignment(int fd)
37 {
38         int alignment = DEFAULT_MEM_ALIGNMENT;
39
40 #ifdef _PC_REC_XFER_ALIGN
41         alignment = fpathconf(fd, _PC_REC_XFER_ALIGN);
42         if (alignment < 0)
43                 alignment = DEFAULT_MEM_ALIGNMENT;
44 #endif
45         return alignment;
46 }
47
48 static void *aligned_malloc(void **base, int size, int alignment)
49 {
50 #ifdef HAVE_POSIX_MEMALIGN
51         return posix_memalign(base, alignment, size) ? NULL : *base;
52 #else
53 /* Credits go to Michal's padlock patches for this alignment code */
54         char *ptr;
55
56         ptr  = malloc(size + alignment);
57         if(ptr == NULL) return NULL;
58
59         *base = ptr;
60         if(alignment > 1 && ((long)ptr & (alignment - 1))) {
61                 ptr += alignment - ((long)(ptr) & (alignment - 1));
62         }
63         return ptr;
64 #endif
65 }
66
67 ssize_t write_blockwise(int fd, int bsize, void *orig_buf, size_t count)
68 {
69         void *hangover_buf, *hangover_buf_base = NULL;
70         void *buf, *buf_base = NULL;
71         int r, hangover, solid, alignment;
72         ssize_t ret = -1;
73
74         if (fd == -1 || !orig_buf || bsize <= 0)
75                 return -1;
76
77         hangover = count % bsize;
78         solid = count - hangover;
79         alignment = get_alignment(fd);
80
81         if ((long)orig_buf & (alignment - 1)) {
82                 buf = aligned_malloc(&buf_base, count, alignment);
83                 if (!buf)
84                         goto out;
85                 memcpy(buf, orig_buf, count);
86         } else
87                 buf = orig_buf;
88
89         r = write(fd, buf, solid);
90         if (r < 0 || r != solid)
91                 goto out;
92
93         if (hangover) {
94                 hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment);
95                 if (!hangover_buf)
96                         goto out;
97
98                 r = read(fd, hangover_buf, bsize);
99                 if (r < 0 || r != bsize)
100                         goto out;
101
102                 r = lseek(fd, -bsize, SEEK_CUR);
103                 if (r < 0)
104                         goto out;
105                 memcpy(hangover_buf, (char*)buf + solid, hangover);
106
107                 r = write(fd, hangover_buf, bsize);
108                 if (r < 0 || r < hangover)
109                         goto out;
110         }
111         ret = count;
112 out:
113         free(hangover_buf_base);
114         if (buf != orig_buf)
115                 free(buf_base);
116         return ret;
117 }
118
119 ssize_t read_blockwise(int fd, int bsize, void *orig_buf, size_t count) {
120         void *hangover_buf, *hangover_buf_base = NULL;
121         void *buf, *buf_base = NULL;
122         int r, hangover, solid, alignment;
123         ssize_t ret = -1;
124
125         if (fd == -1 || !orig_buf || bsize <= 0)
126                 return -1;
127
128         hangover = count % bsize;
129         solid = count - hangover;
130         alignment = get_alignment(fd);
131
132         if ((long)orig_buf & (alignment - 1)) {
133                 buf = aligned_malloc(&buf_base, count, alignment);
134                 if (!buf)
135                         return -1;
136         } else
137                 buf = orig_buf;
138
139         r = read(fd, buf, solid);
140         if(r < 0 || r != solid)
141                 goto out;
142
143         if (hangover) {
144                 hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment);
145                 if (!hangover_buf)
146                         goto out;
147                 r = read(fd, hangover_buf, bsize);
148                 if (r <  0 || r < hangover)
149                         goto out;
150
151                 memcpy((char *)buf + solid, hangover_buf, hangover);
152         }
153         ret = count;
154 out:
155         free(hangover_buf_base);
156         if (buf != orig_buf) {
157                 memcpy(orig_buf, buf, count);
158                 free(buf_base);
159         }
160         return ret;
161 }
162
163 /*
164  * Combines llseek with blockwise write. write_blockwise can already deal with short writes
165  * but we also need a function to deal with short writes at the start. But this information
166  * is implicitly included in the read/write offset, which can not be set to non-aligned
167  * boundaries. Hence, we combine llseek with write.
168  */
169 ssize_t write_lseek_blockwise(int fd, int bsize, char *buf, size_t count, off_t offset) {
170         char *frontPadBuf;
171         void *frontPadBuf_base = NULL;
172         int r, frontHang;
173         size_t innerCount = 0;
174         ssize_t ret = -1;
175
176         if (fd == -1 || !buf || bsize <= 0)
177                 return -1;
178
179         frontHang = offset % bsize;
180
181         if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
182                 goto out;
183
184         if (frontHang) {
185                 frontPadBuf = aligned_malloc(&frontPadBuf_base,
186                                              bsize, get_alignment(fd));
187                 if (!frontPadBuf)
188                         goto out;
189
190                 r = read(fd, frontPadBuf, bsize);
191                 if (r < 0 || r != bsize)
192                         goto out;
193
194                 innerCount = bsize - frontHang;
195                 if (innerCount > count)
196                         innerCount = count;
197
198                 memcpy(frontPadBuf + frontHang, buf, innerCount);
199
200                 if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
201                         goto out;
202
203                 r = write(fd, frontPadBuf, bsize);
204                 if (r < 0 || r != bsize)
205                         goto out;
206
207                 buf += innerCount;
208                 count -= innerCount;
209         }
210
211         ret = count ? write_blockwise(fd, bsize, buf, count) : 0;
212         if (ret >= 0)
213                 ret += innerCount;
214 out:
215         free(frontPadBuf_base);
216
217         return ret;
218 }
219
220 /* MEMLOCK */
221 #define DEFAULT_PROCESS_PRIORITY -18
222
223 static int _priority;
224 static int _memlock_count = 0;
225
226 // return 1 if memory is locked
227 int crypt_memlock_inc(struct crypt_device *ctx)
228 {
229         if (!_memlock_count++) {
230                 log_dbg("Locking memory.");
231                 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
232                         log_dbg("Cannot lock memory with mlockall.");
233                         _memlock_count--;
234                         return 0;
235                 }
236                 errno = 0;
237                 if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno)
238                         log_err(ctx, _("Cannot get process priority.\n"));
239                 else
240                         if (setpriority(PRIO_PROCESS, 0, DEFAULT_PROCESS_PRIORITY))
241                                 log_dbg("setpriority %d failed: %s",
242                                         DEFAULT_PROCESS_PRIORITY, strerror(errno));
243         }
244         return _memlock_count ? 1 : 0;
245 }
246
247 int crypt_memlock_dec(struct crypt_device *ctx)
248 {
249         if (_memlock_count && (!--_memlock_count)) {
250                 log_dbg("Unlocking memory.");
251                 if (munlockall() == -1)
252                         log_err(ctx, _("Cannot unlock memory.\n"));
253                 if (setpriority(PRIO_PROCESS, 0, _priority))
254                         log_dbg("setpriority %d failed: %s", _priority, strerror(errno));
255         }
256         return _memlock_count ? 1 : 0;
257 }