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