• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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