blob: 264542a36a7dd05173b6de1ce2cd1af0d5900510
1 | /* |
2 | * buffered file I/O |
3 | * Copyright (c) 2001 Fabrice Bellard |
4 | * |
5 | * This file is part of FFmpeg. |
6 | * |
7 | * FFmpeg is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * |
12 | * FFmpeg is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with FFmpeg; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | #include "libavutil/avstring.h" |
23 | #include "libavutil/internal.h" |
24 | #include "libavutil/opt.h" |
25 | #include "avformat.h" |
26 | #if HAVE_DIRENT_H |
27 | #include <dirent.h> |
28 | #endif |
29 | #include <fcntl.h> |
30 | #if HAVE_IO_H |
31 | #include <io.h> |
32 | #endif |
33 | #if HAVE_UNISTD_H |
34 | #include <unistd.h> |
35 | #endif |
36 | #include <sys/stat.h> |
37 | #include <stdlib.h> |
38 | #include "os_support.h" |
39 | #include "url.h" |
40 | |
41 | /* Some systems may not have S_ISFIFO */ |
42 | #ifndef S_ISFIFO |
43 | # ifdef S_IFIFO |
44 | # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) |
45 | # else |
46 | # define S_ISFIFO(m) 0 |
47 | # endif |
48 | #endif |
49 | |
50 | /* Not available in POSIX.1-1996 */ |
51 | #ifndef S_ISLNK |
52 | # ifdef S_IFLNK |
53 | # define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK) |
54 | # else |
55 | # define S_ISLNK(m) 0 |
56 | # endif |
57 | #endif |
58 | |
59 | /* Not available in POSIX.1-1996 */ |
60 | #ifndef S_ISSOCK |
61 | # ifdef S_IFSOCK |
62 | # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) |
63 | # else |
64 | # define S_ISSOCK(m) 0 |
65 | # endif |
66 | #endif |
67 | |
68 | /* standard file protocol */ |
69 | |
70 | typedef struct FileContext { |
71 | const AVClass *class; |
72 | int fd; |
73 | int trunc; |
74 | int blocksize; |
75 | int follow; |
76 | #if HAVE_DIRENT_H |
77 | DIR *dir; |
78 | #endif |
79 | } FileContext; |
80 | |
81 | static const AVOption file_options[] = { |
82 | { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, |
83 | { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, |
84 | { "follow", "Follow a file as it is being written", offsetof(FileContext, follow), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, |
85 | { NULL } |
86 | }; |
87 | |
88 | static const AVOption pipe_options[] = { |
89 | { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, |
90 | { NULL } |
91 | }; |
92 | |
93 | static const AVClass file_class = { |
94 | .class_name = "file", |
95 | .item_name = av_default_item_name, |
96 | .option = file_options, |
97 | .version = LIBAVUTIL_VERSION_INT, |
98 | }; |
99 | |
100 | static const AVClass pipe_class = { |
101 | .class_name = "pipe", |
102 | .item_name = av_default_item_name, |
103 | .option = pipe_options, |
104 | .version = LIBAVUTIL_VERSION_INT, |
105 | }; |
106 | |
107 | static int file_read(URLContext *h, unsigned char *buf, int size) |
108 | { |
109 | FileContext *c = h->priv_data; |
110 | int ret; |
111 | size = FFMIN(size, c->blocksize); |
112 | ret = read(c->fd, buf, size); |
113 | if (ret == 0 && c->follow) |
114 | return AVERROR(EAGAIN); |
115 | return (ret == -1) ? AVERROR(errno) : ret; |
116 | } |
117 | |
118 | static int file_write(URLContext *h, const unsigned char *buf, int size) |
119 | { |
120 | FileContext *c = h->priv_data; |
121 | int ret; |
122 | size = FFMIN(size, c->blocksize); |
123 | ret = write(c->fd, buf, size); |
124 | return (ret == -1) ? AVERROR(errno) : ret; |
125 | } |
126 | |
127 | static int file_get_handle(URLContext *h) |
128 | { |
129 | FileContext *c = h->priv_data; |
130 | return c->fd; |
131 | } |
132 | |
133 | static int file_check(URLContext *h, int mask) |
134 | { |
135 | int ret = 0; |
136 | const char *filename = h->filename; |
137 | av_strstart(filename, "file:", &filename); |
138 | |
139 | { |
140 | #if HAVE_ACCESS && defined(R_OK) |
141 | if (access(filename, F_OK) < 0) |
142 | return AVERROR(errno); |
143 | if (mask&AVIO_FLAG_READ) |
144 | if (access(filename, R_OK) >= 0) |
145 | ret |= AVIO_FLAG_READ; |
146 | if (mask&AVIO_FLAG_WRITE) |
147 | if (access(filename, W_OK) >= 0) |
148 | ret |= AVIO_FLAG_WRITE; |
149 | #else |
150 | struct stat st; |
151 | # ifndef _WIN32 |
152 | ret = stat(filename, &st); |
153 | # else |
154 | ret = win32_stat(filename, &st); |
155 | # endif |
156 | if (ret < 0) |
157 | return AVERROR(errno); |
158 | |
159 | ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0; |
160 | ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0; |
161 | #endif |
162 | } |
163 | return ret; |
164 | } |
165 | |
166 | static int file_delete(URLContext *h) |
167 | { |
168 | #if HAVE_UNISTD_H |
169 | int ret; |
170 | const char *filename = h->filename; |
171 | av_strstart(filename, "file:", &filename); |
172 | |
173 | ret = rmdir(filename); |
174 | if (ret < 0 && errno == ENOTDIR) |
175 | ret = unlink(filename); |
176 | if (ret < 0) |
177 | return AVERROR(errno); |
178 | |
179 | return ret; |
180 | #else |
181 | return AVERROR(ENOSYS); |
182 | #endif /* HAVE_UNISTD_H */ |
183 | } |
184 | |
185 | static int file_move(URLContext *h_src, URLContext *h_dst) |
186 | { |
187 | const char *filename_src = h_src->filename; |
188 | const char *filename_dst = h_dst->filename; |
189 | av_strstart(filename_src, "file:", &filename_src); |
190 | av_strstart(filename_dst, "file:", &filename_dst); |
191 | |
192 | if (rename(filename_src, filename_dst) < 0) |
193 | return AVERROR(errno); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | #if CONFIG_FILE_PROTOCOL |
199 | |
200 | static int file_open(URLContext *h, const char *filename, int flags) |
201 | { |
202 | FileContext *c = h->priv_data; |
203 | int access; |
204 | int fd; |
205 | struct stat st; |
206 | |
207 | av_strstart(filename, "file:", &filename); |
208 | |
209 | if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { |
210 | access = O_CREAT | O_RDWR; |
211 | if (c->trunc) |
212 | access |= O_TRUNC; |
213 | } else if (flags & AVIO_FLAG_WRITE) { |
214 | access = O_CREAT | O_WRONLY; |
215 | if (c->trunc) |
216 | access |= O_TRUNC; |
217 | } else { |
218 | access = O_RDONLY; |
219 | } |
220 | #ifdef O_BINARY |
221 | access |= O_BINARY; |
222 | #endif |
223 | fd = avpriv_open(filename, access, 0666); |
224 | if (fd == -1) |
225 | return AVERROR(errno); |
226 | c->fd = fd; |
227 | |
228 | h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode); |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | /* XXX: use llseek */ |
234 | static int64_t file_seek(URLContext *h, int64_t pos, int whence) |
235 | { |
236 | FileContext *c = h->priv_data; |
237 | int64_t ret; |
238 | |
239 | if (whence == AVSEEK_SIZE) { |
240 | struct stat st; |
241 | ret = fstat(c->fd, &st); |
242 | return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size); |
243 | } |
244 | |
245 | ret = lseek(c->fd, pos, whence); |
246 | |
247 | return ret < 0 ? AVERROR(errno) : ret; |
248 | } |
249 | |
250 | static int file_close(URLContext *h) |
251 | { |
252 | FileContext *c = h->priv_data; |
253 | return close(c->fd); |
254 | } |
255 | |
256 | static int file_open_dir(URLContext *h) |
257 | { |
258 | #if HAVE_LSTAT |
259 | FileContext *c = h->priv_data; |
260 | |
261 | c->dir = opendir(h->filename); |
262 | if (!c->dir) |
263 | return AVERROR(errno); |
264 | |
265 | return 0; |
266 | #else |
267 | return AVERROR(ENOSYS); |
268 | #endif /* HAVE_LSTAT */ |
269 | } |
270 | |
271 | static int file_read_dir(URLContext *h, AVIODirEntry **next) |
272 | { |
273 | #if HAVE_LSTAT |
274 | FileContext *c = h->priv_data; |
275 | struct dirent *dir; |
276 | char *fullpath = NULL; |
277 | |
278 | *next = ff_alloc_dir_entry(); |
279 | if (!*next) |
280 | return AVERROR(ENOMEM); |
281 | do { |
282 | errno = 0; |
283 | dir = readdir(c->dir); |
284 | if (!dir) { |
285 | av_freep(next); |
286 | return AVERROR(errno); |
287 | } |
288 | } while (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")); |
289 | |
290 | fullpath = av_append_path_component(h->filename, dir->d_name); |
291 | if (fullpath) { |
292 | struct stat st; |
293 | if (!lstat(fullpath, &st)) { |
294 | if (S_ISDIR(st.st_mode)) |
295 | (*next)->type = AVIO_ENTRY_DIRECTORY; |
296 | else if (S_ISFIFO(st.st_mode)) |
297 | (*next)->type = AVIO_ENTRY_NAMED_PIPE; |
298 | else if (S_ISCHR(st.st_mode)) |
299 | (*next)->type = AVIO_ENTRY_CHARACTER_DEVICE; |
300 | else if (S_ISBLK(st.st_mode)) |
301 | (*next)->type = AVIO_ENTRY_BLOCK_DEVICE; |
302 | else if (S_ISLNK(st.st_mode)) |
303 | (*next)->type = AVIO_ENTRY_SYMBOLIC_LINK; |
304 | else if (S_ISSOCK(st.st_mode)) |
305 | (*next)->type = AVIO_ENTRY_SOCKET; |
306 | else if (S_ISREG(st.st_mode)) |
307 | (*next)->type = AVIO_ENTRY_FILE; |
308 | else |
309 | (*next)->type = AVIO_ENTRY_UNKNOWN; |
310 | |
311 | (*next)->group_id = st.st_gid; |
312 | (*next)->user_id = st.st_uid; |
313 | (*next)->size = st.st_size; |
314 | (*next)->filemode = st.st_mode & 0777; |
315 | (*next)->modification_timestamp = INT64_C(1000000) * st.st_mtime; |
316 | (*next)->access_timestamp = INT64_C(1000000) * st.st_atime; |
317 | (*next)->status_change_timestamp = INT64_C(1000000) * st.st_ctime; |
318 | } |
319 | av_free(fullpath); |
320 | } |
321 | |
322 | (*next)->name = av_strdup(dir->d_name); |
323 | return 0; |
324 | #else |
325 | return AVERROR(ENOSYS); |
326 | #endif /* HAVE_LSTAT */ |
327 | } |
328 | |
329 | static int file_close_dir(URLContext *h) |
330 | { |
331 | #if HAVE_LSTAT |
332 | FileContext *c = h->priv_data; |
333 | closedir(c->dir); |
334 | return 0; |
335 | #else |
336 | return AVERROR(ENOSYS); |
337 | #endif /* HAVE_LSTAT */ |
338 | } |
339 | |
340 | const URLProtocol ff_file_protocol = { |
341 | .name = "file", |
342 | .url_open = file_open, |
343 | .url_read = file_read, |
344 | .url_write = file_write, |
345 | .url_seek = file_seek, |
346 | .url_close = file_close, |
347 | .url_get_file_handle = file_get_handle, |
348 | .url_check = file_check, |
349 | .url_delete = file_delete, |
350 | .url_move = file_move, |
351 | .priv_data_size = sizeof(FileContext), |
352 | .priv_data_class = &file_class, |
353 | .url_open_dir = file_open_dir, |
354 | .url_read_dir = file_read_dir, |
355 | .url_close_dir = file_close_dir, |
356 | .default_whitelist = "file,crypto" |
357 | }; |
358 | |
359 | #endif /* CONFIG_FILE_PROTOCOL */ |
360 | |
361 | #if CONFIG_PIPE_PROTOCOL |
362 | |
363 | static int pipe_open(URLContext *h, const char *filename, int flags) |
364 | { |
365 | FileContext *c = h->priv_data; |
366 | int fd; |
367 | char *final; |
368 | av_strstart(filename, "pipe:", &filename); |
369 | |
370 | fd = strtol(filename, &final, 10); |
371 | if((filename == final) || *final ) {/* No digits found, or something like 10ab */ |
372 | if (flags & AVIO_FLAG_WRITE) { |
373 | fd = 1; |
374 | } else { |
375 | fd = 0; |
376 | } |
377 | } |
378 | #if HAVE_SETMODE |
379 | setmode(fd, O_BINARY); |
380 | #endif |
381 | c->fd = fd; |
382 | h->is_streamed = 1; |
383 | return 0; |
384 | } |
385 | |
386 | const URLProtocol ff_pipe_protocol = { |
387 | .name = "pipe", |
388 | .url_open = pipe_open, |
389 | .url_read = file_read, |
390 | .url_write = file_write, |
391 | .url_get_file_handle = file_get_handle, |
392 | .url_check = file_check, |
393 | .priv_data_size = sizeof(FileContext), |
394 | .priv_data_class = &pipe_class, |
395 | .default_whitelist = "crypto" |
396 | }; |
397 | |
398 | #endif /* CONFIG_PIPE_PROTOCOL */ |
399 |