[project @ 1999-11-26 16:25:55 by simonmar]
[ghc-hetmet.git] / ghc / lib / std / cbits / openFile.c
1 /* 
2  * (c) The GRASP/AQUA Project, Glasgow University, 1994-1998
3  *
4  * $Id: openFile.c,v 1.12 1999/11/26 16:25:56 simonmar Exp $
5  *
6  * openFile Runtime Support
7  */
8
9 /* We use lstat, which is sadly not POSIX */
10 #define NON_POSIX_SOURCE
11
12 #include "Rts.h"
13 #include "stgio.h"
14
15 #ifdef HAVE_SYS_TYPES_H
16 #include <sys/types.h>
17 #endif
18
19 #ifdef HAVE_SYS_STAT_H
20 #include <sys/stat.h>
21 #endif
22
23 #ifdef HAVE_UNISTD_H
24 #include <unistd.h>
25 #endif
26
27 #ifdef HAVE_FCNTL_H
28 #include <fcntl.h>
29 #endif
30
31 #if defined(mingw32_TARGET_OS) && !defined(O_NOCTTY)
32 #define O_NOCTTY 0
33 #endif
34
35 IOFileObject*
36 openStdFile(fd,rd)
37 StgInt fd;
38 StgInt rd;
39 {
40     IOFileObject* fo;
41     long fd_flags;
42
43     if ((fo = malloc(sizeof(IOFileObject))) == NULL)
44        return NULL;
45     fo->fd       = fd;
46     fo->buf      = NULL;
47     fo->bufWPtr  = 0;
48     fo->bufRPtr  = 0;
49     fo->flags    = FILEOBJ_STD | ( rd ? FILEOBJ_READ : FILEOBJ_WRITE);
50     fo->connectedTo = NULL;
51  
52     /* MS Win32 CRT doesn't support fcntl() -- the workaround is to
53        start using 'completion ports', but I'm punting on implementing
54        support for using those.
55     */
56 #if !defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__)
57     /* set the non-blocking flag on this file descriptor */
58     fd_flags = fcntl(fd, F_GETFL);
59     fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
60 #endif
61
62    return fo;
63 }
64
65 #define OPENFILE_APPEND 0
66 #define OPENFILE_WRITE 1
67 #define OPENFILE_READ_ONLY 2
68 #define OPENFILE_READ_WRITE 3
69
70 IOFileObject*
71 openFile(file, how, binary)
72 StgByteArray file;
73 StgInt how;
74 StgInt binary;
75 {
76     FILE *fp;
77     int fd;
78     int oflags;
79     int for_writing;
80     int created = 0;
81     struct stat sb;
82     IOFileObject* fo;
83     int flags = 0;
84
85 #if defined(_WIN32) && !(defined(__CYGWIN__) || defined(__CYGWIN32__))
86 #define O_NONBLOCK 0
87 #endif
88
89     /*
90      * Since we aren't supposed to succeed when we're opening for writing and
91      * there's another writer, we can't just do an open() with O_WRONLY.
92      */
93
94     switch (how) {
95       case OPENFILE_APPEND:
96         oflags = O_NONBLOCK | O_WRONLY | O_NOCTTY | O_APPEND; 
97         for_writing = 1;
98         flags |= FILEOBJ_WRITE;
99         break;
100       case OPENFILE_WRITE:
101         oflags = O_NONBLOCK | O_WRONLY | O_NOCTTY;
102         flags |= FILEOBJ_WRITE;
103         for_writing = 1;
104         break;
105     case OPENFILE_READ_ONLY:
106         oflags = O_NONBLOCK | O_RDONLY | O_NOCTTY;
107         flags |= FILEOBJ_READ;
108         for_writing = 0;
109         break;
110     case OPENFILE_READ_WRITE:
111         oflags = O_NONBLOCK | O_RDWR | O_NOCTTY;
112         flags |= FILEOBJ_READ | FILEOBJ_WRITE;
113         for_writing = 1;
114         break;
115     default:
116         fprintf(stderr, "openFile: unknown mode `%d'\n", how);
117         exit(EXIT_FAILURE);
118     }
119
120 #if HAVE_O_BINARY
121     if (binary) {
122       oflags |= O_BINARY;
123       flags  |= FILEOBJ_BINARY;
124     }
125 #endif
126
127     /* First try to open without creating */
128     while ((fd = open(file, oflags, 0666)) < 0) {
129         if (errno == ENOENT) {
130             if ( how == OPENFILE_READ_ONLY ) {
131                 /* For ReadMode, just bail out now */
132                 ghc_errtype = ERR_NOSUCHTHING;
133                 ghc_errstr = "file does not exist";
134                 return NULL;
135             } else {
136                 /* If it is a dangling symlink, break off now, too. */
137 #ifndef mingw32_TARGET_OS
138                 struct stat st;
139                 if ( lstat(file,&st) == 0) {
140                    ghc_errtype = ERR_NOSUCHTHING;
141                    ghc_errstr = "dangling symlink";
142                    return NULL;
143                 }
144 #endif
145             }
146             /* Now try to create it */
147             while ((fd = open(file, oflags | O_CREAT | O_EXCL, 0666)) < 0) {
148                 if (errno == EEXIST) {
149                     /* Race detected; go back and open without creating it */
150                     break;
151                 } else if (errno != EINTR) {
152                     cvtErrno();
153                     switch (ghc_errno) {
154                     default:
155                         stdErrno();
156                         break;
157                     case GHC_ENOENT:
158                     case GHC_ENOTDIR:
159                         ghc_errtype = ERR_NOSUCHTHING;
160                         ghc_errstr = "no path to file";
161                         break;
162                     case GHC_EINVAL:
163                         ghc_errtype = ERR_PERMISSIONDENIED;
164                         ghc_errstr = "unsupported owner or group";
165                         break;
166                     }
167                     return NULL;
168                 }
169             }
170             if (fd >= 0) {
171                 created = 1;
172                 break;
173             }
174         } else if (errno != EINTR) {
175             cvtErrno();
176             switch (ghc_errno) {
177             default:
178                 stdErrno();
179                 break;
180             case GHC_ENOTDIR:
181                 ghc_errtype = ERR_NOSUCHTHING;
182                 ghc_errstr = "no path to file";
183                 break;
184             case GHC_EINVAL:
185                 ghc_errtype = ERR_PERMISSIONDENIED;
186                 ghc_errstr = "unsupported owner or group";
187                 break;
188             }
189             return NULL;
190         }
191     }
192
193     /* Make sure that we aren't looking at a directory */
194
195     while (fstat(fd, &sb) < 0) {
196         /* highly unlikely */
197         if (errno != EINTR) {
198             cvtErrno();
199             if (created)
200                 (void) unlink(file);
201             (void) close(fd);
202             return NULL;
203         }
204     }
205     if (S_ISDIR(sb.st_mode)) {
206         ghc_errtype = ERR_INAPPROPRIATETYPE;
207         ghc_errstr = "file is a directory";
208         /* We can't have created it in this case. */
209         (void) close(fd);
210
211         return NULL;
212     }
213     /* Use our own personal locking */
214
215     if (lockFile(fd, for_writing, 1/*enforce single-writer, if needs be.*/) < 0) {
216         cvtErrno();
217         switch (ghc_errno) {
218         default:
219             stdErrno();
220             break;
221         case GHC_EACCES:
222         case GHC_EAGAIN:
223             ghc_errtype = ERR_RESOURCEBUSY;
224             ghc_errstr = "file is locked";
225             break;
226         }
227         if (created)
228             (void) unlink(file);
229         (void) close(fd);
230         return NULL;
231     }
232
233     /*
234      * Write mode is supposed to truncate the file.  Unfortunately, our pal
235      * ftruncate() is non-POSIX, so we truncate with a second open, which may fail.
236      */
237
238     if ( how == OPENFILE_WRITE ) {
239         int fd2, oflags2;
240
241         oflags2 = oflags | O_TRUNC;
242         while ((fd2 = open(file, oflags2, 0666)) < 0) {
243             if (errno != EINTR) {
244                 cvtErrno();
245                 if (created)
246                     (void) unlink(file);
247                 (void) close(fd);
248                 switch (ghc_errno) {
249                 default:
250                     stdErrno();
251                     break;
252                 case GHC_EAGAIN:
253                     ghc_errtype = ERR_RESOURCEBUSY;
254                     ghc_errstr = "enforced lock prevents truncation";
255                     break;
256                 case GHC_ENOTDIR:
257                     ghc_errtype = ERR_NOSUCHTHING;
258                     ghc_errstr = "no path to file";
259                     break;
260                 case GHC_EINVAL:
261                     ghc_errtype = ERR_PERMISSIONDENIED;
262                     ghc_errstr = "unsupported owner or group";
263                     break;
264                 }
265                 return NULL;
266             }
267         }
268         close(fd2);
269     }
270
271     /* Allocate a IOFileObject to hold the information
272        we need to record per-handle for the various C stubs.
273        This chunk of memory is wrapped up inside a foreign object,
274        so it will be finalised and freed properly when we're
275        through with the handle.
276     */
277     if ((fo = malloc(sizeof(IOFileObject))) == NULL)
278        return NULL;
279
280     fo->fd       = fd;
281     fo->buf      = NULL;
282     fo->bufStart = 0;
283     fo->bufWPtr  = 0;
284     fo->bufRPtr  = 0;
285     fo->flags    = flags;
286     fo->connectedTo = NULL;
287     return fo;
288 }
289
290 /* `Lock' file descriptor and return file object. */
291 IOFileObject*
292 openFd(StgInt fd,StgInt oflags,StgInt flags)
293 {
294     int for_writing;
295     FILE* fp;
296     IOFileObject* fo;
297
298     for_writing = ( ((oflags & O_WRONLY) || (oflags & O_RDWR)) ? 1 : 0);
299
300     if (lockFile(fd, for_writing, 1/* enforce single-writer */ ) < 0) {
301         cvtErrno();
302         switch (ghc_errno) {
303         default:
304             stdErrno();
305             break;
306         case GHC_EACCES:
307         case GHC_EAGAIN:
308             ghc_errtype = ERR_RESOURCEBUSY;
309             ghc_errstr = "file is locked";
310             break;
311         }
312         return NULL;
313     }
314
315     /* See openFileObject() comment */
316     if ((fo = malloc(sizeof(IOFileObject))) == NULL)
317        return NULL;
318     fo->fd       = fd;
319     fo->buf      = NULL;
320     fo->bufStart = 0;
321     fo->bufWPtr  = 0;
322     fo->bufRPtr  = 0;
323     fo->flags    = flags | ( oflags & O_RDONLY ? FILEOBJ_READ 
324                           : oflags & O_RDWR   ? FILEOBJ_READ 
325                           : 0)
326                         | ( oflags & O_WRONLY ? FILEOBJ_WRITE
327                           : oflags & O_RDWR   ? FILEOBJ_WRITE 
328                           : 0);
329     fo->connectedTo = NULL;
330     return fo;
331 }