1 /* $NetBSD: devnodes.c,v 1.13 2019/06/18 22:34:26 kamil Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: devnodes.c,v 1.13 2019/06/18 22:34:26 kamil Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/device.h> 33 #include <sys/filedesc.h> 34 #include <sys/kmem.h> 35 #include <sys/lwp.h> 36 #include <sys/namei.h> 37 #include <sys/stat.h> 38 #include <sys/vfs_syscalls.h> 39 40 #include <rump-sys/vfs.h> 41 42 /* realqvik(tm) "devfs" */ 43 static int 44 makeonedevnode(dev_t devtype, const char *devname, 45 devmajor_t majnum, devminor_t minnum) 46 { 47 int error; 48 49 error = do_sys_mknod(curlwp, devname, 0666 | devtype, 50 makedev(majnum, minnum), UIO_SYSSPACE); 51 if (error == EEXIST) /* XXX: should check it's actually the same */ 52 error = 0; 53 54 return error; 55 } 56 57 static int 58 makedevnodes(dev_t devtype, const char *basename, char minchar, 59 devmajor_t maj, devminor_t minnum, int nnodes) 60 { 61 int error = 0; 62 char *devname, *p; 63 size_t devlen; 64 65 devlen = strlen(basename) + 1 + 1; /* +letter +0 */ 66 devname = kmem_zalloc(devlen, KM_SLEEP); 67 strlcpy(devname, basename, devlen); 68 p = devname + devlen-2; 69 70 for (; nnodes > 0; nnodes--, minchar++, minnum++) { 71 KASSERT(minchar <= 'z'); 72 *p = minchar; 73 74 if ((error = do_sys_mknod(curlwp, devname, 0666 | devtype, 75 makedev(maj, minnum), UIO_SYSSPACE))) { 76 if (error == EEXIST) 77 error = 0; 78 else 79 goto out; 80 } 81 } 82 83 out: 84 kmem_free(devname, devlen); 85 return error; 86 } 87 88 static int 89 makesymlink(const char *dst, const char *src) 90 { 91 92 return do_sys_symlink(dst, src, UIO_SYSSPACE); 93 } 94 95 enum { NOTEXIST, SAME, DIFFERENT }; 96 static int 97 doesitexist(const char *path, bool isblk, devmajor_t dmaj, devminor_t dmin) 98 { 99 struct stat sb; 100 int error; 101 102 error = do_sys_stat(path, 0, &sb); 103 /* even if not ENOENT, we might be able to create it */ 104 if (error) 105 return NOTEXIST; 106 107 if (major(sb.st_rdev) != dmaj || minor(sb.st_rdev) != dmin) 108 return DIFFERENT; 109 if (isblk && !S_ISBLK(sb.st_mode)) 110 return DIFFERENT; 111 if (!isblk && !S_ISCHR(sb.st_mode)) 112 return DIFFERENT; 113 114 return SAME; 115 } 116 117 static void 118 makeonenode(char *buf, size_t len, devmajor_t blk, devmajor_t chr, 119 devminor_t dmin, const char *base, int c1, int c2) 120 { 121 char cstr1[2] = {0,0}, cstr2[2] = {0,0}; 122 int error; 123 124 if (c1 != -1) { 125 cstr1[0] = '0' + c1; 126 cstr1[1] = '\0'; 127 } 128 129 if (c2 != -1) { 130 cstr2[0] = 'a' + c2; 131 cstr2[1] = '\0'; 132 133 } 134 135 /* block device */ 136 snprintf(buf, len, "/dev/%s%s%s", base, cstr1, cstr2); 137 if (blk != NODEVMAJOR) { 138 switch (doesitexist(buf, true, blk, dmin)) { 139 case DIFFERENT: 140 aprint_verbose("mkdevnodes: block device %s " 141 "already exists\n", buf); 142 break; 143 case NOTEXIST: 144 if ((error = do_sys_mknod(curlwp, buf, 0600 | S_IFBLK, 145 makedev(blk, dmin), UIO_SYSSPACE)) != 0) 146 aprint_verbose("mkdevnodes: failed to " 147 "create %s: %d\n", buf, error); 148 break; 149 case SAME: 150 /* done */ 151 break; 152 } 153 snprintf(buf, len, "/dev/r%s%s%s", base, cstr1, cstr2); 154 } 155 156 switch (doesitexist(buf, true, chr, dmin)) { 157 case DIFFERENT: 158 aprint_verbose("mkdevnodes: character device %s " 159 "already exists\n", buf); 160 break; 161 case NOTEXIST: 162 if ((error = do_sys_mknod(curlwp, buf, 0600 | S_IFCHR, 163 makedev(chr, dmin), UIO_SYSSPACE)) != 0) 164 aprint_verbose("mkdevnodes: failed to " 165 "create %s: %d\n", buf, error); 166 break; 167 case SAME: 168 /* yeehaa */ 169 break; 170 } 171 } 172 173 void 174 rump_vfs_builddevs(struct devsw_conv *dcvec, size_t dcvecsize) 175 { 176 char *pnbuf = PNBUF_GET(); 177 devminor_t themin; 178 struct devsw_conv *dc; 179 size_t i; 180 int v1, v2; 181 182 rump_vfs_makeonedevnode = makeonedevnode; 183 rump_vfs_makedevnodes = makedevnodes; 184 rump_vfs_makesymlink = makesymlink; 185 186 for (i = 0; i < dcvecsize; i++) { 187 dc = &dcvec[i]; 188 189 switch (dc->d_class) { 190 case DEVNODE_DONTBOTHER: 191 break; 192 case DEVNODE_SINGLE: 193 if (dc->d_flags & DEVNODE_FLAG_ISMINOR0) { 194 themin = dc->d_vectdim[0]; 195 } else { 196 themin = 0; 197 } 198 makeonenode(pnbuf, MAXPATHLEN, 199 dc->d_bmajor, dc->d_cmajor, themin, 200 dc->d_name, -1, -1); 201 break; 202 case DEVNODE_VECTOR: 203 for (v1 = 0; v1 < dc->d_vectdim[0]; v1++) { 204 if (dc->d_vectdim[1] == 0) { 205 makeonenode(pnbuf, MAXPATHLEN, 206 dc->d_bmajor, dc->d_cmajor, 207 v1, dc->d_name, v1, -1); 208 } else { 209 for (v2 = 0; 210 v2 < dc->d_vectdim[1]; v2++) { 211 makeonenode(pnbuf, MAXPATHLEN, 212 dc->d_bmajor, dc->d_cmajor, 213 v1 * dc->d_vectdim[1] + v2, 214 dc->d_name, v1, v2); 215 } 216 } 217 } 218 219 /* add some extra sanity checks here */ 220 if (dc->d_flags & DEVNODE_FLAG_LINKZERO) { 221 /* 222 * ok, so we cheat a bit since 223 * symlink isn't supported on rumpfs ... 224 */ 225 makeonenode(pnbuf, MAXPATHLEN, 226 -1, dc->d_cmajor, 0, dc->d_name, -1, -1); 227 228 } 229 break; 230 } 231 } 232 233 PNBUF_PUT(pnbuf); 234 } 235