/src/vlc/modules/access/file.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * file.c: file input (file: access plug-in) |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001-2006 VLC authors and VideoLAN |
5 | | * Copyright © 2006-2007 Rémi Denis-Courmont |
6 | | * |
7 | | * Authors: Christophe Massiot <massiot@via.ecp.fr> |
8 | | * Rémi Denis-Courmont |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2.1 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with this program; if not, write to the Free Software Foundation, |
22 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | #ifdef HAVE_CONFIG_H |
26 | | # include "config.h" |
27 | | #endif |
28 | | |
29 | | #include <assert.h> |
30 | | #include <errno.h> |
31 | | #include <limits.h> |
32 | | #include <sys/types.h> |
33 | | #include <sys/stat.h> |
34 | | #include <fcntl.h> |
35 | | #ifdef HAVE_FSTATVFS |
36 | | # include <sys/statvfs.h> |
37 | | # if defined (HAVE_SYS_MOUNT_H) |
38 | | # include <sys/param.h> |
39 | | # include <sys/mount.h> |
40 | | # endif |
41 | | #endif |
42 | | #ifdef HAVE_LINUX_MAGIC_H |
43 | | # include <sys/vfs.h> |
44 | | # include <linux/magic.h> |
45 | | #endif |
46 | | |
47 | | #if defined( _WIN32 ) |
48 | | # include <io.h> |
49 | | # include <ctype.h> |
50 | | #else |
51 | | # include <unistd.h> |
52 | | #endif |
53 | | |
54 | | #include <vlc_common.h> |
55 | | #include "fs.h" |
56 | | #include <vlc_access.h> |
57 | | #include <vlc_interrupt.h> |
58 | | #ifdef _WIN32 |
59 | | # include <vlc_charset.h> |
60 | | #endif |
61 | | #include <vlc_fs.h> |
62 | | #include <vlc_url.h> |
63 | | |
64 | | typedef struct |
65 | | { |
66 | | int fd; |
67 | | |
68 | | bool b_pace_control; |
69 | | } access_sys_t; |
70 | | |
71 | | #if !defined (_WIN32) && !defined (__OS2__) |
72 | | static bool IsRemote (int fd) |
73 | 0 | { |
74 | | #if defined(__APPLE__) |
75 | | /* This has to preceed the general fstatvfs implmentation below, |
76 | | * as even though Darwin has fstatvfs, it does not expose the |
77 | | * MNT_LOCAL in the statvfs.f_flag field. |
78 | | */ |
79 | | struct statfs sfs; |
80 | | |
81 | | if (fstatfs (fd, &sfs)) |
82 | | return false; |
83 | | |
84 | | return !((sfs.f_flags & MNT_LOCAL) == MNT_LOCAL); |
85 | | |
86 | | #elif defined (HAVE_FSTATVFS) && defined (MNT_LOCAL) |
87 | | struct statvfs stf; |
88 | | |
89 | | if (fstatvfs (fd, &stf)) |
90 | | return false; |
91 | | /* fstatvfs() is in POSIX, but MNT_LOCAL is not */ |
92 | | return !(stf.f_flag & MNT_LOCAL); |
93 | | |
94 | | #elif defined (HAVE_LINUX_MAGIC_H) |
95 | | struct statfs stf; |
96 | |
|
97 | 0 | if (fstatfs (fd, &stf)) |
98 | 0 | return false; |
99 | | |
100 | 0 | switch ((unsigned long)stf.f_type) |
101 | 0 | { |
102 | 0 | case AFS_SUPER_MAGIC: |
103 | 0 | case CODA_SUPER_MAGIC: |
104 | 0 | case NCP_SUPER_MAGIC: |
105 | 0 | case NFS_SUPER_MAGIC: |
106 | 0 | case SMB_SUPER_MAGIC: |
107 | 0 | case 0xFF534D42 /*CIFS_MAGIC_NUMBER*/: |
108 | 0 | return true; |
109 | 0 | } |
110 | 0 | return false; |
111 | |
|
112 | | #else |
113 | | (void)fd; |
114 | | return false; |
115 | | |
116 | | #endif |
117 | 0 | } |
118 | 0 | # define IsRemote(fd,path) IsRemote(fd) |
119 | | |
120 | | #elif defined(_WIN32) |
121 | | |
122 | | static bool IsRemote(int fd, const char *path) |
123 | | { |
124 | | VLC_UNUSED(fd); |
125 | | |
126 | | size_t len = strlen(path); |
127 | | if (len < 2) |
128 | | return false; |
129 | | if (path[0] == '\\' && path[1] == '\\') |
130 | | return true; |
131 | | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) || NTDDI_VERSION >= NTDDI_WIN10_RS3 |
132 | | if (path[1] == ':') |
133 | | { |
134 | | char drive[4]; |
135 | | drive[0] = path[0]; // can only be < 0x80 if second char is ':' |
136 | | drive[1] = ':'; |
137 | | drive[2] = '\\'; |
138 | | drive[3] = '\0'; |
139 | | UINT driveType = GetDriveTypeA(drive); |
140 | | switch (driveType) |
141 | | { |
142 | | case DRIVE_FIXED: |
143 | | case DRIVE_REMOVABLE: // but a floppy drive is slower than network |
144 | | case DRIVE_RAMDISK: |
145 | | case DRIVE_CDROM: |
146 | | return false; |
147 | | default: |
148 | | return true; |
149 | | } |
150 | | } |
151 | | #endif |
152 | | return false; |
153 | | } |
154 | | |
155 | | #else /* __OS2__ */ |
156 | | |
157 | | static bool IsRemote (const char *path) |
158 | | { |
159 | | return (! strncmp(path, "\\\\", 2)); |
160 | | } |
161 | | # define IsRemote(fd,path) IsRemote(path) |
162 | | #endif |
163 | | |
164 | | #ifndef HAVE_POSIX_FADVISE |
165 | | # define posix_fadvise(fd, off, len, adv) |
166 | | #endif |
167 | | |
168 | | static ssize_t Read (stream_t *, void *, size_t); |
169 | | static int FileSeek (stream_t *, uint64_t); |
170 | | static int FileControl (stream_t *, int, va_list); |
171 | | |
172 | | /***************************************************************************** |
173 | | * FileOpen: open the file |
174 | | *****************************************************************************/ |
175 | | int FileOpen( vlc_object_t *p_this ) |
176 | 0 | { |
177 | 0 | stream_t *p_access = (stream_t*)p_this; |
178 | | |
179 | | /* Open file */ |
180 | 0 | int fd = -1; |
181 | |
|
182 | 0 | if (!strcasecmp (p_access->psz_name, "fd")) |
183 | 0 | { |
184 | 0 | char *end; |
185 | 0 | unsigned long oldfd = strtoul(p_access->psz_location, &end, 10); |
186 | |
|
187 | 0 | if (oldfd > INT_MAX) |
188 | 0 | errno = EBADF; |
189 | 0 | else if (*end == '\0') |
190 | 0 | fd = vlc_dup (oldfd); |
191 | 0 | else if (*end == '/' && end > p_access->psz_location) |
192 | 0 | { |
193 | 0 | char *name = vlc_uri_decode_duplicate (end - 1); |
194 | 0 | if (name != NULL) |
195 | 0 | { |
196 | 0 | name[0] = '.'; |
197 | 0 | fd = vlc_openat (oldfd, name, O_RDONLY | O_NONBLOCK); |
198 | 0 | free (name); |
199 | 0 | } |
200 | 0 | } |
201 | 0 | } |
202 | 0 | else |
203 | 0 | { |
204 | 0 | if (unlikely(p_access->psz_filepath == NULL)) |
205 | 0 | return VLC_EGENERIC; |
206 | 0 | fd = vlc_open (p_access->psz_filepath, O_RDONLY | O_NONBLOCK); |
207 | 0 | } |
208 | | |
209 | 0 | if (fd == -1) |
210 | 0 | { |
211 | 0 | msg_Err (p_access, "cannot open file %s (%s)", |
212 | 0 | p_access->psz_filepath ? p_access->psz_filepath |
213 | 0 | : p_access->psz_location, |
214 | 0 | vlc_strerror_c(errno)); |
215 | 0 | return VLC_EGENERIC; |
216 | 0 | } |
217 | | |
218 | 0 | struct stat st; |
219 | 0 | if (fstat (fd, &st)) |
220 | 0 | { |
221 | 0 | msg_Err (p_access, "read error: %s", vlc_strerror_c(errno)); |
222 | 0 | goto error; |
223 | 0 | } |
224 | | |
225 | 0 | #if O_NONBLOCK |
226 | | /* Force blocking mode back */ |
227 | 0 | fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) & ~O_NONBLOCK); |
228 | 0 | #endif |
229 | | |
230 | | /* Directories can be opened and read from, but only readdir() knows |
231 | | * how to parse the data. The directory plugin will do it. */ |
232 | 0 | if (S_ISDIR (st.st_mode)) |
233 | 0 | { |
234 | 0 | #ifdef HAVE_FDOPENDIR |
235 | 0 | DIR *p_dir = fdopendir(fd); |
236 | 0 | if (!p_dir) { |
237 | 0 | msg_Err (p_access, "fdopendir error: %s", vlc_strerror_c(errno)); |
238 | 0 | goto error; |
239 | 0 | } |
240 | 0 | return DirInit (p_access, p_dir); |
241 | | #else |
242 | | msg_Dbg (p_access, "ignoring directory"); |
243 | | goto error; |
244 | | #endif |
245 | 0 | } |
246 | | |
247 | 0 | access_sys_t *p_sys = vlc_obj_malloc(p_this, sizeof (*p_sys)); |
248 | 0 | if (unlikely(p_sys == NULL)) |
249 | 0 | goto error; |
250 | 0 | p_access->pf_read = Read; |
251 | 0 | p_access->pf_block = NULL; |
252 | 0 | p_access->pf_control = FileControl; |
253 | 0 | p_access->p_sys = p_sys; |
254 | 0 | p_sys->fd = fd; |
255 | |
|
256 | 0 | if (S_ISREG (st.st_mode) || S_ISBLK (st.st_mode)) |
257 | 0 | { |
258 | 0 | p_access->pf_seek = FileSeek; |
259 | 0 | p_sys->b_pace_control = true; |
260 | | |
261 | | /* Demuxers will need the beginning of the file for probing. */ |
262 | 0 | posix_fadvise (fd, 0, 4096, POSIX_FADV_WILLNEED); |
263 | | /* In most cases, we only read the file once. */ |
264 | 0 | posix_fadvise (fd, 0, 0, POSIX_FADV_NOREUSE); |
265 | | #ifdef F_NOCACHE |
266 | | fcntl (fd, F_NOCACHE, 0); |
267 | | #endif |
268 | | #ifdef F_RDAHEAD |
269 | | if (IsRemote(fd, p_access->psz_filepath)) |
270 | | fcntl (fd, F_RDAHEAD, 0); |
271 | | else |
272 | | fcntl (fd, F_RDAHEAD, 1); |
273 | | #endif |
274 | 0 | } |
275 | 0 | else |
276 | 0 | { |
277 | 0 | p_access->pf_seek = NULL; |
278 | 0 | p_sys->b_pace_control = strcasecmp (p_access->psz_name, "stream"); |
279 | 0 | } |
280 | |
|
281 | 0 | return VLC_SUCCESS; |
282 | | |
283 | 0 | error: |
284 | 0 | vlc_close (fd); |
285 | 0 | return VLC_EGENERIC; |
286 | 0 | } |
287 | | |
288 | | /***************************************************************************** |
289 | | * FileClose: close the target |
290 | | *****************************************************************************/ |
291 | | void FileClose (vlc_object_t * p_this) |
292 | 0 | { |
293 | 0 | stream_t *p_access = (stream_t*)p_this; |
294 | |
|
295 | 0 | if (p_access->pf_read == NULL) |
296 | 0 | { |
297 | 0 | DirClose (p_this); |
298 | 0 | return; |
299 | 0 | } |
300 | | |
301 | 0 | access_sys_t *p_sys = p_access->p_sys; |
302 | |
|
303 | 0 | vlc_close (p_sys->fd); |
304 | 0 | } |
305 | | |
306 | | |
307 | | static ssize_t Read (stream_t *p_access, void *p_buffer, size_t i_len) |
308 | 0 | { |
309 | 0 | access_sys_t *p_sys = p_access->p_sys; |
310 | 0 | int fd = p_sys->fd; |
311 | |
|
312 | 0 | ssize_t val = vlc_read_i11e (fd, p_buffer, i_len); |
313 | 0 | if (val < 0) |
314 | 0 | { |
315 | 0 | switch (errno) |
316 | 0 | { |
317 | 0 | case EINTR: |
318 | 0 | case EAGAIN: |
319 | 0 | return -1; |
320 | 0 | } |
321 | | |
322 | 0 | msg_Err (p_access, "read error: %s", vlc_strerror_c(errno)); |
323 | 0 | val = 0; |
324 | 0 | } |
325 | | |
326 | 0 | return val; |
327 | 0 | } |
328 | | |
329 | | /***************************************************************************** |
330 | | * Seek: seek to a specific location in a file |
331 | | *****************************************************************************/ |
332 | | static int FileSeek (stream_t *p_access, uint64_t i_pos) |
333 | 0 | { |
334 | 0 | access_sys_t *sys = p_access->p_sys; |
335 | |
|
336 | 0 | if (lseek(sys->fd, i_pos, SEEK_SET) == (off_t)-1) |
337 | 0 | return VLC_EGENERIC; |
338 | 0 | return VLC_SUCCESS; |
339 | 0 | } |
340 | | |
341 | | /***************************************************************************** |
342 | | * Control: |
343 | | *****************************************************************************/ |
344 | | static int FileControl( stream_t *p_access, int i_query, va_list args ) |
345 | 0 | { |
346 | 0 | access_sys_t *p_sys = p_access->p_sys; |
347 | 0 | bool *pb_bool; |
348 | 0 | vlc_tick_t *pi_64; |
349 | |
|
350 | 0 | switch( i_query ) |
351 | 0 | { |
352 | 0 | case STREAM_CAN_SEEK: |
353 | 0 | case STREAM_CAN_FASTSEEK: |
354 | 0 | pb_bool = va_arg( args, bool * ); |
355 | 0 | *pb_bool = (p_access->pf_seek != NULL); |
356 | 0 | break; |
357 | | |
358 | 0 | case STREAM_CAN_PAUSE: |
359 | 0 | case STREAM_CAN_CONTROL_PACE: |
360 | 0 | pb_bool = va_arg( args, bool * ); |
361 | 0 | *pb_bool = p_sys->b_pace_control; |
362 | 0 | break; |
363 | | |
364 | 0 | case STREAM_GET_SIZE: |
365 | 0 | case STREAM_GET_MTIME: |
366 | 0 | { |
367 | 0 | struct stat st; |
368 | |
|
369 | 0 | if (fstat (p_sys->fd, &st) || !S_ISREG(st.st_mode)) |
370 | 0 | return VLC_EGENERIC; |
371 | 0 | const uint64_t stat = |
372 | 0 | (i_query == STREAM_GET_SIZE) ? st.st_size : st.st_mtime; |
373 | 0 | *va_arg( args, uint64_t * ) = stat; |
374 | 0 | break; |
375 | 0 | } |
376 | | |
377 | 0 | case STREAM_GET_PTS_DELAY: |
378 | 0 | pi_64 = va_arg( args, vlc_tick_t * ); |
379 | 0 | if (IsRemote (p_sys->fd, p_access->psz_filepath)) |
380 | 0 | *pi_64 = VLC_TICK_FROM_MS( |
381 | 0 | var_InheritInteger (p_access, "network-caching") ); |
382 | 0 | else |
383 | 0 | *pi_64 = VLC_TICK_FROM_MS( |
384 | 0 | var_InheritInteger (p_access, "file-caching") ); |
385 | 0 | break; |
386 | | |
387 | 0 | case STREAM_SET_PAUSE_STATE: |
388 | | /* Nothing to do */ |
389 | 0 | break; |
390 | | |
391 | 0 | default: |
392 | 0 | return VLC_EGENERIC; |
393 | |
|
394 | 0 | } |
395 | 0 | return VLC_SUCCESS; |
396 | 0 | } |