OSDN Git Service

66a5cc9e927ca6f848a0e5f85cec1c4fd75396ac
[uclinux-h8/uClibc.git] / libc / misc / dirent / opendir.c
1 /*
2  * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
3  *
4  * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
5  */
6
7 #include <errno.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <sys/dir.h>
14 #include <sys/stat.h>
15 #include <not-cancel.h>
16 #include <dirent.h>
17 #include "dirstream.h"
18
19 static DIR *fd_to_DIR(int fd, __blksize_t size)
20 {
21         DIR *ptr;
22
23         ptr = malloc(sizeof(*ptr));
24         if (!ptr)
25                 return NULL;
26
27         ptr->dd_fd = fd;
28         ptr->dd_nextloc = ptr->dd_size = ptr->dd_nextoff = 0;
29         ptr->dd_max = size;
30         if (ptr->dd_max < 512)
31                 ptr->dd_max = 512;
32
33         ptr->dd_buf = calloc(1, ptr->dd_max);
34         if (!ptr->dd_buf) {
35                 free(ptr);
36                 return NULL;
37         }
38         __UCLIBC_MUTEX_INIT_VAR(ptr->dd_lock);
39
40         return ptr;
41 }
42
43 DIR *fdopendir(int fd)
44 {
45         int flags;
46         struct stat st;
47
48         if (fstat(fd, &st))
49                 return NULL;
50         if (!S_ISDIR(st.st_mode)) {
51                 __set_errno(ENOTDIR);
52                 return NULL;
53         }
54
55         flags = fcntl(fd, F_GETFL);
56         if (flags == -1)
57                 return NULL;
58         if ((flags & O_ACCMODE) == O_WRONLY) {
59                 __set_errno(EINVAL);
60                 return NULL;
61         }
62
63         return fd_to_DIR(fd, st.st_blksize);
64 }
65
66 /* opendir just makes an open() call - it return NULL if it fails
67  * (open sets errno), otherwise it returns a DIR * pointer.
68  */
69 DIR *opendir(const char *name)
70 {
71         int fd;
72         struct stat statbuf;
73         DIR *ptr;
74
75 #ifndef O_DIRECTORY
76         /* O_DIRECTORY is linux specific and has been around since like 2.1.x */
77         if (stat(name, &statbuf))
78                 return NULL;
79         if (!S_ISDIR(statbuf.st_mode)) {
80                 __set_errno(ENOTDIR);
81                 return NULL;
82         }
83 # define O_DIRECTORY 0
84 #endif
85         fd = open_not_cancel_2(name, O_RDONLY|O_NDELAY|O_DIRECTORY|O_CLOEXEC);
86         if (fd < 0)
87                 return NULL;
88         /* Note: we should check to make sure that between the stat() and open()
89          * call, 'name' didnt change on us, but that's only if O_DIRECTORY isnt
90          * defined and since Linux has supported it for like ever, i'm not going
91          * to worry about it right now (if ever). */
92
93         if (fstat(fd, &statbuf) < 0) {
94                 /* this close() never fails
95                  *int saved_errno;
96                  *saved_errno = errno; */
97                 close_not_cancel_no_status(fd);
98                 /*__set_errno(saved_errno);*/
99                 return NULL;
100         }
101
102         /* According to POSIX, directory streams should be closed when
103          * exec. From "Anna Pluzhnikov" <besp@midway.uchicago.edu>.
104          */
105 #ifndef __ASSUME_O_CLOEXEC
106         fcntl_not_cancel(fd, F_SETFD, FD_CLOEXEC);
107 #endif
108
109         ptr = fd_to_DIR(fd, statbuf.st_blksize);
110
111         if (!ptr) {
112                 close_not_cancel_no_status(fd);
113                 __set_errno(ENOMEM);
114         }
115         return ptr;
116 }
117 libc_hidden_def(opendir)