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
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 }
68 return !strncmp(name, pn->name, len);
69 }
70
ProcFindNode(struct ProcDirEntry * parent,const char * name)71 static 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 */
ProcFindEntry(const char * path)96 struct 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
CheckProcName(const char * name,struct ProcDirEntry ** parent,const char ** lastName)167 static 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
ProcAllocNode(struct ProcDirEntry ** parent,const char * name,mode_t mode)203 static 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
ProcAddNode(struct ProcDirEntry * parent,struct ProcDirEntry * pn)262 static 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
ProcDetachNode(struct ProcDirEntry * pn)299 void 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
ProcCreateDir(struct ProcDirEntry * parent,const char * name,const struct ProcFileOperations * procFileOps,mode_t mode)320 static 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
ProcCreateFile(struct ProcDirEntry * parent,const char * name,const struct ProcFileOperations * procFileOps,mode_t mode)342 static 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
CreateProcEntry(const char * name,mode_t mode,struct ProcDirEntry * parent)370 struct 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
ProcEntryClearVnode(struct ProcDirEntry * entry)382 void 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
FreeProcEntry(struct ProcDirEntry * entry)401 static 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
ProcFreeEntry(struct ProcDirEntry * pn)418 void ProcFreeEntry(struct ProcDirEntry *pn)
419 {
420 if (atomic_dec_and_test(&pn->count)) {
421 FreeProcEntry(pn);
422 }
423 }
424
RemoveProcEntryTravalsal(struct ProcDirEntry * pn)425 void 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
RemoveProcEntry(const char * name,struct ProcDirEntry * parent)438 void 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
ProcMkdirMode(const char * name,mode_t mode,struct ProcDirEntry * parent)470 struct ProcDirEntry *ProcMkdirMode(const char *name, mode_t mode, struct ProcDirEntry *parent)
471 {
472 return ProcCreateDir(parent, name, NULL, mode);
473 }
474
ProcMkdir(const char * name,struct ProcDirEntry * parent)475 struct ProcDirEntry *ProcMkdir(const char *name, struct ProcDirEntry *parent)
476 {
477 return ProcCreateDir(parent, name, NULL, 0);
478 }
479
ProcCreateData(const char * name,mode_t mode,struct ProcDirEntry * parent,const struct ProcFileOperations * procFileOps,struct ProcDataParm * param)480 struct 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
ProcCreate(const char * name,mode_t mode,struct ProcDirEntry * parent,const struct ProcFileOperations * procFileOps)496 struct 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
ProcStat(const char * file,struct ProcStat * buf)502 int 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
GetNextDir(struct ProcDirEntry * pn,void * buf,size_t len)523 static 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
ProcOpen(struct ProcFile * procFile)542 int 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
ProcRead(struct ProcDirEntry * pde,char * buf,size_t len)559 static 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
OpenProcFile(const char * fileName,int flags,...)592 struct 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
ReadProcFile(struct ProcDirEntry * pde,void * buf,size_t len)619 int 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
WriteProcFile(struct ProcDirEntry * pde,const void * buf,size_t len)636 int 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
LseekProcFile(struct ProcDirEntry * pde,loff_t offset,int whence)656 loff_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
LseekDirProcFile(struct ProcDirEntry * pde,off_t * pos,int whence)686 int 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
CloseProcFile(struct ProcDirEntry * pde)697 int 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
GetProcRootEntry(void)722 struct ProcDirEntry *GetProcRootEntry(void)
723 {
724 return &g_procRootDirEntry;
725 }
726