xref: /third_party/NuttX/fs/driver/fs_blockproxy.c (revision beacf11b)
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
44static uint32_t g_devno;
45static 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
69static 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
152int 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
219errout_with_bchdev:
220  (void)unregister_driver(chardev);
221errout_with_chardev:
222  (void)free(chardev);
223  (void)sem_destroy(&g_devno_sem);
224  return ret;
225}
226
227#endif
228