file.c 10.9 KB
Newer Older
1
2
3
/*****************************************************************************
 * file.c: file input (file: access plug-in)
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2001-2006 VLC authors and VideoLAN
5
 * Copyright © 2006-2007 Rémi Denis-Courmont
gbazin's avatar
gbazin committed
6
 * $Id$
7
8
 *
 * Authors: Christophe Massiot <massiot@via.ecp.fr>
9
 *          Rémi Denis-Courmont <rem # videolan # org>
10
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
11
12
13
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
14
 * (at your option) any later version.
15
 *
16
17
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
18
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
LGPL    
Jean-Baptiste Kempf committed
21
22
23
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
 *****************************************************************************/

26
27
28
29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30
#include <assert.h>
31
#include <errno.h>
32
#include <sys/types.h>
33
#include <sys/stat.h>
34
#include <fcntl.h>
35
36
37
38
39
40
41
#ifdef HAVE_FSTATVFS
#   include <sys/statvfs.h>
#   if defined (HAVE_SYS_MOUNT_H)
#      include <sys/param.h>
#      include <sys/mount.h>
#   endif
#endif
42
#ifdef HAVE_LINUX_MAGIC_H
43
#   include <sys/vfs.h>
44
#   include <linux/magic.h>
45
#endif
46

47
#if defined( WIN32 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
48
#   include <io.h>
49
#   include <ctype.h>
50
#   include <shlwapi.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
51
#else
52
53
#   include <unistd.h>
#endif
54
#include <dirent.h>
55

56
57
58
59
60
61
62
63
#include <vlc_common.h>
#include "fs.h"
#include <vlc_input.h>
#include <vlc_access.h>
#include <vlc_dialog.h>
#ifdef WIN32
# include <vlc_charset.h>
#endif
64
#include <vlc_fs.h>
65
#include <vlc_url.h>
66

67
struct access_sys_t
68
{
69
    int fd;
gbazin's avatar
gbazin committed
70

71
    /* */
72
    bool b_pace_control;
73
};
74

75
#if !defined (WIN32) && !defined (__OS2__)
76
77
static bool IsRemote (int fd)
{
78
79
80
81
82
83
#if defined (HAVE_FSTATVFS) && defined (MNT_LOCAL)
    struct statvfs stf;

    if (fstatvfs (fd, &stf))
        return false;
    /* fstatvfs() is in POSIX, but MNT_LOCAL is not */
Joseph S. Atkinson's avatar
Joseph S. Atkinson committed
84
    return !(stf.f_flag & MNT_LOCAL);
85
86

#elif defined (HAVE_LINUX_MAGIC_H)
87
88
89
90
91
    struct statfs stf;

    if (fstatfs (fd, &stf))
        return false;

92
    switch ((unsigned long)stf.f_type)
93
94
95
96
97
98
99
100
101
102
    {
        case AFS_SUPER_MAGIC:
        case CODA_SUPER_MAGIC:
        case NCP_SUPER_MAGIC:
        case NFS_SUPER_MAGIC:
        case SMB_SUPER_MAGIC:
        case 0xFF534D42 /*CIFS_MAGIC_NUMBER*/:
            return true;
    }
    return false;
103
104

#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
105
    (void)fd;
106
107
108
109
    return false;

#endif
}
110
111
# define IsRemote(fd,path) IsRemote(fd)

KO Myung-Hun's avatar
KO Myung-Hun committed
112
#else /* WIN32 || __OS2__ */
113
114
static bool IsRemote (const char *path)
{
115
# if !defined(__OS2__) && !VLC_WINSTORE_APP
116
117
118
119
120
121
122
123
124
125
    wchar_t *wpath = ToWide (path);
    bool is_remote = (wpath != NULL && PathIsNetworkPathW (wpath));
    free (wpath);
    return is_remote;
# else
    return (! strncmp(path, "\\\\", 2));
# endif
}
# define IsRemote(fd,path) IsRemote(path)
#endif
126

127
#ifndef HAVE_POSIX_FADVISE
128
# define posix_fadvise(fd, off, len, adv)
129
#endif
130

131
132
133
134
135
136
static ssize_t FileRead (access_t *, uint8_t *, size_t);
static int FileSeek (access_t *, uint64_t);
static ssize_t StreamRead (access_t *, uint8_t *, size_t);
static int NoSeek (access_t *, uint64_t);
static int FileControl (access_t *, int, va_list);

137
/*****************************************************************************
Rafaël Carré's avatar
Rafaël Carré committed
138
 * FileOpen: open the file
139
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
140
int FileOpen( vlc_object_t *p_this )
141
{
142
143
    access_t     *p_access = (access_t*)p_this;

144
    /* Open file */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
145
    int fd = -1;
