Get page size should never fail (in the works case it fails later with wrong alignment).
[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 != bsize)
101                         goto out;
102
103                 r = lseek(fd, -bsize, SEEK_CUR);
104                 if (r < 0)
105                         goto out;
106                 memcpy(hangover_buf, (char*)buf + solid, hangover);
107
108                 r = write(fd, hangover_buf, bsize);
109                 if (r < 0 || r < hangover)
110                         goto out;
111         }
112         ret = count;
113 out:
114         free(hangover_buf_base);
115         if (buf != orig_buf)
116                 free(buf_base);
117         return ret;
118 }
119
120 ssize_t read_blockwise(int fd, int bsize, void *orig_buf, size_t count) {
121         void *hangover_buf, *hangover_buf_base = NULL;
122         void *buf, *buf_base = NULL;
123         int r, hangover, solid, alignment;
124         ssize_t ret = -1;
125
126         if (fd == -1 || !orig_buf || bsize <= 0)
127                 return -1;
128
129         hangover = count % bsize;
130         solid = count - hangover;
131         alignment = get_alignment(fd);
132
133         if ((long)orig_buf & (alignment - 1)) {
134                 buf = aligned_malloc(&buf_base, count, alignment);
135                 if (!buf)
136                         return -1;
137         } else
138                 buf = orig_buf;
139
140         r = read(fd, buf, solid);
141         if(r < 0 || r != solid)
142                 goto out;
143
144         if (hangover) {
145                 hangover_buf = aligned_malloc(&hangover_buf_base, bsize, alignment);
146                 if (!hangover_buf)
147                         goto out;
148                 r = read(fd, hangover_buf, bsize);
149                 if (r <  0 || r < hangover)
150                         goto out;
151
152                 memcpy((char *)buf + solid, hangover_buf, hangover);
153         }
154         ret = count;
155 out:
156         free(hangover_buf_base);
157         if (buf != orig_buf) {
158                 memcpy(orig_buf, buf, count);
159                 free(buf_base);
160         }
161         return ret;
162 }
163
164 /*
165  * Combines llseek with blockwise write. write_blockwise can already deal with short writes
166  * but we also need a function to deal with short writes at the start. But this information
167  * is implicitly included in the read/write offset, which can not be set to non-aligned
168  * boundaries. Hence, we combine llseek with write.
169  */
170 ssize_t write_lseek_blockwise(int fd, int bsize, char *buf, size_t count, off_t offset) {
171         char *frontPadBuf;
172         void *frontPadBuf_base = NULL;
173         int r, frontHang;
174         size_t innerCount = 0;
175         ssize_t ret = -1;
176
177         if (fd == -1 || !buf || bsize <= 0)
178                 return -1;
179
180         frontHang = offset % bsize;
181
182         if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
183                 goto out;
184
185         if (frontHang) {
186                 frontPadBuf = aligned_malloc(&frontPadBuf_base,
187                                              bsize, get_alignment(fd));
188                 if (!frontPadBuf)
189                         goto out;
190
191                 r = read(fd, frontPadBuf, bsize);
192                 if (r < 0 || r != bsize)
193                         goto out;
194
195                 innerCount = bsize - frontHang;
196                 if (innerCount > count)
197                         innerCount = count;
198
199                 memcpy(frontPadBuf + frontHang, buf, innerCount);
200
201                 if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
202                         goto out;
203
204                 r = write(fd, frontPadBuf, bsize);
205                 if (r < 0 || r != bsize)
206                         goto out;
207
208                 buf += innerCount;
209                 count -= innerCount;
210         }
211
212         ret = count ? write_blockwise(fd, bsize, buf, count) : 0;
213         if (ret >= 0)
214                 ret += innerCount;
215 out:
216         free(frontPadBuf_base);
217
218         return ret;
219 }
220
221 /* MEMLOCK */
222 #define DEFAULT_PROCESS_PRIORITY -18
223
224 static int _priority;
225 static int _memlock_count = 0;
226
227 // return 1 if memory is locked
228 int crypt_memlock_inc(struct crypt_device *ctx)
229 {
230         if (!_memlock_count++) {
231                 log_dbg("Locking memory.");
232                 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
233                         log_dbg("Cannot lock memory with mlockall.");
234                         _memlock_count--;
235                         return 0;
236                 }
237                 errno = 0;
238                 if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno)
239                         log_err(ctx, _("Cannot get process priority.\n"));
240                 else
241                         if (setpriority(PRIO_PROCESS, 0, DEFAULT_PROCESS_PRIORITY))
242                                 log_dbg("setpriority %d failed: %s",
243                                         DEFAULT_PROCESS_PRIORITY, strerror(errno));
244         }
245         return _memlock_count ? 1 : 0;
246 }
247
248 int crypt_memlock_dec(struct crypt_device *ctx)
249 {
250         if (_memlock_count && (!--_memlock_count)) {
251                 log_dbg("Unlocking memory.");
252                 if (munlockall() == -1)
253                         log_err(ctx, _("Cannot unlock memory.\n"));
254                 if (setpriority(PRIO_PROCESS, 0, _priority))
255                         log_dbg("setpriority %d failed: %s", _priority, strerror(errno));
256         }
257         return _memlock_count ? 1 : 0;
258 }