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