1 /****************************************************************************
2 * fs/driver/fs_blockproxy.c
3 *
4 * Copyright (C) 2015-2018 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <semaphore.h>
47 #include <string.h>
48 #include <errno.h>
49 #include <assert.h>
50 #include "fs/driver.h"
51 #include "blockproxy.h"
52
53 #ifdef LOSCFG_FS_VFS_BLOCK_DEVICE
54
55 /****************************************************************************
56 * Private Data
57 ****************************************************************************/
58
59 static uint32_t g_devno;
60 static sem_t g_devno_sem;
61
62 /****************************************************************************
63 * Private Functions
64 ****************************************************************************/
65
66 /****************************************************************************
67 * Name: unique_chardev
68 *
69 * Description:
70 * Create a unique temporary device name in the /dev/ directory of the
71 * pseudo-file system. We cannot use mktemp for this because it will
72 * attempt to open() the file.
73 *
74 * Input Parameters:
75 * None
76 *
77 * Returned Value:
78 * The allocated path to the device. This must be released by the caller
79 * to prevent memory links. NULL will be returned only the case where
80 * we fail to allocate memory.
81 *
82 ****************************************************************************/
83
unique_chardev(void)84 static char *unique_chardev(void)
85 {
86 struct stat statbuf;
87 char devbuf[16];
88 uint32_t devno;
89 int ret;
90
91 /* Loop until we get a unique device name */
92
93 for (; ;)
94 {
95 /* Get the semaphore protecting the path number */
96
97 while (sem_wait(&g_devno_sem) != 0)
98 {
99 /* The only case that an error should occur here is if the wait
100 * was awakened by a signal.
101 */
102
103 ret = get_errno();
104 LOS_ASSERT(ret == EINTR);
105 }
106
107 /* Get the next device number and release the semaphore */
108
109 devno = ++g_devno;
110 (void)sem_post(&g_devno_sem);
111
112 /* Construct the full device number */
113
114 devno &= 0xffffff;
115 ret = snprintf_s(devbuf, 16, 14, "/dev/tmp%06lx", (unsigned long)devno);
116 /* length of the format string is 14 */
117 if (ret < 0)
118 {
119 set_errno(ENAMETOOLONG);
120 return strdup(devbuf);
121 }
122
123 /* Make sure that file name is not in use */
124
125 ret = stat(devbuf, &statbuf);
126 if (ret < 0)
127 {
128 DEBUGASSERT(errno == ENOENT);
129 return strdup(devbuf);
130 }
131
132 /* It is in use, try again */
133 }
134 }
135
136 /****************************************************************************
137 * Public Functions
138 ****************************************************************************/
139
140 /****************************************************************************
141 * Name: block_proxy
142 *
143 * Description:
144 * Create a temporary char driver using drivers/bch to mediate character
145 * oriented accessed to the block driver.
146 *
147 * Input Parameters:
148 * blkdev - The path to the block driver
149 * oflags - Character driver open flags
150 *
151 * Returned Value:
152 * If positive, non-zero file descriptor is returned on success. This
153 * is the file descriptor of the nameless character driver that mediates
154 * accesses to the block driver.
155 *
156 * Errors that may be returned:
157 *
158 * ENOMEM - Failed to create a temporay path name.
159 *
160 * Plus:
161 *
162 * - Errors reported from bchdev_register()
163 * - Errors reported from open() or unlink()
164 *
165 ****************************************************************************/
166
block_proxy(const char * blkdev,int oflags)167 int block_proxy(const char *blkdev, int oflags)
168 {
169 struct file *filep = NULL;
170 struct Vnode *vnode = NULL;
171 char *chardev;
172 bool readonly;
173 int ret;
174 int fd;
175
176 DEBUGASSERT(blkdev);
177 (void)sem_init(&g_devno_sem, 0, 1);
178
179 /* Create a unique temporary file name for the character device */
180
181 chardev = unique_chardev();
182 if (chardev == NULL)
183 {
184 PRINTK("ERROR: Failed to create temporary device name\n");
185 (void)sem_destroy(&g_devno_sem);
186 return -ENOMEM;
187 }
188
189 /* Should this character driver be read-only? */
190
191 readonly = (((unsigned int)oflags & O_ACCMODE) == O_RDONLY);
192
193 /* Wrap the block driver with an instance of the BCH driver */
194
195 ret = bchdev_register(blkdev, chardev, readonly);
196 if (ret < 0)
197 {
198 PRINTK("ERROR: bchdev_register(%s, %s) failed: %d\n", blkdev, chardev, ret);
199 goto errout_with_chardev;
200 }
201
202 /* Open the newly created character driver */
203
204 oflags =(unsigned int)oflags & (~(O_CREAT | O_EXCL | O_APPEND | O_TRUNC));
205 fd = open(chardev, oflags);
206 if (fd < 0)
207 {
208 ret = -errno;
209 PRINTK("ERROR: Failed to open %s: %d\n", chardev, ret);
210 goto errout_with_bchdev;
211 }
212
213 ret = fs_getfilep(fd, &filep);
214 if (ret < 0)
215 {
216 files_release(fd);
217 ret = -get_errno();
218 goto errout_with_bchdev;
219 }
220
221 vnode = filep->f_vnode;
222 VnodeHold();
223 vnode->type = VNODE_TYPE_BCHR;
224 VnodeDrop();
225
226 /* Free the allocate character driver name and return the open file
227 * descriptor.
228 */
229
230 (void)free(chardev);
231 (void)sem_destroy(&g_devno_sem);
232 return fd;
233
234 errout_with_bchdev:
235 (void)unregister_driver(chardev);
236 errout_with_chardev:
237 (void)free(chardev);
238 (void)sem_destroy(&g_devno_sem);
239 return ret;
240 }
241
242 #endif
243