1 /****************************************************************************
2 * fs/driver/fs_blockproxy.c
3 *
4 * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
5 * Based on NuttX originally from nuttx source (nuttx/fs/ and nuttx/drivers/)
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ****************************************************************************/
20
21 /****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <semaphore.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include "fs/driver.h"
36 #include "blockproxy.h"
37
38 #ifdef LOSCFG_FS_VFS_BLOCK_DEVICE
39
40 /****************************************************************************
41 * Private Data
42 ****************************************************************************/
43
44 static uint32_t g_devno;
45 static sem_t g_devno_sem;
46
47 /****************************************************************************
48 * Private Functions
49 ****************************************************************************/
50
51 /****************************************************************************
52 * Name: unique_chardev
53 *
54 * Description:
55 * Create a unique temporary device name in the /dev/ directory of the
56 * pseudo-file system. We cannot use mktemp for this because it will
57 * attempt to open() the file.
58 *
59 * Input Parameters:
60 * None
61 *
62 * Returned Value:
63 * The allocated path to the device. This must be released by the caller
64 * to prevent memory links. NULL will be returned only the case where
65 * we fail to allocate memory.
66 *
67 ****************************************************************************/
68
unique_chardev(void)69 static char *unique_chardev(void)
70 {
71 struct stat statbuf;
72 char devbuf[16];
73 uint32_t devno;
74 int ret;
75
76 /* Loop until we get a unique device name */
77
78 for (; ;)
79 {
80 /* Get the semaphore protecting the path number */
81
82 while (sem_wait(&g_devno_sem) != 0)
83 {
84 /* The only case that an error should occur here is if the wait
85 * was awakened by a signal.
86 */
87
88 ret = get_errno();
89 LOS_ASSERT(ret == EINTR);
90 }
91
92 /* Get the next device number and release the semaphore */
93
94 devno = ++g_devno;
95 (void)sem_post(&g_devno_sem);
96
97 /* Construct the full device number */
98
99 devno &= 0xffffff;
100 ret = snprintf_s(devbuf, 16, 14, "/dev/tmp%06lx", (unsigned long)devno);
101 /* length of the format string is 14 */
102 if (ret < 0)
103 {
104 set_errno(ENAMETOOLONG);
105 return strdup(devbuf);
106 }
107
108 /* Make sure that file name is not in use */
109
110 ret = stat(devbuf, &statbuf);
111 if (ret < 0)
112 {
113 DEBUGASSERT(errno == ENOENT);
114 return strdup(devbuf);
115 }
116
117 /* It is in use, try again */
118 }
119 }
120
121 /****************************************************************************
122 * Public Functions
123 ****************************************************************************/
124
125 /****************************************************************************
126 * Name: block_proxy
127 *
128 * Description:
129 * Create a temporary char driver using drivers/bch to mediate character
130 * oriented accessed to the block driver.
131 *
132 * Input Parameters:
133 * blkdev - The path to the block driver
134 * oflags - Character driver open flags
135 *
136 * Returned Value:
137 * If positive, non-zero file descriptor is returned on success. This
138 * is the file descriptor of the nameless character driver that mediates
139 * accesses to the block driver.
140 *
141 * Errors that may be returned:
142 *
143 * ENOMEM - Failed to create a temporay path name.
144 *
145 * Plus:
146 *
147 * - Errors reported from bchdev_register()
148 * - Errors reported from open() or unlink()
149 *
150 ****************************************************************************/
151
block_proxy(const char * blkdev,int oflags)152 int block_proxy(const char *blkdev, int oflags)
153 {
154 struct file *filep = NULL;
155 struct Vnode *vnode = NULL;
156 char *chardev;
157 bool readonly;
158 int ret;
159 int fd;
160
161 DEBUGASSERT(blkdev);
162 (void)sem_init(&g_devno_sem, 0, 1);
163
164 /* Create a unique temporary file name for the character device */
165
166 chardev = unique_chardev();
167 if (chardev == NULL)
168 {
169 PRINTK("ERROR: Failed to create temporary device name\n");
170 (void)sem_destroy(&g_devno_sem);
171 return -ENOMEM;
172 }
173
174 /* Should this character driver be read-only? */
175
176 readonly = (((unsigned int)oflags & O_ACCMODE) == O_RDONLY);
177
178 /* Wrap the block driver with an instance of the BCH driver */
179
180 ret = bchdev_register(blkdev, chardev, readonly);
181 if (ret < 0)
182 {
183 PRINTK("ERROR: bchdev_register(%s, %s) failed: %d\n", blkdev, chardev, ret);
184 goto errout_with_chardev;
185 }
186
187 /* Open the newly created character driver */
188
189 oflags =(unsigned int)oflags & (~(O_CREAT | O_EXCL | O_APPEND | O_TRUNC));
190 fd = open(chardev, oflags);
191 if (fd < 0)
192 {
193 ret = -errno;
194 PRINTK("ERROR: Failed to open %s: %d\n", chardev, ret);
195 goto errout_with_bchdev;
196 }
197
198 ret = fs_getfilep(fd, &filep);
199 if (ret < 0)
200 {
201 files_release(fd);
202 ret = -get_errno();
203 goto errout_with_bchdev;
204 }
205
206 vnode = filep->f_vnode;
207 VnodeHold();
208 vnode->type = VNODE_TYPE_BCHR;
209 VnodeDrop();
210
211 /* Free the allocate character driver name and return the open file
212 * descriptor.
213 */
214
215 (void)free(chardev);
216 (void)sem_destroy(&g_devno_sem);
217 return fd;
218
219 errout_with_bchdev:
220 (void)unregister_driver(chardev);
221 errout_with_chardev:
222 (void)free(chardev);
223 (void)sem_destroy(&g_devno_sem);
224 return ret;
225 }
226
227 #endif
228