1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ueventd_device_handler.h" 17 18#include <errno.h> 19#include <libgen.h> 20#include <limits.h> 21#include <stdbool.h> 22#include <stdlib.h> 23#include <string.h> 24#include <sys/stat.h> 25#include <sys/sysmacros.h> 26#include "init_utils.h" 27#include "ueventd.h" 28#ifndef __RAMDISK__ 29#include "ueventd_parameter.h" 30#endif 31#include "ueventd_read_cfg.h" 32#include "ueventd_utils.h" 33#include "securec.h" 34#define INIT_LOG_TAG "ueventd" 35#include "init_log.h" 36#ifdef WITH_SELINUX 37#include <selinux/selinux.h> 38#include <policycoreutils.h> 39#endif 40 41static inline void AdjustDeviceNodePermissions(const char *deviceNode, uid_t uid, gid_t gid, mode_t mode) 42{ 43 if (INVALIDSTRING(deviceNode)) { 44 return; 45 } 46 if (chown(deviceNode, uid, gid) != 0) { 47 INIT_LOGW("Failed to change \" %s \" owner, errno %d", deviceNode, errno); 48 } 49 50 if (chmod(deviceNode, mode) != 0) { 51 INIT_LOGW("Failed to change \" %s \" mode, errno %d", deviceNode, errno); 52 } 53} 54 55static void CreateSymbolLinks(const char *deviceNode, char **symLinks) 56{ 57 if (INVALIDSTRING(deviceNode) || symLinks == NULL) { 58 return; 59 } 60 61 uid_t uid = 0; 62 gid_t gid = 0; 63 mode_t mode = DEVMODE; 64 65 for (int i = 0; symLinks[i] != NULL; i++) { 66 const char *linkName = symLinks[i]; 67 char linkBuf[DEVICE_FILE_SIZE] = {}; 68 69 if (strstr(linkName, "/dev/block/by-name") != NULL) { 70 int res = GetDeviceNodePermissions(linkName, &uid, &gid, &mode); 71 INIT_CHECK(res != 0, AdjustDeviceNodePermissions(deviceNode, uid, gid, mode)); 72 } 73 74 if (strncpy_s(linkBuf, DEVICE_FILE_SIZE - 1, linkName, strlen(linkName)) != EOK) { 75 INIT_LOGE("Failed to copy link name"); 76 return; 77 } 78 const char *linkDir = dirname(linkBuf); 79 if (MakeDirRecursive(linkDir, DIRMODE) < 0) { 80 INIT_LOGE("[uevent] Failed to create dir \" %s \", err = %d", linkDir, errno); 81 return; 82 } 83 84 errno = 0; 85 INIT_LOGI("symlink %s->%s", deviceNode, linkName); 86 int rc = symlink(deviceNode, linkName); 87 if (rc != 0) { 88 if (errno != EEXIST) { 89 INIT_LOGE("Failed to link \" %s \" to \" %s \", err = %d", deviceNode, linkName, errno); 90 } 91 } 92 } 93} 94 95#if defined(WITH_SELINUX) && !defined(__RAMDISK__) 96static void SetDeviceLable(const char *path, char **symLinks) 97{ 98 int rc = 0; 99 char buffer[PATH_MAX] = {}; 100 const char *p = NULL; 101 char *slash = NULL; 102 103 p = path; 104 slash = strchr(path, '/'); 105 while (slash != NULL) { 106 int gap = slash - p; 107 p = slash + 1; 108 if (gap == 0) { 109 slash = strchr(p, '/'); 110 continue; 111 } 112 if (gap < 0) { // end with '/' 113 return; 114 } 115 116 if (memcpy_s(buffer, PATH_MAX, path, p - path - 1) != EOK) { 117 INIT_LOGE("[uevent] Failed to memcpy path %s", path); 118 return; 119 } 120 rc += Restorecon(buffer); 121 slash = strchr(p, '/'); 122 } 123 124 rc += Restorecon(path); 125 if (rc != 0) { 126 INIT_LOGE("[uevent] Failed to Restorecon \" %s \"", path); 127 } 128 129 INIT_CHECK_ONLY_RETURN(symLinks != NULL); 130 char *context = NULL; 131 for (int i = 0; symLinks[i] != NULL; i++) { 132 const char *linkName = symLinks[i]; 133 const char *byNamePath = "/dev/block/by-name"; 134 if (strncmp(linkName, byNamePath, strlen(byNamePath)) == 0) { 135 (void)Restorecon(linkName); 136 lgetfilecon(linkName, &context); 137 if (context != NULL) { 138 setfilecon(path, context); 139 } 140 return; 141 } 142 } 143 144 return; 145} 146#endif 147 148static int CreateDeviceNode(const struct Uevent *uevent, const char *deviceNode, char **symLinks, bool isBlock) 149{ 150 int rc = -1; 151 int major = uevent->major; 152 int minor = uevent->minor; 153 uid_t uid = uevent->ug.uid; 154 gid_t gid = uevent->ug.gid; 155 mode_t mode = DEVMODE; 156 157 if (deviceNode == NULL || *deviceNode == '\0') { 158 INIT_LOGE("Invalid device file"); 159 return rc; 160 } 161 162 char deviceNodeBuffer[DEVICE_FILE_SIZE] = {}; 163 if (strncpy_s(deviceNodeBuffer, DEVICE_FILE_SIZE - 1, deviceNode, strlen(deviceNode)) != EOK) { 164 INIT_LOGE("Failed to copy device node"); 165 return rc; 166 } 167 const char *devicePath = dirname(deviceNodeBuffer); 168 // device node always installed in /dev, should not be other locations. 169 if (STRINGEQUAL(devicePath, ".") || STRINGEQUAL(devicePath, "/")) { 170 INIT_LOGE("device path is not valid. should be starts with /dev"); 171 return rc; 172 } 173 174 rc = MakeDirRecursive(devicePath, DIRMODE); 175 if (rc < 0) { 176 INIT_LOGE("Create path \" %s \" failed", devicePath); 177 return rc; 178 } 179 180 (void)GetDeviceNodePermissions(deviceNode, &uid, &gid, &mode); 181 mode |= isBlock ? S_IFBLK : S_IFCHR; 182 dev_t dev = makedev((unsigned int)major, (unsigned int)minor); 183 setegid(0); 184 rc = mknod(deviceNode, mode, dev); 185 if (rc < 0) { 186 if (errno != EEXIST) { 187 INIT_LOGE("Create device node[%s %d, %d] failed. %d", deviceNode, major, minor, errno); 188 return rc; 189 } 190 } 191 AdjustDeviceNodePermissions(deviceNode, uid, gid, mode); 192 if (symLinks != NULL) { 193 CreateSymbolLinks(deviceNode, symLinks); 194 } 195#if defined(WITH_SELINUX) && !defined(__RAMDISK__) 196 SetDeviceLable(deviceNode, symLinks); 197#endif 198 // No matter what result the symbol links returns, 199 // as long as create device node done, just returns success. 200 rc = 0; 201 return rc; 202} 203 204static int RemoveDeviceNode(const char *deviceNode, char **symLinks) 205{ 206 if (INVALIDSTRING(deviceNode)) { 207 INIT_LOGE("Invalid device node"); 208 return -1; 209 } 210 if (symLinks != NULL) { 211 for (int i = 0; symLinks[i] != NULL; i++) { 212 char realPath[DEVICE_FILE_SIZE] = {0}; 213 const char *linkName = symLinks[i]; 214 ssize_t ret = readlink(linkName, realPath, DEVICE_FILE_SIZE - 1); 215 if (ret < 0) { 216 continue; 217 } 218 if (STRINGEQUAL(deviceNode, realPath)) { 219 INIT_LOGI("unlink %s", linkName); 220 unlink(linkName); 221 } 222 } 223 } 224 INIT_LOGI("unlink %s", deviceNode); 225 return unlink(deviceNode); 226} 227 228static char *FindPlatformDeviceName(char *path) 229{ 230 if (INVALIDSTRING(path)) { 231 return NULL; 232 } 233 234 if (STARTSWITH(path, "/sys/devices/platform/")) { 235 path += strlen("/sys/devices/platform/"); 236 return path; 237 } 238 239 // Some platform devices may not be registered under platform device. 240 if (STARTSWITH(path, "/sys/devices/")) { 241 path += strlen("/sys/devices/"); 242 return path; 243 } 244 return NULL; 245} 246 247static int BuildDeviceSymbolLinks(char **links, int linkNum, const char *parent, 248 const char *partitionName, const char *deviceName) 249{ 250 int num = linkNum; 251 links[num] = calloc(DEVICE_FILE_SIZE, sizeof(char)); 252 if (links[num] == NULL) { 253 INIT_LOGE("Failed to allocate memory for link, err = %d", errno); 254 return num; 255 } 256 257 // If a block device without partition name. 258 // For now, we will not create symbol link for it. 259 if (!INVALIDSTRING(partitionName)) { 260 if (snprintf_s(links[num], DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, 261 "/dev/block/platform/%s/by-name/%s", parent, partitionName) == -1) { 262 INIT_LOGE("Failed to build link"); 263 } 264 if (STRINGEQUAL(parent, bootDevice)) { 265 num = linkNum + 1; 266 links[num] = calloc(DEVICE_FILE_SIZE, sizeof(char)); 267 if (links[num] == NULL) { 268 INIT_LOGE("Failed to allocate memory for link, err = %d", errno); 269 return linkNum; 270 } 271 if (snprintf_s(links[num], DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, 272 "/dev/block/by-name/%s", partitionName) == -1) { 273 INIT_LOGE("Failed to build link"); 274 } 275 } else { 276 INIT_LOGI("%s and %s is not match", parent, bootDevice); 277 } 278 } else if (!INVALIDSTRING(deviceName)) { 279 if (snprintf_s(links[num], DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, 280 "/dev/block/platform/%s/%s", parent, deviceName) == -1) { 281 INIT_LOGE("Failed to build link"); 282 } 283 } 284 285 return num; 286} 287 288static void FreeSymbolLinks(char **links, int length) 289{ 290 if (links != NULL) { 291 for (int i = 0; i < length && links[i] != NULL; i++) { 292 free(links[i]); 293 links[i] = NULL; 294 } 295 free(links); 296 links = NULL; 297 } 298} 299 300static char **GetBlockDeviceSymbolLinks(const struct Uevent *uevent) 301{ 302 if (uevent == NULL || uevent->subsystem == NULL || STRINGEQUAL(uevent->subsystem, "block") == 0) { 303 INIT_LOGW("Invalid arguments, Skip to get device symbol links."); 304 return NULL; 305 } 306 307 // Only if current uevent is for real device. 308 if (!STARTSWITH(uevent->syspath, "/devices")) { 309 return NULL; 310 } 311 // For block device under one platform device. 312 // check subsystem file under directory, see if it links to bus/platform. 313 // For now, only support platform device. 314 char sysPath[SYSPATH_SIZE] = {}; 315 if (snprintf_s(sysPath, SYSPATH_SIZE, SYSPATH_SIZE - 1, "/sys%s", uevent->syspath) == -1) { 316 INIT_LOGE("Failed to build sys path for device %s", uevent->syspath); 317 return NULL; 318 } 319 char **links = calloc(BLOCKDEVICE_LINKS, sizeof(char *)); 320 int linkNum = 0; 321 if (links == NULL) { 322 INIT_LOGE("Failed to allocate memory for links, err = %d", errno); 323 return NULL; 324 } 325 326 // Reverse walk through sysPath, and check subsystem file under each directory. 327 char *parent = dirname(sysPath); 328 while (parent != NULL && !STRINGEQUAL(parent, "/") && !STRINGEQUAL(parent, ".")) { 329 char subsystem[SYSPATH_SIZE]; 330 if (snprintf_s(subsystem, SYSPATH_SIZE, SYSPATH_SIZE - 1, "%s/subsystem", parent) == -1) { 331 INIT_LOGE("Failed to build subsystem path for device \" %s \"", uevent->syspath); 332 FreeSymbolLinks(links, BLOCKDEVICE_LINKS); 333 return NULL; 334 } 335 char *bus = GetRealPath(subsystem); 336 if (bus == NULL) { 337 parent = dirname(parent); 338 continue; 339 } 340 if (STRINGEQUAL(bus, "/sys/bus/platform")) { 341 INIT_LOGV("Find a platform device: %s", parent); 342 parent = FindPlatformDeviceName(parent); 343 if (parent != NULL) { 344 INIT_WARNING_CHECK(linkNum < BLOCKDEVICE_LINKS - 1, links[linkNum] = NULL; 345 return links, "Too many links, ignore"); 346 linkNum = BuildDeviceSymbolLinks(links, linkNum, parent, uevent->partitionName, uevent->deviceName); 347 linkNum++; 348 } 349 } 350 free(bus); 351 parent = dirname(parent); 352 } 353 354 links[linkNum] = NULL; 355 return links; 356} 357 358static void HandleDeviceNode(const struct Uevent *uevent, const char *deviceNode, bool isBlock) 359{ 360 ACTION action = uevent->action; 361 char **symLinks = NULL; 362 363 // Block device path and name maybe not human readable. 364 // Consider to create symbol links for them. 365 // Make block device more readable. 366 if (isBlock) { 367 symLinks = GetBlockDeviceSymbolLinks(uevent); 368 } 369 370 if (action == ACTION_ADD) { 371 if (CreateDeviceNode(uevent, deviceNode, symLinks, isBlock) < 0) { 372 INIT_LOGE("Create device \" %s \" failed", deviceNode); 373 } else { 374#ifndef __RAMDISK__ 375 if (SetUeventDeviceParameter(deviceNode, action) != 0) { 376 INIT_LOGE("Set device parameter added failed"); 377 } 378#endif 379 } 380 } else if (action == ACTION_REMOVE) { 381 if (RemoveDeviceNode(deviceNode, symLinks) < 0) { 382 INIT_LOGE("Remove device \" %s \" failed", deviceNode); 383 } else { 384#ifndef __RAMDISK__ 385 if (SetUeventDeviceParameter(deviceNode, action) != 0) { 386 INIT_LOGE("Set device parameter removed failed"); 387 } 388#endif 389 } 390 } else if (action == ACTION_CHANGE) { 391 INIT_LOGV("Device %s changed", uevent->syspath); 392 } 393 // Ignore other actions 394 FreeSymbolLinks(symLinks, BLOCKDEVICE_LINKS); 395} 396 397static const char *GetDeviceName(char *sysPath, const char *deviceName) 398{ 399 const char *devName = NULL; 400 if (INVALIDSTRING(sysPath)) { 401 INIT_LOGE("Invalid sys path"); 402 return NULL; 403 } 404 if (deviceName != NULL && deviceName[0] != '\0') { 405 // if device name reported by kernel includes '/', skip it. 406 // use entire device name reported by kernel 407 devName = basename((char *)deviceName); 408 char *p = strrchr(deviceName, '/'); 409 if (p != NULL) { // device name includes slash 410 p++; 411 if (p == NULL || *p == '\0') { 412 // device name ends with '/', which should never happen. 413 // Get name from sys path. 414 devName = basename(sysPath); 415 } else { 416 devName = p; 417 } 418 } 419 } else { 420 // kernel does not report DEVNAME, which is possible. use base name of syspath instead. 421 devName = basename(sysPath); 422 } 423 return devName; 424} 425 426static const char *GetDeviceBasePath(const char *subsystem) 427{ 428 char *devPath = NULL; 429 if (INVALIDSTRING(subsystem)) { 430 return devPath; 431 } 432 433 if (STRINGEQUAL(subsystem, "block")) { 434 devPath = "/dev/block"; 435 } else if (STRINGEQUAL(subsystem, "input")) { 436 devPath = "/dev/input"; 437 } else if (STRINGEQUAL(subsystem, "drm")) { 438 devPath = "/dev/dri"; 439 } else if (STRINGEQUAL(subsystem, "graphics")) { 440 devPath = "/dev/graphics"; 441 } else if (STRINGEQUAL(subsystem, "sound")) { 442 devPath = "/dev/snd"; 443 } else if (STRINGEQUAL(subsystem, "functionfs")) { 444 devPath = "/dev/functionfs"; 445 } else if (STRINGEQUAL(subsystem, "dma_heap")) { 446 devPath = "/dev/dma_heap"; 447 } else { 448 devPath = "/dev"; 449 } 450 return devPath; 451} 452 453void HandleBlockDeviceEvent(const struct Uevent *uevent) 454{ 455 // Sanity checks 456 if (uevent == NULL || uevent->subsystem == NULL) { 457 INIT_LOGE("Invalid uevent message received"); 458 return; 459 } 460 461 if (strcmp(uevent->subsystem, "block") != 0) { 462 INIT_LOGE("Unexpected uevent subsystem \" %s \" received in block device handler", uevent->subsystem); 463 return; 464 } 465 466 if (uevent->major < 0 || uevent->minor < 0) { 467 return; 468 } 469 470 bool isBlock = true; 471 // Block device always installed into /dev/block 472 const char *devPath = GetDeviceBasePath(uevent->subsystem); 473 char deviceNode[DEVICE_FILE_SIZE] = {}; 474 char sysPath[SYSPATH_SIZE] = {}; 475 476 if (uevent->syspath == NULL) { 477 return; 478 } 479 if (strncpy_s(sysPath, SYSPATH_SIZE - 1, uevent->syspath, strlen(uevent->syspath) != EOK)) { 480 INIT_LOGE("Failed to copy sys path"); 481 return; 482 } 483 const char *devName = GetDeviceName(sysPath, uevent->deviceName); 484 485 if (devPath == NULL || devName == NULL) { 486 INIT_LOGE("Cannot get device path or device name"); 487 return; 488 } 489 if (snprintf_s(deviceNode, DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, "%s/%s", devPath, devName) == -1) { 490 INIT_LOGE("Make device file for device [%d : %d]", uevent->major, uevent->minor); 491 return; 492 } 493 HandleDeviceNode(uevent, deviceNode, isBlock); 494} 495 496void HandleOtherDeviceEvent(const struct Uevent *uevent) 497{ 498 if (uevent == NULL || uevent->subsystem == NULL || uevent->syspath == NULL) { 499 INIT_LOGE("Invalid uevent received"); 500 return; 501 } 502 503 if (uevent->major < 0 || uevent->minor < 0) { 504 return; 505 } 506 507 char deviceNode[DEVICE_FILE_SIZE] = {}; 508 char sysPath[SYSPATH_SIZE] = {}; 509 if (strncpy_s(sysPath, SYSPATH_SIZE - 1, uevent->syspath, strlen(uevent->syspath)) != EOK) { 510 INIT_LOGE("Failed to copy sys path"); 511 return; 512 } 513 const char *devName = GetDeviceName(sysPath, uevent->deviceName); 514 const char *devPath = GetDeviceBasePath(uevent->subsystem); 515 516 if (devPath == NULL || devName == NULL) { 517 INIT_LOGE("Cannot get device path or device name"); 518 return; 519 } 520 INIT_LOGV("HandleOtherDeviceEvent, devPath = %s, devName = %s", devPath, devName); 521 522 // For usb devices, should take care of it specially. 523 // if usb devices report DEVNAME, just create device node. 524 // otherwise, create deviceNode with bus number and device number. 525 if (STRINGEQUAL(uevent->subsystem, "usb")) { 526 if (uevent->deviceName != NULL) { 527 if (snprintf_s(deviceNode, DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, "/dev/%s", uevent->deviceName) == -1) { 528 INIT_LOGE("Make device file for device [%d : %d]", uevent->major, uevent->minor); 529 return; 530 } 531 } else { 532 if (uevent->busNum < 0 || uevent->devNum < 0) { 533 // usb device should always report bus number and device number. 534 INIT_LOGE("usb device with invalid bus number or device number"); 535 return; 536 } 537 if (snprintf_s(deviceNode, DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, 538 "/dev/bus/usb/%03d/%03d", uevent->busNum, uevent->devNum) == -1) { 539 INIT_LOGE("Make usb device node for device [%d : %d]", uevent->busNum, uevent->devNum); 540 } 541 } 542 } else if (STARTSWITH(uevent->subsystem, "usb")) { 543 // Other usb devies, do not handle it. 544 return; 545 } else { 546 if (strcmp(uevent->deviceName, "mapper/control") == 0) { 547 devName = "mapper/control"; 548 } 549 if (snprintf_s(deviceNode, DEVICE_FILE_SIZE, DEVICE_FILE_SIZE - 1, "%s/%s", devPath, devName) == -1) { 550 INIT_LOGE("Make device file for device [%d : %d]", uevent->major, uevent->minor); 551 return; 552 } 553 } 554 HandleDeviceNode(uevent, deviceNode, false); 555} 556