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