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