7a18f0134f9d4cc5cb329844c367b0af11762449
[openafs.git] / src / util / readdir_nt.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * readdir.c - A minimal implementation of readdir to ease porting of AFS to
12  * NT. Include dirent.h to pickup the required structs and prototypes.
13  *
14  * Implemented routines:
15  * opendir
16  * closedir
17  * readdir
18  */
19 #include <afsconfig.h>
20 #include <afs/param.h>
21
22 #include <roken.h>
23
24 #include <afs/errmap_nt.h>
25 #include <windows.h>
26 #include <winbase.h>
27
28 /* opendir() - The case insensitive version of opendir */
29 DIR *
30 opendir(const char *path)
31 {
32     DIR *tDir;
33     HANDLE tH;
34     char tPath[MAX_PATH];
35     WIN32_FIND_DATA tData;
36     int ntErr;
37
38     (void)strcpy(tPath, path);
39     (void)strcat(tPath, "\\*");
40     tH = FindFirstFile(tPath, &tData);
41
42     if (tH == INVALID_HANDLE_VALUE) {
43         ntErr = GetLastError();
44         switch (ntErr) {
45         case ERROR_DIRECTORY:
46             errno = ENOTDIR;
47             return (DIR *) 0;
48         case ERROR_BAD_PATHNAME:
49             /* AFS NT client returns ERROR_BAD_PATHNAME where it should return
50              * ERROR_DIRECTORY.
51              */
52         case ERROR_FILE_NOT_FOUND:
53             /* If at the "root" directory, then this can happen if it's empty.
54              */
55             {
56                 struct stat status;
57                 size_t len = strlen(tPath) - 1;
58                 tPath[len] = '\0';
59                 if (len >= 2 && tPath[len - 2] != ':') {
60                     tPath[len - 1] = '\0';
61                 }
62                 if (stat(tPath, &status) < 0) {
63                     errno = nterr_nt2unix(GetLastError(), ENOENT);
64                     return (DIR *) 0;
65                 }
66                 if (!(status.st_mode & _S_IFDIR)) {
67                     errno = ENOTDIR;
68                     return (DIR *) 0;
69                 }
70             }
71             break;
72         default:
73             errno = nterr_nt2unix(GetLastError(), ENOENT);
74             return (DIR *) 0;
75         }
76     }
77
78     tDir = (DIR *) malloc(sizeof(DIR));
79     if (!tDir) {
80         errno = ENOMEM;
81     } else {
82         memset((void *)tDir, 0, sizeof(*tDir));
83         tDir->h = tH;
84         tDir->data = tData;
85     }
86     return tDir;
87 }
88
89 int
90 closedir(DIR * dir)
91 {
92     if (!dir || !dir->h) {
93         errno = EINVAL;
94         return -1;
95     }
96
97     if (dir->h != INVALID_HANDLE_VALUE)
98         FindClose(dir->h);
99     free(dir);
100     return 0;
101 }
102
103 struct dirent *
104 readdir(DIR * dir)
105 {
106     int rc;
107
108     if (!dir) {
109         errno = EBADF;
110         return (struct dirent *)0;
111     }
112
113     errno = 0;
114     if (dir->h == INVALID_HANDLE_VALUE)
115         return (struct dirent *)0;
116
117     if (dir->first) {
118         dir->first = 0;
119     } else {
120         while (rc = FindNextFile(dir->h, &dir->data)) {
121             if ((strcmp(dir->data.cFileName, ".") == 0)
122                 || (strcmp(dir->data.cFileName, "..") == 0)) {
123
124                 continue;       /* skip "." and ".." */
125             }
126             break;              /* found a non '.' or '..' entry. */
127         }
128         if (rc == 0) {          /* FindNextFile() failed */
129             if (GetLastError() == ERROR_NO_MORE_FILES)
130                 return (struct dirent *)0;
131             else {
132                 errno = nterr_nt2unix(GetLastError(), EBADF);
133                 return (struct dirent *)0;
134             }
135         }
136     }
137
138     dir->cdirent.d_name = dir->data.cFileName;
139     return &dir->cdirent;
140 }