1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 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
43 DEFINE_SPINLOCK(procfsLock);
44 bool procfsInit = false;
45
46 static struct ProcFile g_procPf = {
47 .fPos = 0,
48 };
49
50 static 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
ProcMatch(unsigned int len,const char * name,struct ProcDirEntry * pn)63 int ProcMatch(unsigned int len, const char *name, struct ProcDirEntry *pn)
64 {
65 if (len != pn->nameLen)
66 return 0;
67 return !strncmp(name, pn->name, len);
68 }
69
ProcFindNode(struct ProcDirEntry * parent,const char * name)70 static struct ProcDirEntry *ProcFindNode(struct ProcDirEntry *parent, const char *name)
71 {
72 struct ProcDirEntry *pn = NULL;
73 int length;
74
75 if ((parent == NULL) || (name == NULL)) {
76 return pn;
77 }
78 length = strlen(name);
79
80 for (pn = parent->subdir; pn != NULL; pn = pn->next) {
81 if ((length == pn->nameLen) && strcmp(pn->name, name) == 0) {
82 break;
83 }
84 }
85
86 return pn;
87 }
88
89 /*
90 * descrition: find the file's handle
91 * path: the file of fullpath
92 * return: the file of handle
93 * add by ll
94 */
ProcFindEntry(const char * path)95 struct ProcDirEntry *ProcFindEntry(const char *path)
96 {
97 struct ProcDirEntry *pn = NULL;
98 int isfoundsub;
99 const char *next = NULL;
100 unsigned int len;
101 int leveltotal = 0;
102 int levelcount = 0;
103 const char *p = NULL;
104 const char *name = path;
105
106 while ((p = strchr(name, '/')) != NULL) {
107 leveltotal++;
108 name = p;
109 name++;
110 }
111 if (leveltotal < 1) {
112 return pn;
113 }
114
115 spin_lock(&procfsLock);
116
117 pn = &g_procRootDirEntry;
118
119 while ((pn != NULL) && (levelcount < leveltotal)) {
120 levelcount++;
121 isfoundsub = 0;
122 while (pn != NULL) {
123 next = strchr(path, '/');
124 if (next == NULL) {
125 while (pn != NULL) {
126 if (strcmp(path, pn->name) == 0) {
127 spin_unlock(&procfsLock);
128 return pn;
129 }
130 pn = pn->next;
131 }
132 pn = NULL;
133 spin_unlock(&procfsLock);
134 return pn;
135 }
136
137 len = next - path;
138 if (pn == &g_procRootDirEntry) {
139 if (levelcount == leveltotal) {
140 spin_unlock(&procfsLock);
141 return pn;
142 }
143 len = g_procRootDirEntry.nameLen;
144 }
145 if (ProcMatch(len, path, pn)) {
146 isfoundsub = 1;
147 path += len + 1;
148 break;
149 }
150
151 pn = pn->next;
152 }
153
154 if ((isfoundsub == 1) && (pn != NULL)) {
155 pn = pn->subdir;
156 } else {
157 pn = NULL;
158 spin_unlock(&procfsLock);
159 return pn;
160 }
161 }
162 spin_unlock(&procfsLock);
163 return NULL;
164 }
165
CheckProcName(const char * name,struct ProcDirEntry ** parent,const char ** lastName)166 static int CheckProcName(const char *name, struct ProcDirEntry **parent, const char **lastName)
167 {
168 struct ProcDirEntry *pn = *parent;
169 const char *segment = name;
170 const char *restName = NULL;
171 int length;
172
173 if (pn == NULL) {
174 pn = &g_procRootDirEntry;
175 }
176
177 spin_lock(&procfsLock);
178
179 restName = strchr(segment, '/');
180 for (; restName != NULL; restName = strchr(segment, '/')) {
181 length = restName - segment;
182 for (pn = pn->subdir; pn != NULL; pn = pn->next) {
183 if (ProcMatch(length, segment, pn)) {
184 break;
185 }
186 }
187 if (pn == NULL) {
188 PRINT_ERR(" Error!No such name '%s'\n", name);
189 spin_unlock(&procfsLock);
190 return -ENOENT;
191 }
192 segment = restName;
193 segment++;
194 }
195 *lastName = segment;
196 *parent = pn;
197 spin_unlock(&procfsLock);
198
199 return 0;
200 }
201
ProcAllocNode(struct ProcDirEntry ** parent,const char * name,mode_t mode)202 static struct ProcDirEntry *ProcAllocNode(struct ProcDirEntry **parent, const char *name, mode_t mode)
203 {
204 struct ProcDirEntry *pn = NULL;
205 const char *lastName = NULL;
206 int ret;
207
208 if ((name == NULL) || (strlen(name) == 0) || (procfsInit == false)) {
209 return pn;
210 }
211
212 if (CheckProcName(name, parent, &lastName) != 0) {
213 return pn;
214 }
215
216 if (strlen(lastName) > NAME_MAX) {
217 return pn;
218 }
219
220 if ((S_ISDIR((*parent)->mode) == 0) || (strchr(lastName, '/'))) {
221 return pn;
222 }
223
224 pn = (struct ProcDirEntry *)malloc(sizeof(struct ProcDirEntry));
225 if (pn == NULL) {
226 return NULL;
227 }
228
229 if ((mode & S_IALLUGO) == 0) {
230 mode |= S_IRUSR | S_IRGRP | S_IROTH;
231 }
232
233 (void)memset_s(pn, sizeof(struct ProcDirEntry), 0, sizeof(struct ProcDirEntry));
234 pn->nameLen = strlen(lastName);
235 pn->mode = mode;
236 ret = memcpy_s(pn->name, sizeof(pn->name), lastName, strlen(lastName) + 1);
237 if (ret != EOK) {
238 free(pn);
239 return NULL;
240 }
241
242 pn->pf = (struct ProcFile *)malloc(sizeof(struct ProcFile));
243 if (pn->pf == NULL) {
244 free(pn);
245 return NULL;
246 }
247 (void)memset_s(pn->pf, sizeof(struct ProcFile), 0, sizeof(struct ProcFile));
248 pn->pf->pPDE = pn;
249 ret = memcpy_s(pn->pf->name, sizeof(pn->pf->name), pn->name, pn->nameLen + 1);
250 if (ret != EOK) {
251 free(pn->pf);
252 free(pn);
253 return NULL;
254 }
255
256 atomic_set(&pn->count, 1);
257 spin_lock_init(&pn->pdeUnloadLock);
258 return pn;
259 }
260
ProcAddNode(struct ProcDirEntry * parent,struct ProcDirEntry * pn)261 static int ProcAddNode(struct ProcDirEntry *parent, struct ProcDirEntry *pn)
262 {
263 struct ProcDirEntry *temp = NULL;
264
265 if (parent == NULL) {
266 PRINT_ERR("%s(): parent is NULL", __FUNCTION__);
267 return -EINVAL;
268 }
269
270 if (pn->parent != NULL) {
271 PRINT_ERR("%s(): node already has a parent", __FUNCTION__);
272 return -EINVAL;
273 }
274
275 if (S_ISDIR(parent->mode) == 0) {
276 PRINT_ERR("%s(): parent is not a directory", __FUNCTION__);
277 return -EINVAL;
278 }
279
280 spin_lock(&procfsLock);
281
282 temp = ProcFindNode(parent, pn->name);
283 if (temp != NULL) {
284 PRINT_ERR("Error!ProcDirEntry '%s/%s' already registered\n", parent->name, pn->name);
285 spin_unlock(&procfsLock);
286 return -EEXIST;
287 }
288
289 pn->parent = parent;
290 pn->next = parent->subdir;
291 parent->subdir = pn;
292
293 spin_unlock(&procfsLock);
294
295 return 0;
296 }
297
ProcDetachNode(struct ProcDirEntry * pn)298 static void ProcDetachNode(struct ProcDirEntry *pn)
299 {
300 struct ProcDirEntry *parent = pn->parent;
301 struct ProcDirEntry **iter = NULL;
302
303 if (parent == NULL) {
304 PRINT_ERR("%s(): node has no parent", __FUNCTION__);
305 return;
306 }
307
308 iter = &parent->subdir;
309 while (*iter != NULL) {
310 if (*iter == pn) {
311 *iter = pn->next;
312 break;
313 }
314 iter = &(*iter)->next;
315 }
316 pn->parent = NULL;
317 }
318
ProcCreateDir(struct ProcDirEntry * parent,const char * name,const struct ProcFileOperations * procFileOps,mode_t mode)319 static struct ProcDirEntry *ProcCreateDir(struct ProcDirEntry *parent, const char *name,
320 const struct ProcFileOperations *procFileOps, mode_t mode)
321 {
322 struct ProcDirEntry *pn = NULL;
323 int ret;
324
325 pn = ProcAllocNode(&parent, name, S_IFDIR | mode);
326 if (pn == NULL) {
327 return pn;
328 }
329 pn->procFileOps = procFileOps;
330 pn->type = VNODE_TYPE_DIR;
331 ret = ProcAddNode(parent, pn);
332 if (ret != 0) {
333 free(pn->pf);
334 free(pn);
335 return NULL;
336 }
337
338 return pn;
339 }
340
ProcCreateFile(struct ProcDirEntry * parent,const char * name,const struct ProcFileOperations * procFileOps,mode_t mode)341 static struct ProcDirEntry *ProcCreateFile(struct ProcDirEntry *parent, const char *name,
342 const struct ProcFileOperations *procFileOps, mode_t mode)
343 {
344 struct ProcDirEntry *pn = NULL;
345 int ret;
346
347 pn = ProcAllocNode(&parent, name, S_IFREG | mode);
348 if (pn == NULL) {
349 return pn;
350 }
351
352 pn->procFileOps = procFileOps;
353 pn->type = VNODE_TYPE_REG;
354 ret = ProcAddNode(parent, pn);
355 if (ret != 0) {
356 free(pn->pf);
357 free(pn);
358 return NULL;
359 }
360
361 return pn;
362 }
363
CreateProcEntry(const char * name,mode_t mode,struct ProcDirEntry * parent)364 struct ProcDirEntry *CreateProcEntry(const char *name, mode_t mode, struct ProcDirEntry *parent)
365 {
366 struct ProcDirEntry *pde = NULL;
367
368 if (S_ISDIR(mode)) {
369 pde = ProcCreateDir(parent, name, NULL, mode);
370 } else {
371 pde = ProcCreateFile(parent, name, NULL, mode);
372 }
373 return pde;
374 }
375
FreeProcEntry(struct ProcDirEntry * entry)376 static void FreeProcEntry(struct ProcDirEntry *entry)
377 {
378 if (entry == NULL) {
379 return;
380 }
381 if (entry->pf != NULL) {
382 free(entry->pf);
383 entry->pf = NULL;
384 }
385 free(entry);
386 }
387
ProcFreeEntry(struct ProcDirEntry * pn)388 void ProcFreeEntry(struct ProcDirEntry *pn)
389 {
390 if (atomic_dec_and_test(&pn->count))
391 FreeProcEntry(pn);
392 }
393
RemoveProcEntryTravalsal(struct ProcDirEntry * pn)394 static void RemoveProcEntryTravalsal(struct ProcDirEntry *pn)
395 {
396 if (pn == NULL) {
397 return;
398 }
399 RemoveProcEntryTravalsal(pn->next);
400 RemoveProcEntryTravalsal(pn->subdir);
401
402 ProcFreeEntry(pn);
403 }
404
RemoveProcEntry(const char * name,struct ProcDirEntry * parent)405 void RemoveProcEntry(const char *name, struct ProcDirEntry *parent)
406 {
407 struct ProcDirEntry *pn = NULL;
408 const char *lastName = name;
409
410 if ((name == NULL) || (strlen(name) == 0) || (procfsInit == false)) {
411 return;
412 }
413
414 if (CheckProcName(name, &parent, &lastName) != 0) {
415 return;
416 }
417
418 spin_lock(&procfsLock);
419
420 pn = ProcFindNode(parent, lastName);
421 if (pn == NULL) {
422 PRINT_ERR("Error:name '%s' not found!\n", name);
423 spin_unlock(&procfsLock);
424 return;
425 }
426 ProcDetachNode(pn);
427
428 spin_unlock(&procfsLock);
429
430 RemoveProcEntryTravalsal(pn->subdir);
431 ProcFreeEntry(pn);
432 }
433
ProcMkdirMode(const char * name,mode_t mode,struct ProcDirEntry * parent)434 struct ProcDirEntry *ProcMkdirMode(const char *name, mode_t mode, struct ProcDirEntry *parent)
435 {
436 return ProcCreateDir(parent, name, NULL, mode);
437 }
438
ProcMkdir(const char * name,struct ProcDirEntry * parent)439 struct ProcDirEntry *ProcMkdir(const char *name, struct ProcDirEntry *parent)
440 {
441 return ProcCreateDir(parent, name, NULL, 0);
442 }
443
ProcCreateData(const char * name,mode_t mode,struct ProcDirEntry * parent,const struct ProcFileOperations * procFileOps,void * data)444 struct ProcDirEntry *ProcCreateData(const char *name, mode_t mode, struct ProcDirEntry *parent,
445 const struct ProcFileOperations *procFileOps, void *data)
446 {
447 struct ProcDirEntry *pde = CreateProcEntry(name, mode, parent);
448 if (pde != NULL) {
449 if (procFileOps != NULL) {
450 pde->procFileOps = procFileOps;
451 }
452 pde->data = data;
453 }
454 return pde;
455 }
456
ProcCreate(const char * name,mode_t mode,struct ProcDirEntry * parent,const struct ProcFileOperations * procFileOps)457 struct ProcDirEntry *ProcCreate(const char *name, mode_t mode, struct ProcDirEntry *parent,
458 const struct ProcFileOperations *procFileOps)
459 {
460 return ProcCreateData(name, mode, parent, procFileOps, NULL);
461 }
462
ProcStat(const char * file,struct ProcStat * buf)463 int ProcStat(const char *file, struct ProcStat *buf)
464 {
465 struct ProcDirEntry *pn = NULL;
466 int len = sizeof(buf->name);
467 int ret;
468
469 pn = ProcFindEntry(file);
470 if (pn == NULL) {
471 return ENOENT;
472 }
473 ret = strncpy_s(buf->name, len, pn->name, len - 1);
474 if (ret != EOK) {
475 return ENAMETOOLONG;
476 }
477 buf->name[len - 1] = '\0';
478 buf->stMode = pn->mode;
479 buf->pPDE = pn;
480
481 return 0;
482 }
483
GetNextDir(struct ProcDirEntry * pn,void * buf,size_t len)484 static int GetNextDir(struct ProcDirEntry *pn, void *buf, size_t len)
485 {
486 char *buff = (char *)buf;
487
488 if (pn->pdirCurrent == NULL) {
489 *buff = '\0';
490 return -ENOENT;
491 }
492 int namelen = pn->pdirCurrent->nameLen;
493 int ret = memcpy_s(buff, len, pn->pdirCurrent->name, namelen);
494 if (ret != EOK) {
495 return -ENAMETOOLONG;
496 }
497
498 pn->pdirCurrent = pn->pdirCurrent->next;
499 pn->pf->fPos++;
500 return ENOERR;
501 }
502
ProcOpen(struct ProcFile * procFile)503 int ProcOpen(struct ProcFile *procFile)
504 {
505 if (procFile == NULL) {
506 return PROC_ERROR;
507 }
508 if (procFile->sbuf != NULL) {
509 return OK;
510 }
511
512 struct SeqBuf *buf = LosBufCreat();
513 if (buf == NULL) {
514 return PROC_ERROR;
515 }
516 procFile->sbuf = buf;
517 return OK;
518 }
519
ProcRead(struct ProcDirEntry * pde,char * buf,size_t len)520 static int ProcRead(struct ProcDirEntry *pde, char *buf, size_t len)
521 {
522 if (pde == NULL || pde == NULL || pde->pf == NULL) {
523 return PROC_ERROR;
524 }
525 struct ProcFile *procFile = pde->pf;
526 struct SeqBuf *sb = procFile->sbuf;
527
528 if (sb->buf == NULL) {
529 // only read once to build the storage buffer
530 if (pde->procFileOps->read(sb, NULL) != 0) {
531 return PROC_ERROR;
532 }
533 }
534
535 size_t realLen;
536 loff_t pos = procFile->fPos;
537
538 if ((pos >= sb->count) || (len == 0)) {
539 /* there's no data or at the file tail. */
540 realLen = 0;
541 } else {
542 realLen = MIN((sb->count - pos), MIN(len, INT_MAX));
543 if (LOS_CopyFromKernel(buf, len, sb->buf + pos, realLen) != 0) {
544 return PROC_ERROR;
545 }
546
547 procFile->fPos = pos + realLen;
548 }
549
550 return (ssize_t)realLen;
551 }
552
OpenProcFile(const char * fileName,int flags,...)553 struct ProcDirEntry *OpenProcFile(const char *fileName, int flags, ...)
554 {
555 unsigned int intSave;
556 struct ProcDirEntry *pn = ProcFindEntry(fileName);
557 if (pn == NULL) {
558 return NULL;
559 }
560
561 SCHEDULER_LOCK(intSave);
562 if (S_ISREG(pn->mode) && (pn->count != 1)) {
563 SCHEDULER_UNLOCK(intSave);
564 return NULL;
565 }
566
567 pn->flags = (unsigned int)(pn->flags) | (unsigned int)flags;
568 atomic_set(&pn->count, PROC_INUSE);
569 SCHEDULER_UNLOCK(intSave);
570 if (ProcOpen(pn->pf) != OK) {
571 return NULL;
572 }
573 if (S_ISREG(pn->mode) && (pn->procFileOps != NULL) && (pn->procFileOps->open != NULL)) {
574 (void)pn->procFileOps->open((struct Vnode *)pn, pn->pf);
575 }
576 if (S_ISDIR(pn->mode)) {
577 pn->pdirCurrent = pn->subdir;
578 pn->pf->fPos = 0;
579 }
580
581 return pn;
582 }
583
ReadProcFile(struct ProcDirEntry * pde,void * buf,size_t len)584 int ReadProcFile(struct ProcDirEntry *pde, void *buf, size_t len)
585 {
586 int result = -EPERM;
587
588 if (pde == NULL) {
589 return result;
590 }
591 if (S_ISREG(pde->mode)) {
592 if ((pde->procFileOps != NULL) && (pde->procFileOps->read != NULL)) {
593 result = ProcRead(pde, (char *)buf, len);
594 }
595 } else if (S_ISDIR(pde->mode)) {
596 result = GetNextDir(pde, buf, len);
597 }
598 return result;
599 }
600
WriteProcFile(struct ProcDirEntry * pde,const void * buf,size_t len)601 int WriteProcFile(struct ProcDirEntry *pde, const void *buf, size_t len)
602 {
603 int result = -EPERM;
604
605 if (pde == NULL) {
606 return result;
607 }
608
609 if (S_ISDIR(pde->mode)) {
610 return -EISDIR;
611 }
612
613 spin_lock(&procfsLock);
614 if ((pde->procFileOps != NULL) && (pde->procFileOps->write != NULL)) {
615 result = pde->procFileOps->write(pde->pf, (const char *)buf, len, &(pde->pf->fPos));
616 }
617 spin_unlock(&procfsLock);
618
619 return result;
620 }
621
LseekProcFile(struct ProcDirEntry * pde,loff_t offset,int whence)622 loff_t LseekProcFile(struct ProcDirEntry *pde, loff_t offset, int whence)
623 {
624 if (pde == NULL || pde->pf == NULL) {
625 return PROC_ERROR;
626 }
627
628 struct ProcFile *procFile = pde->pf;
629
630 loff_t result = -EINVAL;
631
632 switch (whence) {
633 case SEEK_CUR:
634 result = procFile->fPos + offset;
635 break;
636
637 case SEEK_SET:
638 result = offset;
639 break;
640
641 default:
642 break;
643 }
644
645 if (result >= 0) {
646 procFile->fPos = result;
647 }
648
649 return result;
650 }
651
LseekDirProcFile(struct ProcDirEntry * pde,off_t * pos,int whence)652 int LseekDirProcFile(struct ProcDirEntry *pde, off_t *pos, int whence)
653 {
654 /* Only allow SEEK_SET to zero */
655 if ((whence != SEEK_SET) || (*pos != 0)) {
656 return EINVAL;
657 }
658 pde->pdirCurrent = pde->subdir;
659 pde->pf->fPos = 0;
660 return ENOERR;
661 }
662
CloseProcFile(struct ProcDirEntry * pde)663 int CloseProcFile(struct ProcDirEntry *pde)
664 {
665 int result = 0;
666
667 if (pde == NULL) {
668 return -EPERM;
669 }
670 pde->pf->fPos = 0;
671 atomic_set(&pde->count, 1);
672 if (S_ISDIR(pde->mode)) {
673 pde->pdirCurrent = pde->subdir;
674 }
675
676 if ((pde->procFileOps != NULL) && (pde->procFileOps->release != NULL)) {
677 result = pde->procFileOps->release((struct Vnode *)pde, pde->pf);
678 }
679 LosBufRelease(pde->pf->sbuf);
680 pde->pf->sbuf = NULL;
681
682 if (pde->parent == NULL) {
683 FreeProcEntry(pde);
684 }
685 return result;
686 }
687
GetProcRootEntry(void)688 struct ProcDirEntry *GetProcRootEntry(void)
689 {
690 return &g_procRootDirEntry;
691 }
692