146
147

    if (!strcasecmp (p_access->psz_access, "fd"))
148
149
    {
        char *end;
150
        int oldfd = strtol (p_access->psz_location, &end, 10);
151
152

        if (*end == '\0')
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
153
            fd = vlc_dup (oldfd);
154
        else if (*end == '/' && end > p_access->psz_location)
155
156
        {
            char *name = decode_URI_duplicate (end - 1);
157
            if (name != NULL)
158
159
            {
                name[0] = '.';
160
                fd = vlc_openat (oldfd, name, O_RDONLY | O_NONBLOCK);
161
162
163
164
                free (name);
            }
        }
    }
165
    else
166
    {
167
168
        const char *path = p_access->psz_filepath;

169
170
        if (unlikely(path == NULL))
            return VLC_EGENERIC;
171
        msg_Dbg (p_access, "opening file `%s'", path);
172
        fd = vlc_open (path, O_RDONLY | O_NONBLOCK);
173
174
175
176
        if (fd == -1)
        {
            msg_Err (p_access, "cannot open file %s (%m)", path);
            dialog_Fatal (p_access, _("File reading failed"),
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
177
                          _("VLC could not open the file \"%s\" (%m)."), path);
178
        }
179
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
180
    if (fd == -1)
181
        return VLC_EGENERIC;
182

183
    struct stat st;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
184
    if (fstat (fd, &st))
gbazin's avatar
gbazin committed
185
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
186
187
        msg_Err (p_access, "failed to read (%m)");
        goto error;
188
    }
189

190
191
192
193
194
195
196
197
198
#if O_NONBLOCK
    int flags = fcntl (fd, F_GETFL);
    if (S_ISFIFO (st.st_mode) || S_ISSOCK (st.st_mode))
        /* Force non-blocking mode where applicable (fd://) */
        flags |= O_NONBLOCK;
    else
        /* Force blocking mode when not useful or not specified */
        flags &= ~O_NONBLOCK;
    fcntl (fd, F_SETFL, flags);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
199
#endif
200

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
201
202
203
    /* Directories can be opened and read from, but only readdir() knows
     * how to parse the data. The directory plugin will do it. */
    if (S_ISDIR (st.st_mode))
204
    {
205
206
207
208
209
210
#ifdef HAVE_FDOPENDIR
        DIR *handle = fdopendir (fd);
        if (handle == NULL)
            goto error; /* Uh? */
        return DirInit (p_access, handle);
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
211
212
        msg_Dbg (p_access, "ignoring directory");
        goto error;
213
#endif
214
    }
215
216
217
218
219
220
221
222
223
224

    access_sys_t *p_sys = malloc (sizeof (*p_sys));
    if (unlikely(p_sys == NULL))
        goto error;
    access_InitFields (p_access);
    p_access->pf_block = NULL;
    p_access->pf_control = FileControl;
    p_access->p_sys = p_sys;
    p_sys->fd = fd;

225
    if (S_ISREG (st.st_mode) || S_ISBLK (st.st_mode))
226
    {
227
228
229
230
        p_access->pf_read = FileRead;
        p_access->pf_seek = FileSeek;
        p_access->info.i_size = st.st_size;
        p_sys->b_pace_control = true;
231
232
233
234
235

        /* Demuxers will need the beginning of the file for probing. */
        posix_fadvise (fd, 0, 4096, POSIX_FADV_WILLNEED);
        /* In most cases, we only read the file once. */
        posix_fadvise (fd, 0, 0, POSIX_FADV_NOREUSE);
236
#ifdef F_RDAHEAD
237
        fcntl (fd, F_RDAHEAD, 1);
238
239
240
#endif
#ifdef F_NOCACHE
        fcntl (fd, F_NOCACHE, 0);
241
#endif
242
    }
243
244
245
246
247
248
249
    else
    {
        p_access->pf_read = StreamRead;
        p_access->pf_seek = NoSeek;
        p_sys->b_pace_control = strcasecmp (p_access->psz_access, "stream");
    }

250
    return VLC_SUCCESS;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
251
252

error:
253
    close (fd);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
254
    return VLC_EGENERIC;
255
256
257
}

/*****************************************************************************
Rafaël Carré's avatar
Rafaël Carré committed
258
 * FileClose: close the target
259
 *****************************************************************************/
