xref: /kernel/liteos_a/fs/proc/src/proc_file.c (revision 0d163575)
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