279724805eeddcf131eb9aceb775dbccfac820f6
[lwext4.git] / blockdev / linux / file_dev.c
1 /*
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  * - Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * - The name of the author may not be used to endorse or promote products
15  *   derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #define _LARGEFILE64_SOURCE
30 #define _FILE_OFFSET_BITS 64
31
32 #include <ext4_config.h>
33 #include <ext4_blockdev.h>
34 #include <ext4_errno.h>
35 #include <stdio.h>
36 #include <stdbool.h>
37 #include <string.h>
38 #ifdef __APPLE__
39 #include <fcntl.h>
40 #include <sys/disk.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <sys/socket.h>
44 #endif
45
46 /**@brief   Default filename.*/
47 static const char *fname = "ext2";
48
49 /**@brief   Image block size.*/
50 #define EXT4_FILEDEV_BSIZE 512
51
52 /**@brief   Image file descriptor.*/
53 static FILE *dev_file;
54
55 #define DROP_LINUXCACHE_BUFFERS 0
56
57 /**********************BLOCKDEV INTERFACE**************************************/
58 static int file_dev_open(struct ext4_blockdev *bdev);
59 static int file_dev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
60                          uint32_t blk_cnt);
61 static int file_dev_bwrite(struct ext4_blockdev *bdev, const void *buf,
62                           uint64_t blk_id, uint32_t blk_cnt);
63 static int file_dev_close(struct ext4_blockdev *bdev);
64
65 /******************************************************************************/
66 EXT4_BLOCKDEV_STATIC_INSTANCE(file_dev, EXT4_FILEDEV_BSIZE, 0, file_dev_open,
67                 file_dev_bread, file_dev_bwrite, file_dev_close, 0, 0);
68
69 /******************************************************************************/
70 static int file_dev_open(struct ext4_blockdev *bdev)
71 {
72 #ifdef __APPLE__
73         /* We need to use authopen to open the device.  It asks the user for permission
74          * then give us an open fd over a socket.
75          */
76
77         int pipe[2];
78         if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) < 0) {
79                 return EFAULT;
80         }
81
82         pid_t pid = fork();
83         if (pid < 0) {
84                 return EFAULT;
85         }
86
87         if (pid == 0) {
88                 close(pipe[0]);
89                 dup2(pipe[1], STDOUT_FILENO);
90                 execl("/usr/libexec/authopen", "/usr/libexec/authopen", "-stdoutpipe", "-w", "-a", fname, (char *) 0);
91                 exit(-1);
92         }
93
94         close(pipe[1]);
95
96         int dev = -1;
97
98         size_t const data_buffer_size = sizeof(struct cmsghdr) + sizeof(int);
99         char data_buffer[data_buffer_size];
100
101         struct iovec io_vec[1];
102         io_vec[0].iov_base = data_buffer;
103         io_vec[0].iov_len = data_buffer_size;
104
105         socklen_t const cmsg_socket_size = CMSG_SPACE(sizeof(int));
106         char cmsg_socket[cmsg_socket_size];
107         struct msghdr message = { 0 };
108         message.msg_iov = io_vec;
109         message.msg_iovlen = 1;
110         message.msg_control = cmsg_socket;
111         message.msg_controllen = cmsg_socket_size;
112
113         if (recvmsg(pipe[0], &message, 0) <= 0) {
114                 return EIO;
115         }
116
117         struct cmsghdr* cmsg_socket_header = CMSG_FIRSTHDR(&message);
118         if (cmsg_socket_header && cmsg_socket_header->cmsg_level == SOL_SOCKET && cmsg_socket_header->cmsg_type == SCM_RIGHTS) {
119                 dev = *((int *) CMSG_DATA(cmsg_socket_header));
120         }
121
122         /* The fseek/ftell approach to finding the device's size does not seem
123          * to work on macOS so do it this way instead.
124          */
125         uint64_t sectors = 0;
126         if (ioctl(dev, DKIOCGETBLOCKCOUNT, &sectors) < 0) {
127                 close(dev);
128                 return EFAULT;
129         }
130         uint32_t sector_size = 0;
131         if (ioctl(dev, DKIOCGETBLOCKSIZE, &sector_size) < 0) {
132                 close(dev);
133                 return EFAULT;
134         }
135
136         off_t size = sectors * sector_size;
137
138         dev_file = fdopen(dev, "r+b");
139 #else
140         dev_file = fopen(fname, "r+b");
141 #endif
142
143         if (!dev_file)
144                 return EIO;
145
146         /*No buffering at file.*/
147         setbuf(dev_file, 0);
148
149 #ifndef __APPLE__
150         if (fseeko(dev_file, 0, SEEK_END))
151                 return EFAULT;
152
153         off_t size = ftello(dev_file);
154 #endif
155
156         file_dev.part_offset = 0;
157         file_dev.part_size = size;
158         file_dev.bdif->ph_bcnt = file_dev.part_size / file_dev.bdif->ph_bsize;
159
160         return EOK;
161 }
162
163 /******************************************************************************/
164
165 static int file_dev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id,
166                          uint32_t blk_cnt)
167 {
168         if (fseeko(dev_file, blk_id * bdev->bdif->ph_bsize, SEEK_SET))
169                 return EIO;
170         if (!blk_cnt)
171                 return EOK;
172         if (!fread(buf, bdev->bdif->ph_bsize * blk_cnt, 1, dev_file))
173                 return EIO;
174
175         return EOK;
176 }
177
178 static void drop_cache(void)
179 {
180 #if defined(__linux__) && DROP_LINUXCACHE_BUFFERS
181         int fd;
182         char *data = "3";
183
184         sync();
185         fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
186         write(fd, data, sizeof(char));
187         close(fd);
188 #endif
189 }
190
191 /******************************************************************************/
192 static int file_dev_bwrite(struct ext4_blockdev *bdev, const void *buf,
193                           uint64_t blk_id, uint32_t blk_cnt)
194 {
195         if (fseeko(dev_file, blk_id * bdev->bdif->ph_bsize, SEEK_SET))
196                 return EIO;
197         if (!blk_cnt)
198                 return EOK;
199         if (!fwrite(buf, bdev->bdif->ph_bsize * blk_cnt, 1, dev_file))
200                 return EIO;
201
202         drop_cache();
203         return EOK;
204 }
205 /******************************************************************************/
206 static int file_dev_close(struct ext4_blockdev *bdev)
207 {
208         fclose(dev_file);
209         return EOK;
210 }
211
212 /******************************************************************************/
213 struct ext4_blockdev *file_dev_get(void)
214 {
215         return &file_dev;
216 }
217 /******************************************************************************/
218 void file_dev_name_set(const char *n)
219 {
220         fname = n;
221 }
222 /******************************************************************************/