Rafaël Carré's avatar
Rafaël Carré committed
260
void FileClose (vlc_object_t * p_this)
261
{
262
    access_t     *p_access = (access_t*)p_this;
263
264
265
266
267
268
269

    if (p_access->pf_read == NULL)
    {
        DirClose (p_this);
        return;
    }

270
    access_sys_t *p_sys = p_access->p_sys;
271

272
    close (p_sys->fd);
273
    free (p_sys);
274
275
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
276
277
278

#include <vlc_network.h>

279
280
281
282
/**
 * Reads from a regular file.
 */
static ssize_t FileRead (access_t *p_access, uint8_t *p_buffer, size_t i_len)
283
{
284
    access_sys_t *p_sys = p_access->p_sys;
285
    int fd = p_sys->fd;
286
    ssize_t val = read (fd, p_buffer, i_len);
287

288
    if (val < 0)
289
    {
290
        switch (errno)
291
        {
292
293
            case EINTR:
            case EAGAIN:
294
                return -1;
295
        }
296
297
298
299
300

        msg_Err (p_access, "read error: %m");
        dialog_Fatal (p_access, _("File reading failed"),
                      _("VLC could not read the file (%m)."));
        val = 0;
301
    }
302
303
304

    p_access->info.i_pos += val;
    p_access->info.b_eof = !val;
305
    if (p_access->info.i_pos >= p_access->info.i_size)
306
    {
307
        struct stat st;
gbazin's avatar
gbazin committed
308

309
        if (fstat (fd, &st) == 0)
310
            p_access->info.i_size = st.st_size;
311
    }
312
    return val;
313
314
}

315

316
317
318
/*****************************************************************************
 * Seek: seek to a specific location in a file
 *****************************************************************************/
319
static int FileSeek (access_t *p_access, uint64_t i_pos)
320
{
321
    p_access->info.i_pos = i_pos;
322
    p_access->info.b_eof = false;
323

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
324
    lseek (p_access->p_sys->fd, i_pos, SEEK_SET);
325
326
327
    return VLC_SUCCESS;
}

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/**
 * Reads from a non-seekable file.
 */
static ssize_t StreamRead (access_t *p_access, uint8_t *p_buffer, size_t i_len)
{
    access_sys_t *p_sys = p_access->p_sys;
    int fd = p_sys->fd;

#if !defined (WIN32) && !defined (__OS2__)
    ssize_t val = net_Read (p_access, fd, NULL, p_buffer, i_len, false);
#else
    ssize_t val = read (fd, p_buffer, i_len);
#endif

    if (val < 0)
    {
        switch (errno)
        {
            case EINTR:
            case EAGAIN:
                return -1;
        }
        msg_Err (p_access, "read error: %m");
        val = 0;
    }

    p_access->info.i_pos += val;
    p_access->info.b_eof = !val;
    return val;
}

static int NoSeek (access_t *p_access, uint64_t i_pos)
360
361
362
363
364
365
{
    /* assert(0); ?? */
    (void) p_access; (void) i_pos;
    return VLC_EGENERIC;
}

366
367
368
/*****************************************************************************
 * Control:
 *****************************************************************************/
369
static int FileControl( access_t *p_access, int i_query, va_list args )
370
371
{
    access_sys_t *p_sys = p_access->p_sys;
372
373
    bool    *pb_bool;
    int64_t *pi_64;
374
375

    switch( i_query )
376
    {
377
378
379
        /* */
        case ACCESS_CAN_SEEK:
        case ACCESS_CAN_FASTSEEK:
380
            pb_bool = (bool*)va_arg( args, bool* );
381
            *pb_bool = (p_access->pf_seek != NoSeek);
382
383
384
385
            break;

        case ACCESS_CAN_PAUSE:
        case ACCESS_CAN_CONTROL_PACE:
386
            pb_bool = (bool*)va_arg( args, bool* );
387
388
389
390
391
392
            *pb_bool = p_sys->b_pace_control;
            break;

        /* */
        case ACCESS_GET_PTS_DELAY:
            pi_64 = (int64_t*)va_arg( args, int64_t * );
393
            if (IsRemote (p_sys->fd, p_access->psz_filepath))
394
395
396
397
                *pi_64 = var_InheritInteger (p_access, "network-caching");
            else
                *pi_64 = var_InheritInteger (p_access, "file-caching");
            *pi_64 *= 1000;
398
            break;
399

400
401
402
403
404
405
406
407
        /* */
        case ACCESS_SET_PAUSE_STATE:
            /* Nothing to do */
            break;

        case ACCESS_GET_TITLE_INFO:
        case ACCESS_SET_TITLE:
        case ACCESS_SET_SEEKPOINT:
408
        case ACCESS_SET_PRIVATE_ID_STATE:
409
        case ACCESS_GET_META:
410
        case ACCESS_GET_PRIVATE_ID_STATE:
411
        case ACCESS_GET_CONTENT_TYPE:
412
413
414
            return VLC_EGENERIC;

        default:
415
            msg_Warn( p_access, "unimplemented query %d in control", i_query );
416
417
            return VLC_EGENERIC;

418
    }
419
    return VLC_SUCCESS;
420
}