]> git.sesse.net Git - vlc/blob - src/text/filesystem.c
d2f5324394810dcd5ffac2fb5e5494ae3191138f
[vlc] / src / text / filesystem.c
1 /*****************************************************************************
2  * filesystem.c: File system helpers
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * Copyright © 2005-2008 Rémi Denis-Courmont
6  *
7  * Authors: Rémi Denis-Courmont <rem # videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_charset.h>
33 #include <vlc_fs.h>
34 #include "libvlc.h" /* vlc_mkdir */
35 #include <vlc_rand.h>
36
37 #include <assert.h>
38
39 #include <stdio.h>
40 #include <limits.h> /* NAME_MAX */
41 #if !defined(NAME_MAX) && defined(_POSIX_NAME_MAX)
42 # define NAME_MAX _POSIX_NAME_MAX
43 #endif
44 #include <errno.h>
45 #include <sys/types.h>
46 #ifdef HAVE_DIRENT_H
47 #  include <dirent.h>
48 #endif
49 #ifdef HAVE_SYS_STAT_H
50 # include <sys/stat.h>
51 #endif
52 #ifdef HAVE_FCNTL_H
53 # include <fcntl.h>
54 #endif
55 #ifdef WIN32
56 # include <io.h>
57 # include <winsock2.h>
58 # ifndef UNDER_CE
59 #  include <direct.h>
60 # else
61 #  include <tchar.h>
62 # endif
63 #else
64 # include <unistd.h>
65 # include <sys/socket.h>
66 #endif
67
68 #ifndef HAVE_LSTAT
69 # define lstat( a, b ) stat(a, b)
70 #endif
71
72 #ifdef WIN32
73 static int convert_path (const char *restrict path, wchar_t *restrict wpath)
74 {
75     if (!MultiByteToWideChar (CP_UTF8, 0, path, -1, wpath, MAX_PATH))
76     {
77         errno = ENOENT;
78         return -1;
79     }
80     wpath[MAX_PATH] = L'\0';
81     return 0;
82 }
83 # define CONVERT_PATH(path, wpath, err) \
84   wchar_t wpath[MAX_PATH+1]; \
85   if (convert_path (path, wpath)) \
86       return (err)
87 #endif
88
89 /**
90  * Opens a system file handle.
91  *
92  * @param filename file path to open (with UTF-8 encoding)
93  * @param flags open() flags, see the C library open() documentation
94  * @return a file handle on success, -1 on error (see errno).
95  * @note Contrary to standard open(), this function returns file handles
96  * with the close-on-exec flag enabled.
97  */
98 int vlc_open (const char *filename, int flags, ...)
99 {
100     unsigned int mode = 0;
101     va_list ap;
102
103     va_start (ap, flags);
104     if (flags & O_CREAT)
105         mode = va_arg (ap, unsigned int);
106     va_end (ap);
107
108 #ifdef O_CLOEXEC
109     flags |= O_CLOEXEC;
110 #endif
111
112 #ifdef UNDER_CE
113     /*_open translates to wchar internally on WinCE*/
114     return _open (filename, flags, mode);
115 #elif defined (WIN32)
116     /*
117      * open() cannot open files with non-“ANSI” characters on Windows.
118      * We use _wopen() instead. Same thing for mkdir() and stat().
119      */
120     CONVERT_PATH(filename, wpath, -1);
121     return _wopen (wpath, flags, mode);
122
123 #endif
124     const char *local_name = ToLocale (filename);
125
126     if (local_name == NULL)
127     {
128         errno = ENOENT;
129         return -1;
130     }
131
132     int fd = open (local_name, flags, mode);
133 #ifdef HAVE_FCNTL
134     if (fd != -1)
135         fcntl (fd, F_SETFD, FD_CLOEXEC);
136 #endif
137
138     LocaleFree (local_name);
139     return fd;
140 }
141
142 /**
143  * Opens a FILE pointer.
144  * @param filename file path, using UTF-8 encoding
145  * @param mode fopen file open mode
146  * @return NULL on error, an open FILE pointer on success.
147  */
148 FILE *vlc_fopen (const char *filename, const char *mode)
149 {
150     int rwflags = 0, oflags = 0;
151     bool append = false;
152
153     for (const char *ptr = mode; *ptr; ptr++)
154     {
155         switch (*ptr)
156         {
157             case 'r':
158                 rwflags = O_RDONLY;
159                 break;
160
161             case 'a':
162                 rwflags = O_WRONLY;
163                 oflags |= O_CREAT;
164                 append = true;
165                 break;
166
167             case 'w':
168                 rwflags = O_WRONLY;
169                 oflags |= O_CREAT | O_TRUNC;
170                 break;
171
172             case '+':
173                 rwflags = O_RDWR;
174                 break;
175
176 #ifdef O_TEXT
177             case 't':
178                 oflags |= O_TEXT;
179                 break;
180 #endif
181         }
182     }
183
184     int fd = vlc_open (filename, rwflags | oflags, 0666);
185     if (fd == -1)
186         return NULL;
187
188     if (append && (lseek (fd, 0, SEEK_END) == -1))
189     {
190         close (fd);
191         return NULL;
192     }
193
194     FILE *stream = fdopen (fd, mode);
195     if (stream == NULL)
196         close (fd);
197
198     return stream;
199 }
200
201 /**
202  * Opens a system file handle relative to an existing directory handle.
203  *
204  * @param dir directory file descriptor
205  * @param filename file path to open (with UTF-8 encoding)
206  * @param flags open() flags, see the C library open() documentation
207  * @return a file handle on success, -1 on error (see errno).
208  * @note Contrary to standard open(), this function returns file handles
209  * with the close-on-exec flag enabled.
210  */
211 int vlc_openat (int dir, const char *filename, int flags, ...)
212 {
213     unsigned int mode = 0;
214     va_list ap;
215
216     va_start (ap, flags);
217     if (flags & O_CREAT)
218         mode = va_arg (ap, unsigned int);
219     va_end (ap);
220
221 #ifdef O_CLOEXEC
222     flags |= O_CLOEXEC;
223 #endif
224
225     const char *local_name = ToLocale (filename);
226     if (local_name == NULL)
227     {
228         errno = ENOENT;
229         return -1;
230     }
231
232 #ifdef HAVE_OPENAT
233     int fd = openat (dir, local_name, flags, mode);
234 # ifdef HAVE_FCNTL
235     if (fd != -1)
236         fcntl (fd, F_SETFD, FD_CLOEXEC);
237 # endif
238 #else
239     int fd = -1;
240     errno = ENOSYS;
241 #endif
242
243     LocaleFree (local_name);
244     return fd;
245 }
246
247
248 /**
249  * Creates a directory using UTF-8 paths.
250  *
251  * @param dirname a UTF-8 string with the name of the directory that you
252  *        want to create.
253  * @param mode directory permissions
254  * @return 0 on success, -1 on error (see errno).
255  */
256 int vlc_mkdir( const char *dirname, mode_t mode )
257 {
258 #if defined (UNDER_CE)
259     (void) mode;
260     /* mkdir converts internally to wchar */
261     return _mkdir(dirname);
262 #elif defined (WIN32)
263     (void) mode;
264     CONVERT_PATH (dirname, wpath, -1);
265     return _wmkdir (wpath);
266
267 #else
268     char *locname = ToLocale( dirname );
269     int res;
270
271     if( locname == NULL )
272     {
273         errno = ENOENT;
274         return -1;
275     }
276     res = mkdir( locname, mode );
277
278     LocaleFree( locname );
279     return res;
280 #endif
281 }
282
283 /**
284  * Opens a DIR pointer.
285  *
286  * @param dirname UTF-8 representation of the directory name
287  * @return a pointer to the DIR struct, or NULL in case of error.
288  * Release with standard closedir().
289  */
290 DIR *vlc_opendir( const char *dirname )
291 {
292 #ifdef WIN32
293     CONVERT_PATH (dirname, wpath, NULL);
294     return (DIR *)vlc_wopendir (wpath);
295
296 #else
297     const char *local_name = ToLocale( dirname );
298
299     if( local_name != NULL )
300     {
301         DIR *dir = opendir( local_name );
302         LocaleFree( local_name );
303         return dir;
304     }
305 #endif
306
307     errno = ENOENT;
308     return NULL;
309 }
310
311 /**
312  * Reads the next file name from an open directory.
313  *
314  * @param dir The directory that is being read
315  *
316  * @return a UTF-8 string of the directory entry.
317  * Use free() to free this memory.
318  */
319 char *vlc_readdir( DIR *dir )
320 {
321 #ifdef WIN32
322     struct _wdirent *ent = vlc_wreaddir (dir);
323     if (ent == NULL)
324         return NULL;
325
326     return FromWide (ent->d_name);
327 #else
328     struct dirent *ent;
329     struct
330     {
331         struct dirent ent;
332         char buf[NAME_MAX + 1];
333     } buf;
334     int val = readdir_r (dir, &buf.ent, &ent);
335     if (val)
336     {
337         errno = val;
338         return NULL;
339     }
340     return ent ? vlc_fix_readdir( ent->d_name ) : NULL;
341 #endif
342 }
343
344 static int dummy_select( const char *str )
345 {
346     (void)str;
347     return 1;
348 }
349
350 /**
351  * Does the same as vlc_scandir(), but takes an open directory pointer
352  * instead of a directory path.
353  */
354 int vlc_loaddir( DIR *dir, char ***namelist,
355                   int (*select)( const char * ),
356                   int (*compar)( const char **, const char ** ) )
357 {
358     if( select == NULL )
359         select = dummy_select;
360
361     if( dir == NULL )
362         return -1;
363     else
364     {
365         char **tab = NULL;
366         char *entry;
367         unsigned num = 0;
368
369         rewinddir( dir );
370
371         while( ( entry = vlc_readdir( dir ) ) != NULL )
372         {
373             char **newtab;
374
375             if( !select( entry ) )
376             {
377                 free( entry );
378                 continue;
379             }
380
381             newtab = realloc( tab, sizeof( char * ) * (num + 1) );
382             if( newtab == NULL )
383             {
384                 free( entry );
385                 goto error;
386             }
387             tab = newtab;
388             tab[num++] = entry;
389         }
390
391         if( compar != NULL )
392             qsort( tab, num, sizeof( tab[0] ),
393                    (int (*)( const void *, const void *))compar );
394
395         *namelist = tab;
396         return num;
397
398     error:{
399         unsigned i;
400
401         for( i = 0; i < num; i++ )
402             free( tab[i] );
403         free( tab );
404         }
405     }
406     return -1;
407 }
408
409 /**
410  * Selects file entries from a directory, as GNU C scandir().
411  *
412  * @param dirname UTF-8 diretory path
413  * @param pointer [OUT] pointer set, on successful completion, to the address
414  * of a table of UTF-8 filenames. All filenames must be freed with free().
415  * The table itself must be freed with free() as well.
416  *
417  * @return How many file names were selected (possibly 0),
418  * or -1 in case of error.
419  */
420 int vlc_scandir( const char *dirname, char ***namelist,
421                   int (*select)( const char * ),
422                   int (*compar)( const char **, const char ** ) )
423 {
424     DIR *dir = vlc_opendir (dirname);
425     int val = -1;
426
427     if (dir != NULL)
428     {
429         val = vlc_loaddir (dir, namelist, select, compar);
430         closedir (dir);
431     }
432     return val;
433 }
434
435 static int vlc_statEx( const char *filename, struct stat *buf,
436                         bool deref )
437 {
438 #ifdef UNDER_CE
439     /*_stat translates to wchar internally on WinCE*/
440     return _stat( filename, buf );
441 #elif defined (WIN32)
442     CONVERT_PATH (filename, wpath, -1);
443     return _wstati64 (wpath, buf);
444
445 #endif
446 #ifdef HAVE_SYS_STAT_H
447     const char *local_name = ToLocale( filename );
448
449     if( local_name != NULL )
450     {
451         int res = deref ? stat( local_name, buf )
452                        : lstat( local_name, buf );
453         LocaleFree( local_name );
454         return res;
455     }
456     errno = ENOENT;
457 #endif
458     return -1;
459 }
460
461 /**
462  * Finds file/inode information, as stat().
463  * Consider using fstat() instead, if possible.
464  *
465  * @param filename UTF-8 file path
466  */
467 int vlc_stat( const char *filename, struct stat *buf)
468 {
469     return vlc_statEx( filename, buf, true );
470 }
471
472 /**
473  * Finds file/inode information, as lstat().
474  * Consider using fstat() instead, if possible.
475  *
476  * @param filename UTF-8 file path
477  */
478 int vlc_lstat( const char *filename, struct stat *buf)
479 {
480     return vlc_statEx( filename, buf, false );
481 }
482
483 /**
484  * Removes a file.
485  *
486  * @param filename a UTF-8 string with the name of the file you want to delete.
487  * @return A 0 return value indicates success. A -1 return value indicates an
488  *        error, and an error code is stored in errno
489  */
490 int vlc_unlink( const char *filename )
491 {
492 #ifdef UNDER_CE
493     /*_open translates to wchar internally on WinCE*/
494     return _unlink( filename );
495 #elif defined (WIN32)
496     CONVERT_PATH (filename, wpath, -1);
497     return _wunlink (wpath);
498
499 #endif
500     const char *local_name = ToLocale( filename );
501
502     if( local_name == NULL )
503     {
504         errno = ENOENT;
505         return -1;
506     }
507
508     int ret = unlink( local_name );
509     LocaleFree( local_name );
510     return ret;
511 }
512
513 /**
514  * Moves a file atomically. This only works within a single file system.
515  *
516  * @param oldpath path to the file before the move
517  * @param newpath intended path to the file after the move
518  * @return A 0 return value indicates success. A -1 return value indicates an
519  *        error, and an error code is stored in errno
520  */
521 int vlc_rename (const char *oldpath, const char *newpath)
522 {
523 #if defined (WIN32)
524     CONVERT_PATH (oldpath, wold, -1);
525     CONVERT_PATH (newpath, wnew, -1);
526 # ifdef UNDER_CE
527     /* FIXME: errno support */
528     if (MoveFileW (wold, wnew))
529         return 0;
530     else
531         return -1;
532 #else
533     if (_wrename (wold, wnew) && errno == EACCES)
534     {   /* Windows does not allow atomic file replacement */
535         if (_wremove (wnew))
536         {
537             errno = EACCES; /* restore errno */
538             return -1;
539         }
540         if (_wrename (wold, wnew))
541             return -1;
542     }
543     return 0;
544 #endif
545
546 #endif
547     const char *lo = ToLocale (oldpath);
548     if (lo == NULL)
549         goto error;
550
551     const char *ln = ToLocale (newpath);
552     if (ln == NULL)
553     {
554         LocaleFree (lo);
555 error:
556         errno = ENOENT;
557         return -1;
558     }
559
560     int ret = rename (lo, ln);
561     LocaleFree (lo);
562     LocaleFree (ln);
563     return ret;
564 }
565
566 int vlc_mkstemp( char *template )
567 {
568     static const char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
569     static const int i_digits = sizeof(digits)/sizeof(*digits) - 1;
570
571     /* */
572     assert( template );
573
574     /* Check template validity */
575     const size_t i_length = strlen( template );
576     char *psz_rand = &template[i_length-6];
577
578     if( i_length < 6 || strcmp( psz_rand, "XXXXXX" ) )
579     {
580         errno = EINVAL;
581         return -1;
582     }
583
584     /* */
585     for( int i = 0; i < 256; i++ )
586     {
587         /* Create a pseudo random file name */
588         uint8_t pi_rand[6];
589
590         vlc_rand_bytes( pi_rand, sizeof(pi_rand) );
591         for( int j = 0; j < 6; j++ )
592             psz_rand[j] = digits[pi_rand[j] % i_digits];
593
594         /* */
595         int fd = vlc_open( template, O_CREAT | O_EXCL | O_RDWR, 0600 );
596         if( fd >= 0 )
597             return fd;
598         if( errno != EEXIST )
599             return -1;
600     }
601
602     errno = EEXIST;
603     return -1;
604 }
605
606 #ifdef UNDER_CE
607 # define dup(fd) (fd, -1)
608 #endif
609
610 /**
611  * Duplicates a file descriptor. The new file descriptor has the close-on-exec
612  * descriptor flag set.
613  * @return a new file descriptor or -1
614  */
615 int vlc_dup (int oldfd)
616 {
617     int newfd;
618
619 #ifdef HAVE_DUP3
620     /* Unfortunately, dup3() works like dup2(), not like plain dup(). So we
621      * need such contortion to find the new file descriptor while preserving
622      * thread safety of the file descriptor table. */
623     newfd = vlc_open ("/dev/null", O_RDONLY);
624     if (likely(newfd != -1))
625     {
626         if (likely(dup3 (oldfd, newfd, O_CLOEXEC) == newfd))
627             return newfd;
628         close (newfd);
629     }
630 #endif
631
632     newfd = dup (oldfd);
633 #ifdef HAVE_FCNTL
634     if (likely(newfd != -1))
635         fcntl (newfd, F_SETFD, FD_CLOEXEC);
636 #endif
637     return newfd;
638 }
639
640 #include <vlc_network.h>
641
642 /**
643  * Creates a socket file descriptor. The new file descriptor has the
644  * close-on-exec flag set.
645  * @param pf protocol family
646  * @param type socket type
647  * @param proto network protocol
648  * @param nonblock true to create a non-blocking socket
649  * @return a new file descriptor or -1
650  */
651 int vlc_socket (int pf, int type, int proto, bool nonblock)
652 {
653     int fd;
654
655 #ifdef SOCK_CLOEXEC
656     type |= SOCK_CLOEXEC;
657     if (nonblock)
658         type |= SOCK_NONBLOCK;
659     fd = socket (pf, type, proto);
660     if (fd != -1 || errno != EINVAL)
661         return fd;
662
663     type &= ~(SOCK_CLOEXEC|SOCK_NONBLOCK);
664 #endif
665
666     fd = socket (pf, type, proto);
667     if (fd == -1)
668         return -1;
669
670 #ifndef WIN32
671     fcntl (fd, F_SETFD, FD_CLOEXEC);
672     if (nonblock)
673         fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
674 #else
675     if (nonblock)
676         ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 });
677 #endif
678     return fd;
679 }
680
681 /**
682  * Accepts an inbound connection request on a listening socket.
683  * The new file descriptor has the close-on-exec flag set.
684  * @param lfd listening socket file descriptor
685  * @param addr pointer to the peer address or NULL [OUT]
686  * @param alen pointer to the length of the peer address or NULL [OUT]
687  * @param nonblock whether to put the new socket in non-blocking mode
688  * @return a new file descriptor, or -1 on error.
689  */
690 int vlc_accept (int lfd, struct sockaddr *addr, socklen_t *alen, bool nonblock)
691 {
692 #ifdef HAVE_ACCEPT4
693     int flags = SOCK_CLOEXEC;
694     if (nonblock)
695         flags |= SOCK_NONBLOCK;
696
697     do
698     {
699         int fd = accept4 (lfd, addr, alen, flags);
700         if (fd != -1)
701             return fd;
702     }
703     while (errno == EINTR);
704
705     if (errno != ENOSYS)
706         return -1;
707 #endif
708 #ifdef WIN32
709     errno = 0;
710 #endif
711
712     do
713     {
714         int fd = accept (lfd, addr, alen);
715         if (fd != -1)
716         {
717 #ifndef WIN32
718             fcntl (fd, F_SETFD, FD_CLOEXEC);
719             if (nonblock)
720                 fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
721 #else
722             if (nonblock)
723                 ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 });
724 #endif
725             return fd;
726         }
727     }
728     while (errno == EINTR);
729
730     return -1;
731 }