1/* 2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. 3 * Copyright (c) 2020-2023 Huawei Device Co., Ltd. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this list of 9 * conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 12 * of conditions and the following disclaimer in the documentation and/or other materials 13 * provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used 16 * to endorse or promote products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "proc_file.h" 33#include <stdio.h> 34#include <linux/errno.h> 35#include <linux/module.h> 36#include "internal.h" 37#include "user_copy.h" 38 39#define MIN(a, b) ((a) < (b) ? (a) : (b)) 40#define PROC_ROOTDIR_NAMELEN 5 41#define PROC_INUSE 2 42 43DEFINE_SPINLOCK(procfsLock); 44bool procfsInit = false; 45 46static struct ProcFile g_procPf = { 47 .fPos = 0, 48}; 49 50static struct ProcDirEntry g_procRootDirEntry = { 51 .nameLen = 5, 52 .mode = S_IFDIR | PROCFS_DEFAULT_MODE, 53 .count = ATOMIC_INIT(1), 54 .procFileOps = NULL, 55 .parent = &g_procRootDirEntry, 56 .name = "/proc", 57 .subdir = NULL, 58 .next = NULL, 59 .pf = &g_procPf, 60 .type = VNODE_TYPE_DIR, 61}; 62 63int ProcMatch(unsigned int len, const char *name, struct ProcDirEntry *pn) 64{ 65 if (len != pn->nameLen) { 66 return 0; 67 } 68 return !strncmp(name, pn->name, len); 69} 70 71static struct ProcDirEntry *ProcFindNode(struct ProcDirEntry *parent, const char *name) 72{ 73 struct ProcDirEntry *pn = NULL; 74 int length; 75 76 if ((parent == NULL) || (name == NULL)) { 77 return pn; 78 } 79 length = strlen(name); 80 81 for (pn = parent->subdir; pn != NULL; pn = pn->next) { 82 if ((length == pn->nameLen) && strcmp(pn->name, name) == 0) { 83 break; 84 } 85 } 86 87 return pn; 88} 89 90/* 91 * description: find the file's handle 92 * path: the file of fullpath 93 * return: the file of handle 94 * add by ll 95 */ 96struct ProcDirEntry *ProcFindEntry(const char *path) 97{ 98 struct ProcDirEntry *pn = NULL; 99 int isfoundsub; 100 const char *next = NULL; 101 unsigned int len; 102 int leveltotal = 0; 103 int levelcount = 0; 104 const char *p = NULL; 105 const char *name = path; 106 107 while ((p = strchr(name, '/')) != NULL) { 108 leveltotal++; 109 name = p; 110 name++; 111 } 112 if (leveltotal < 1) { 113 return pn; 114 } 115 116 spin_lock(&procfsLock); 117 118 pn = &g_procRootDirEntry; 119 120 while ((pn != NULL) && (levelcount < leveltotal)) { 121 levelcount++; 122 isfoundsub = 0; 123 while (pn != NULL) { 124 next = strchr(path, '/'); 125 if (next == NULL) { 126 while (pn != NULL) { 127 if (strcmp(path, pn->name) == 0) { 128 spin_unlock(&procfsLock); 129 return pn; 130 } 131 pn = pn->next; 132 } 133 pn = NULL; 134 spin_unlock(&procfsLock); 135 return pn; 136 } 137 138 len = next - path; 139 if (pn == &g_procRootDirEntry) { 140 if (levelcount == leveltotal) { 141 spin_unlock(&procfsLock); 142 return pn; 143 } 144 len = g_procRootDirEntry.nameLen; 145 } 146 if (ProcMatch(len, path, pn)) { 147 isfoundsub = 1; 148 path += len + 1; 149 break; 150 } 151 152 pn = pn->next; 153 } 154 155 if ((isfoundsub == 1) && (pn != NULL)) { 156 pn = pn->subdir; 157 } else { 158 pn = NULL; 159 spin_unlock(&procfsLock); 160 return pn; 161 } 162 } 163 spin_unlock(&procfsLock); 164 return NULL; 165} 166 167static int CheckProcName(const char *name, struct ProcDirEntry **parent, const char **lastName) 168{ 169 struct ProcDirEntry *pn = *parent; 170 const char *segment = name; 171 const char *restName = NULL; 172 int length; 173 174 if (pn == NULL) { 175 pn = &g_procRootDirEntry; 176 } 177 178 spin_lock(&procfsLock); 179 180 restName = strchr(segment, '/'); 181 for (; restName != NULL; restName = strchr(segment, '/')) { 182 length = restName - segment; 183 for (pn = pn->subdir; pn != NULL; pn = pn->next) { 184 if (ProcMatch(length, segment, pn)) { 185 break; 186 } 187 } 188 if (pn == NULL) { 189 PRINT_ERR(" Error!No such name '%s'\n", name); 190 spin_unlock(&procfsLock); 191 return -ENOENT; 192 } 193 segment = restName; 194 segment++; 195 } 196 *lastName = segment; 197 *parent = pn; 198 spin_unlock(&procfsLock); 199 200 return 0; 201} 202 203static struct ProcDirEntry *ProcAllocNode(struct ProcDirEntry **parent, const char *name, mode_t mode) 204{ 205 struct ProcDirEntry *pn = NULL; 206 const char *lastName = NULL; 207 int ret; 208 209 if ((name == NULL) || (strlen(name) == 0) || (procfsInit == false)) { 210 return pn; 211 } 212 213 if (CheckProcName(name, parent, &lastName) != 0) { 214 return pn; 215 } 216 217 if (strlen(lastName) > NAME_MAX) { 218 return pn; 219 } 220 221 if ((S_ISDIR((*parent)->mode) == 0) || (strchr(lastName, '/'))) { 222 return pn; 223 } 224 225 pn = (struct ProcDirEntry *)malloc(sizeof(struct ProcDirEntry)); 226 if (pn == NULL) { 227 return NULL; 228 } 229 230 if ((mode & S_IALLUGO) == 0) { 231 mode |= S_IRUSR | S_IRGRP | S_IROTH; 232 } 233 234 (void)memset_s(pn, sizeof(struct ProcDirEntry), 0, sizeof(struct ProcDirEntry)); 235 pn->nameLen = strlen(lastName); 236 pn->mode = mode; 237 ret = memcpy_s(pn->name, sizeof(pn->name), lastName, strlen(lastName) + 1); 238 if (ret != EOK) { 239 free(pn); 240 return NULL; 241 } 242 243 pn->pf = (struct ProcFile *)malloc(sizeof(struct ProcFile)); 244 if (pn->pf == NULL) { 245 free(pn); 246 return NULL; 247 } 248 (void)memset_s(pn->pf, sizeof(struct ProcFile), 0, sizeof(struct ProcFile)); 249 pn->pf->pPDE = pn; 250 ret = memcpy_s(pn->pf->name, sizeof(pn->pf->name), pn->name, pn->nameLen + 1); 251 if (ret != EOK) { 252 free(pn->pf); 253 free(pn); 254 return NULL; 255 } 256 257 atomic_set(&pn->count, 1); 258 spin_lock_init(&pn->pdeUnloadLock); 259 return pn; 260} 261 262static int ProcAddNode(struct ProcDirEntry *parent, struct ProcDirEntry *pn) 263{ 264 struct ProcDirEntry *temp = NULL; 265 266 if (parent == NULL) { 267 PRINT_ERR("%s(): parent is NULL", __FUNCTION__); 268 return -EINVAL; 269 } 270 271 if (pn->parent != NULL) { 272 PRINT_ERR("%s(): node already has a parent", __FUNCTION__); 273 return -EINVAL; 274 } 275 276 if (S_ISDIR(parent->mode) == 0) { 277 PRINT_ERR("%s(): parent is not a directory", __FUNCTION__); 278 return -EINVAL; 279 } 280 281 spin_lock(&procfsLock); 282 283 temp = ProcFindNode(parent, pn->name); 284 if (temp != NULL) { 285 PRINT_ERR("Error!ProcDirEntry '%s/%s' already registered\n", parent->name, pn->name); 286 spin_unlock(&procfsLock); 287 return -EEXIST; 288 } 289 290 pn->parent = parent; 291 pn->next = parent->subdir; 292 parent->subdir = pn; 293 294 spin_unlock(&procfsLock); 295 296 return 0; 297} 298 299void ProcDetachNode(struct ProcDirEntry *pn) 300{ 301 struct ProcDirEntry *parent = pn->parent; 302 struct ProcDirEntry **iter = NULL; 303 304 if (parent == NULL) { 305 PRINT_ERR("%s(): node has no parent", __FUNCTION__); 306 return; 307 } 308 309 iter = &parent->subdir; 310 while (*iter != NULL) { 311 if (*iter == pn) { 312 *iter = pn->next; 313 break; 314 } 315 iter = &(*iter)->next; 316 } 317 pn->parent = NULL; 318} 319 320static struct ProcDirEntry *ProcCreateDir(struct ProcDirEntry *parent, const char *name, 321 const struct ProcFileOperations *procFileOps, mode_t mode) 322{ 323 struct ProcDirEntry *pn = NULL; 324 int ret; 325 326 pn = ProcAllocNode(&parent, name, S_IFDIR | mode); 327 if (pn == NULL) { 328 return pn; 329 } 330 pn->procFileOps = procFileOps; 331 pn->type = VNODE_TYPE_DIR; 332 ret = ProcAddNode(parent, pn); 333 if (ret != 0) { 334 free(pn->pf); 335 free(pn); 336 return NULL; 337 } 338 339 return pn; 340} 341 342static struct ProcDirEntry *ProcCreateFile(struct ProcDirEntry *parent, const char *name, 343 const struct ProcFileOperations *procFileOps, mode_t mode) 344{ 345 struct ProcDirEntry *pn = NULL; 346 int ret; 347 348 pn = ProcAllocNode(&parent, name, S_IFREG | mode); 349 if (pn == NULL) { 350 return pn; 351 } 352 353 pn->procFileOps = procFileOps; 354 pn->type = VNODE_TYPE_REG; 355#ifdef LOSCFG_PROC_PROCESS_DIR 356 if (S_ISLNK(mode)) { 357 pn->type = VNODE_TYPE_VIR_LNK; 358 } 359#endif 360 ret = ProcAddNode(parent, pn); 361 if (ret != 0) { 362 free(pn->pf); 363 free(pn); 364 return NULL; 365 } 366 367 return pn; 368} 369 370struct ProcDirEntry *CreateProcEntry(const char *name, mode_t mode, struct ProcDirEntry *parent) 371{ 372 struct ProcDirEntry *pde = NULL; 373 374 if (S_ISDIR(mode)) { 375 pde = ProcCreateDir(parent, name, NULL, mode); 376 } else { 377 pde = ProcCreateFile(parent, name, NULL, mode); 378 } 379 return pde; 380} 381 382void ProcEntryClearVnode(struct ProcDirEntry *entry) 383{ 384 struct Vnode *item = NULL; 385 struct Vnode *nextItem = NULL; 386 387 VnodeHold(); 388 LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(item, nextItem, GetVnodeActiveList(), struct Vnode, actFreeEntry) { 389 if ((struct ProcDirEntry *)item->data != entry) { 390 continue; 391 } 392 393 if (VnodeFree(item) != LOS_OK) { 394 PRINT_ERR("ProcEntryClearVnode free failed, entry: %s\n", entry->name); 395 } 396 } 397 VnodeDrop(); 398 return; 399} 400 401static void FreeProcEntry(struct ProcDirEntry *entry) 402{ 403 if (entry == NULL) { 404 return; 405 } 406 407 if (entry->pf != NULL) { 408 free(entry->pf); 409 entry->pf = NULL; 410 } 411 if ((entry->dataType == PROC_DATA_FREE) && (entry->data != NULL)) { 412 free(entry->data); 413 } 414 entry->data = NULL; 415 free(entry); 416} 417 418void ProcFreeEntry(struct ProcDirEntry *pn) 419{ 420 if (atomic_dec_and_test(&pn->count)) { 421 FreeProcEntry(pn); 422 } 423} 424 425void RemoveProcEntryTravalsal(struct ProcDirEntry *pn) 426{ 427 if (pn == NULL) { 428 return; 429 } 430 RemoveProcEntryTravalsal(pn->next); 431 RemoveProcEntryTravalsal(pn->subdir); 432 433 ProcEntryClearVnode(pn); 434 435 ProcFreeEntry(pn); 436} 437 438void RemoveProcEntry(const char *name, struct ProcDirEntry *parent) 439{ 440 struct ProcDirEntry *pn = NULL; 441 const char *lastName = name; 442 443 if ((name == NULL) || (strlen(name) == 0) || (procfsInit == false)) { 444 return; 445 } 446 447 if (CheckProcName(name, &parent, &lastName) != 0) { 448 return; 449 } 450 451 spin_lock(&procfsLock); 452 453 pn = ProcFindNode(parent, lastName); 454 if (pn == NULL) { 455 PRINT_ERR("Error:name '%s' not found!\n", name); 456 spin_unlock(&procfsLock); 457 return; 458 } 459 ProcDetachNode(pn); 460 461 spin_unlock(&procfsLock); 462 463 RemoveProcEntryTravalsal(pn->subdir); 464 465 ProcEntryClearVnode(pn); 466 467 ProcFreeEntry(pn); 468} 469 470struct ProcDirEntry *ProcMkdirMode(const char *name, mode_t mode, struct ProcDirEntry *parent) 471{ 472 return ProcCreateDir(parent, name, NULL, mode); 473} 474 475struct ProcDirEntry *ProcMkdir(const char *name, struct ProcDirEntry *parent) 476{ 477 return ProcCreateDir(parent, name, NULL, 0); 478} 479 480struct ProcDirEntry *ProcCreateData(const char *name, mode_t mode, struct ProcDirEntry *parent, 481 const struct ProcFileOperations *procFileOps, struct ProcDataParm *param) 482{ 483 struct ProcDirEntry *pde = CreateProcEntry(name, mode, parent); 484 if (pde != NULL) { 485 if (procFileOps != NULL) { 486 pde->procFileOps = procFileOps; 487 } 488 if (param != NULL) { 489 pde->data = param->data; 490 pde->dataType = param->dataType; 491 } 492 } 493 return pde; 494} 495 496struct ProcDirEntry *ProcCreate(const char *name, mode_t mode, struct ProcDirEntry *parent, 497 const struct ProcFileOperations *procFileOps) 498{ 499 return ProcCreateData(name, mode, parent, procFileOps, NULL); 500} 501 502int ProcStat(const char *file, struct ProcStat *buf) 503{ 504 struct ProcDirEntry *pn = NULL; 505 int len = sizeof(buf->name); 506 int ret; 507 508 pn = ProcFindEntry(file); 509 if (pn == NULL) { 510 return ENOENT; 511 } 512 ret = strncpy_s(buf->name, len, pn->name, len - 1); 513 if (ret != EOK) { 514 return ENAMETOOLONG; 515 } 516 buf->name[len - 1] = '\0'; 517 buf->stMode = pn->mode; 518 buf->pPDE = pn; 519 520 return 0; 521} 522 523static int GetNextDir(struct ProcDirEntry *pn, void *buf, size_t len) 524{ 525 char *buff = (char *)buf; 526 527 if (pn->pdirCurrent == NULL) { 528 *buff = '\0'; 529 return -ENOENT; 530 } 531 int namelen = pn->pdirCurrent->nameLen; 532 int ret = memcpy_s(buff, len, pn->pdirCurrent->name, namelen); 533 if (ret != EOK) { 534 return -ENAMETOOLONG; 535 } 536 537 pn->pdirCurrent = pn->pdirCurrent->next; 538 pn->pf->fPos++; 539 return ENOERR; 540} 541 542int ProcOpen(struct ProcFile *procFile) 543{ 544 if (procFile == NULL) { 545 return PROC_ERROR; 546 } 547 if (procFile->sbuf != NULL) { 548 return OK; 549 } 550 551 struct SeqBuf *buf = LosBufCreat(); 552 if (buf == NULL) { 553 return PROC_ERROR; 554 } 555 procFile->sbuf = buf; 556 return OK; 557} 558 559static int ProcRead(struct ProcDirEntry *pde, char *buf, size_t len) 560{ 561 if (pde == NULL || pde->pf == NULL) { 562 return PROC_ERROR; 563 } 564 struct ProcFile *procFile = pde->pf; 565 struct SeqBuf *sb = procFile->sbuf; 566 567 if (sb->buf == NULL) { 568 // only read once to build the storage buffer 569 if (pde->procFileOps->read(sb, pde->data) != 0) { 570 return PROC_ERROR; 571 } 572 } 573 574 size_t realLen; 575 loff_t pos = procFile->fPos; 576 577 if ((pos >= sb->count) || (len == 0)) { 578 /* there's no data or at the file tail. */ 579 realLen = 0; 580 } else { 581 realLen = MIN((sb->count - pos), MIN(len, INT_MAX)); 582 if (LOS_CopyFromKernel(buf, len, sb->buf + pos, realLen) != 0) { 583 return PROC_ERROR; 584 } 585 586 procFile->fPos = pos + realLen; 587 } 588 589 return (ssize_t)realLen; 590} 591 592struct ProcDirEntry *OpenProcFile(const char *fileName, int flags, ...) 593{ 594 struct ProcDirEntry *pn = ProcFindEntry(fileName); 595 if (pn == NULL) { 596 return NULL; 597 } 598 599 if (S_ISREG(pn->mode) && (pn->count != 1)) { 600 return NULL; 601 } 602 603 pn->flags = (unsigned int)(pn->flags) | (unsigned int)flags; 604 atomic_set(&pn->count, PROC_INUSE); 605 if (ProcOpen(pn->pf) != OK) { 606 return NULL; 607 } 608 if (S_ISREG(pn->mode) && (pn->procFileOps != NULL) && (pn->procFileOps->open != NULL)) { 609 (void)pn->procFileOps->open((struct Vnode *)pn, pn->pf); 610 } 611 if (S_ISDIR(pn->mode)) { 612 pn->pdirCurrent = pn->subdir; 613 pn->pf->fPos = 0; 614 } 615 616 return pn; 617} 618 619int ReadProcFile(struct ProcDirEntry *pde, void *buf, size_t len) 620{ 621 int result = -EPERM; 622 623 if (pde == NULL) { 624 return result; 625 } 626 if (S_ISREG(pde->mode)) { 627 if ((pde->procFileOps != NULL) && (pde->procFileOps->read != NULL)) { 628 result = ProcRead(pde, (char *)buf, len); 629 } 630 } else if (S_ISDIR(pde->mode)) { 631 result = GetNextDir(pde, buf, len); 632 } 633 return result; 634} 635 636int WriteProcFile(struct ProcDirEntry *pde, const void *buf, size_t len) 637{ 638 int result = -EPERM; 639 640 if (pde == NULL) { 641 return result; 642 } 643 644 if (S_ISDIR(pde->mode)) { 645 return -EISDIR; 646 } 647 648 spin_lock(&procfsLock); 649 if ((pde->procFileOps != NULL) && (pde->procFileOps->write != NULL)) { 650 result = pde->procFileOps->write(pde->pf, (const char *)buf, len, &(pde->pf->fPos)); 651 } 652 spin_unlock(&procfsLock); 653 return result; 654} 655 656loff_t LseekProcFile(struct ProcDirEntry *pde, loff_t offset, int whence) 657{ 658 if (pde == NULL || pde->pf == NULL) { 659 return PROC_ERROR; 660 } 661 662 struct ProcFile *procFile = pde->pf; 663 664 loff_t result = -EINVAL; 665 666 switch (whence) { 667 case SEEK_CUR: 668 result = procFile->fPos + offset; 669 break; 670 671 case SEEK_SET: 672 result = offset; 673 break; 674 675 default: 676 break; 677 } 678 679 if (result >= 0) { 680 procFile->fPos = result; 681 } 682 683 return result; 684} 685 686int LseekDirProcFile(struct ProcDirEntry *pde, off_t *pos, int whence) 687{ 688 /* Only allow SEEK_SET to zero */ 689 if ((whence != SEEK_SET) || (*pos != 0)) { 690 return EINVAL; 691 } 692 pde->pdirCurrent = pde->subdir; 693 pde->pf->fPos = 0; 694 return ENOERR; 695} 696 697int CloseProcFile(struct ProcDirEntry *pde) 698{ 699 int result = 0; 700 701 if (pde == NULL) { 702 return -EPERM; 703 } 704 pde->pf->fPos = 0; 705 atomic_set(&pde->count, 1); 706 if (S_ISDIR(pde->mode)) { 707 pde->pdirCurrent = pde->subdir; 708 } 709 710 if ((pde->procFileOps != NULL) && (pde->procFileOps->release != NULL)) { 711 result = pde->procFileOps->release((struct Vnode *)pde, pde->pf); 712 } 713 LosBufRelease(pde->pf->sbuf); 714 pde->pf->sbuf = NULL; 715 716 if (pde->parent == NULL) { 717 FreeProcEntry(pde); 718 } 719 return result; 720} 721 722struct ProcDirEntry *GetProcRootEntry(void) 723{ 724 return &g_procRootDirEntry; 725} 726