nixio: Fix accidental closing of file descriptors after dup with two
[project/luci.git] / libs / nixio / src / file.c
1 /*
2 * nixio - Linux I/O library for lua
3 *
4 * Copyright (C) 2009 Steven Barth <steven@midlink.org>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include "nixio.h"
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/param.h>
29
30
31 static int nixio_open(lua_State *L) {
32 const char *filename = luaL_checklstring(L, 1, NULL);
33 int flags;
34
35 if (lua_isnoneornil(L, 2)) {
36 flags = O_RDONLY;
37 } else if (lua_isnumber(L, 2)) {
38 flags = lua_tointeger(L, 2);
39 } else if (lua_isstring(L, 2)) {
40 const char *str = lua_tostring(L, 2);
41 if (!strcmp(str, "r")) {
42 flags = O_RDONLY;
43 } else if (!strcmp(str, "r+")) {
44 flags = O_RDWR;
45 } else if (!strcmp(str, "w")) {
46 flags = O_WRONLY | O_CREAT | O_TRUNC;
47 } else if (!strcmp(str, "w+")) {
48 flags = O_RDWR | O_CREAT | O_TRUNC;
49 } else if (!strcmp(str, "a")) {
50 flags = O_WRONLY | O_CREAT | O_APPEND;
51 } else if (!strcmp(str, "a+")) {
52 flags = O_RDWR | O_CREAT | O_APPEND;
53 } else {
54 return luaL_argerror(L, 2, "supported values: r, r+, w, w+, a, a+");
55 }
56 } else {
57 return luaL_argerror(L, 2, "open flags or string expected");
58 }
59
60 int fd;
61
62 do {
63 fd = open(filename, flags, nixio__check_mode(L, 3, 0666));
64 } while (fd == -1 && errno == EINTR);
65 if (fd == -1) {
66 return nixio__perror(L);
67 }
68
69 int *udata = lua_newuserdata(L, sizeof(int));
70 if (!udata) {
71 return luaL_error(L, "out of memory");
72 }
73
74 *udata = fd;
75
76 luaL_getmetatable(L, NIXIO_FILE_META);
77 lua_setmetatable(L, -2);
78
79 return 1;
80 }
81
82 static int nixio_open_flags(lua_State *L) {
83 int mode = 0;
84 const int j = lua_gettop(L);
85 for (int i=1; i<=j; i++) {
86 const char *flag = luaL_checkstring(L, i);
87 if (!strcmp(flag, "append")) {
88 mode |= O_APPEND;
89 } else if (!strcmp(flag, "creat")) {
90 mode |= O_CREAT;
91 } else if (!strcmp(flag, "excl")) {
92 mode |= O_EXCL;
93 } else if (!strcmp(flag, "nonblock") || !strcmp(flag, "ndelay")) {
94 #ifndef __WINNT__
95 mode |= O_NONBLOCK;
96 #endif
97 } else if (!strcmp(flag, "sync")) {
98 #ifndef __WINNT__
99 mode |= O_SYNC;
100 #endif
101 } else if (!strcmp(flag, "trunc")) {
102 mode |= O_TRUNC;
103 } else if (!strcmp(flag, "rdonly")) {
104 mode |= O_RDONLY;
105 } else if (!strcmp(flag, "wronly")) {
106 mode |= O_WRONLY;
107 } else if (!strcmp(flag, "rdwr")) {
108 mode |= O_RDWR;
109 } else {
110 return luaL_argerror(L, i, "supported values: append, creat, "
111 "excl, nonblock, ndelay, sync, trunc");
112 }
113 }
114 lua_pushinteger(L, mode);
115 return 1;
116 }
117
118 static int nixio_dup(lua_State *L) {
119 int oldfd = nixio__checkfd(L, 1);
120 int newfd = (lua_gettop(L) > 1) ? nixio__checkfd(L, 2) : -1;
121 int stat = (newfd == -1) ? dup(oldfd) : dup2(oldfd, newfd);
122
123 if (stat == -1) {
124 return nixio__perror(L);
125 } else {
126 if (newfd == -1) {
127 int *udata = lua_newuserdata(L, sizeof(int));
128 if (!udata) {
129 return luaL_error(L, "out of memory");
130 }
131
132 *udata = stat;
133 luaL_getmetatable(L, NIXIO_FILE_META);
134 lua_setmetatable(L, -2);
135 } else {
136 lua_pushvalue(L, 2);
137 }
138 return 1;
139 }
140 }
141
142 static int nixio_pipe(lua_State *L) {
143 int pipefd[2], *udata;
144 if (pipe(pipefd)) {
145 return nixio__perror(L);
146 }
147
148 luaL_getmetatable(L, NIXIO_FILE_META);
149 udata = lua_newuserdata(L, sizeof(int));
150 if (!udata) {
151 return luaL_error(L, "out of memory");
152 }
153
154 *udata = pipefd[0];
155 lua_pushvalue(L, -2);
156 lua_setmetatable(L, -2);
157
158
159 udata = lua_newuserdata(L, sizeof(int));
160 if (!udata) {
161 return luaL_error(L, "out of memory");
162 }
163
164 *udata = pipefd[1];
165 lua_pushvalue(L, -3);
166 lua_setmetatable(L, -2);
167
168 return 2;
169 }
170
171 static int nixio_file_write(lua_State *L) {
172 int fd = nixio__checkfd(L, 1);
173 size_t len;
174 ssize_t sent;
175 const char *data = luaL_checklstring(L, 2, &len);
176
177 if (lua_gettop(L) > 2) {
178 int offset = luaL_optint(L, 3, 0);
179 if (offset) {
180 if (offset < len) {
181 data += offset;
182 len -= offset;
183 } else {
184 len = 0;
185 }
186 }
187
188 unsigned int wlen = luaL_optint(L, 4, len);
189 if (wlen < len) {
190 len = wlen;
191 }
192 }
193
194 do {
195 sent = write(fd, data, len);
196 } while(sent == -1 && errno == EINTR);
197 if (sent >= 0) {
198 lua_pushinteger(L, sent);
199 return 1;
200 } else {
201 return nixio__perror(L);
202 }
203 }
204
205 static int nixio_file_read(lua_State *L) {
206 int fd = nixio__checkfd(L, 1);
207 char buffer[NIXIO_BUFFERSIZE];
208 uint req = luaL_checkinteger(L, 2);
209 int readc;
210
211 /* We limit the readsize to NIXIO_BUFFERSIZE */
212 req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req;
213
214 do {
215 readc = read(fd, buffer, req);
216 } while (readc == -1 && errno == EINTR);
217
218 if (readc < 0) {
219 return nixio__perror(L);
220 } else {
221 lua_pushlstring(L, buffer, readc);
222 return 1;
223 }
224 }
225
226
227 static int nixio_file_seek(lua_State *L) {
228 int fd = nixio__checkfd(L, 1);
229 off_t len = (off_t)luaL_checknumber(L, 2);
230 int whence;
231 const char *whstr = luaL_optlstring(L, 3, "set", NULL);
232 if (!strcmp(whstr, "set")) {
233 whence = SEEK_SET;
234 } else if (!strcmp(whstr, "cur")) {
235 whence = SEEK_CUR;
236 } else if (!strcmp(whstr, "end")) {
237 whence = SEEK_END;
238 } else {
239 return luaL_argerror(L, 3, "supported values: set, cur, end");
240 }
241 len = lseek(fd, len, whence);
242 if (len == -1) {
243 return nixio__perror(L);
244 } else {
245 lua_pushnumber(L, len);
246 return 1;
247 }
248 }
249
250 static int nixio_file_tell(lua_State *L) {
251 int fd = nixio__checkfd(L, 1);
252 off_t pos = lseek(fd, 0, SEEK_CUR);
253 if (pos < 0) {
254 return nixio__perror(L);
255 } else {
256 lua_pushnumber(L, pos);
257 return 1;
258 }
259 }
260
261 static int nixio_file_stat(lua_State *L) {
262 nixio_stat_t buf;
263 if (fstat(nixio__checkfd(L, 1), &buf)) {
264 return nixio__perror(L);
265 } else {
266 nixio__push_stat(L, &buf);
267 if (lua_isstring(L, 2)) {
268 lua_getfield(L, -1, lua_tostring(L, 2));
269 }
270 return 1;
271 }
272 }
273
274 static int nixio_file_sync(lua_State *L) {
275 int fd = nixio__checkfd(L, 1);
276 int stat;
277 #if (!defined BSD && !defined __WINNT__)
278 int dataonly = lua_toboolean(L, 2);
279 do {
280 stat = (dataonly) ? fdatasync(fd) : fsync(fd);
281 } while (stat == -1 && errno == EINTR);
282 return nixio__pstatus(L, !stat);
283 #else
284 do {
285 stat = fsync(fd);
286 } while (stat == -1 && errno == EINTR);
287 return nixio__pstatus(L, !stat);
288 #endif
289 }
290
291 static int nixio_file_lock(lua_State *L) {
292 int fd = nixio__checkfd(L, 1);
293 const char *flag = luaL_checkstring(L, 2);
294 off_t len = (off_t)luaL_optnumber(L, 3, 0);
295 int stat;
296
297 int cmd = 0;
298 if (!strcmp(flag, "lock")) {
299 cmd = F_LOCK;
300 } else if (!strcmp(flag, "tlock")) {
301 cmd = F_TLOCK;
302 } else if (!strcmp(flag, "ulock")) {
303 cmd = F_ULOCK;
304 } else if (!strcmp(flag, "test")) {
305 cmd = F_TEST;
306 } else {
307 return luaL_argerror(L, 2,
308 "supported values: lock, tlock, ulock, test");
309 }
310
311 do {
312 stat = lockf(fd, cmd, len);
313 } while (stat == -1 && errno == EINTR);
314
315 return nixio__pstatus(L, !stat);
316 }
317
318 static int nixio_file_close(lua_State *L) {
319 int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
320 luaL_argcheck(L, *fdp != -1, 1, "invalid file object");
321 int res;
322 do {
323 res = close(*fdp);
324 } while (res == -1 && errno == EINTR);
325 *fdp = -1;
326 return nixio__pstatus(L, !res);
327 }
328
329 static int nixio_file__gc(lua_State *L) {
330 int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
331 int res;
332 if (*fdp != -1) {
333 do {
334 res = close(*fdp);
335 } while (res == -1 && errno == EINTR);
336 *fdp = -1;
337 }
338 return 0;
339 }
340
341 /**
342 * string representation
343 */
344 static int nixio_file__tostring(lua_State *L) {
345 lua_pushfstring(L, "nixio file %d", nixio__tofd(L, 1));
346 return 1;
347 }
348
349 /* method table */
350 static const luaL_reg M[] = {
351 {"write", nixio_file_write},
352 {"read", nixio_file_read},
353 {"tell", nixio_file_tell},
354 {"seek", nixio_file_seek},
355 {"stat", nixio_file_stat},
356 {"sync", nixio_file_sync},
357 {"lock", nixio_file_lock},
358 {"close", nixio_file_close},
359 {"__gc", nixio_file__gc},
360 {"__tostring", nixio_file__tostring},
361 {NULL, NULL}
362 };
363
364 /* module table */
365 static const luaL_reg R[] = {
366 {"dup", nixio_dup},
367 {"open", nixio_open},
368 {"open_flags", nixio_open_flags},
369 {"pipe", nixio_pipe},
370 {NULL, NULL}
371 };
372
373 void nixio_open_file(lua_State *L) {
374 luaL_register(L, NULL, R);
375
376 luaL_newmetatable(L, NIXIO_FILE_META);
377 luaL_register(L, NULL, M);
378 lua_pushvalue(L, -1);
379 lua_setfield(L, -2, "__index");
380
381 int *uin = lua_newuserdata(L, sizeof(int));
382 int *uout = lua_newuserdata(L, sizeof(int));
383 int *uerr = lua_newuserdata(L, sizeof(int));
384
385 if (!uin || !uout || !uerr) {
386 luaL_error(L, "out of memory");
387 }
388
389 *uin = STDIN_FILENO;
390 *uout = STDOUT_FILENO;
391 *uerr = STDERR_FILENO;
392
393 for (int i = -4; i < -1; i++) {
394 lua_pushvalue(L, -4);
395 lua_setmetatable(L, i);
396 }
397
398 lua_setfield(L, -5, "stderr");
399 lua_setfield(L, -4, "stdout");
400 lua_setfield(L, -3, "stdin");
401 lua_setfield(L, -2, "meta_file");
402 }