]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/fs-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "dirent-util.h"
28 #include "parse-util.h"
29 #include "path-util.h"
30 #include "string-util.h"
32 #include "user-util.h"
35 int unlink_noerrno ( const char * path
) {
46 int rmdir_parents ( const char * path
, const char * stop
) {
55 /* Skip trailing slashes */
56 while ( l
> 0 && path
[ l
- 1 ] == '/' )
62 /* Skip last component */
63 while ( l
> 0 && path
[ l
- 1 ] != '/' )
66 /* Skip trailing slashes */
67 while ( l
> 0 && path
[ l
- 1 ] == '/' )
77 if ( path_startswith ( stop
, t
)) {
94 int rename_noreplace ( int olddirfd
, const char * oldpath
, int newdirfd
, const char * newpath
) {
98 ret
= renameat2 ( olddirfd
, oldpath
, newdirfd
, newpath
, RENAME_NOREPLACE
);
102 /* renameat2() exists since Linux 3.15, btrfs added support for it later.
103 * If it is not implemented, fallback to another method. */
104 if (! IN_SET ( errno
, EINVAL
, ENOSYS
))
107 /* The link()/unlink() fallback does not work on directories. But
108 * renameat() without RENAME_NOREPLACE gives the same semantics on
109 * directories, except when newpath is an *empty* directory. This is
111 ret
= fstatat ( olddirfd
, oldpath
, & buf
, AT_SYMLINK_NOFOLLOW
);
112 if ( ret
>= 0 && S_ISDIR ( buf
. st_mode
)) {
113 ret
= renameat ( olddirfd
, oldpath
, newdirfd
, newpath
);
114 return ret
>= 0 ? 0 : - errno
;
117 /* If it is not a directory, use the link()/unlink() fallback. */
118 ret
= linkat ( olddirfd
, oldpath
, newdirfd
, newpath
, 0 );
122 ret
= unlinkat ( olddirfd
, oldpath
, 0 );
124 /* backup errno before the following unlinkat() alters it */
126 ( void ) unlinkat ( newdirfd
, newpath
, 0 );
134 int readlinkat_malloc ( int fd
, const char * p
, char ** ret
) {
149 n
= readlinkat ( fd
, p
, c
, l
- 1 );
156 if (( size_t ) n
< l
- 1 ) {
167 int readlink_malloc ( const char * p
, char ** ret
) {
168 return readlinkat_malloc ( AT_FDCWD
, p
, ret
);
171 int readlink_value ( const char * p
, char ** ret
) {
172 _cleanup_free_
char * link
= NULL
;
176 r
= readlink_malloc ( p
, & link
);
180 value
= basename ( link
);
184 value
= strdup ( value
);
193 int readlink_and_make_absolute ( const char * p
, char ** r
) {
194 _cleanup_free_
char * target
= NULL
;
201 j
= readlink_malloc ( p
, & target
);
205 k
= file_in_same_dir ( p
, target
);
213 int readlink_and_canonicalize ( const char * p
, char ** r
) {
220 j
= readlink_and_make_absolute ( p
, & t
);
224 s
= canonicalize_file_name ( t
);
231 path_kill_slashes (* r
);
236 int chmod_and_chown ( const char * path
, mode_t mode
, uid_t uid
, gid_t gid
) {
239 /* Under the assumption that we are running privileged we
240 * first change the access mode and only then hand out
241 * ownership to avoid a window where access is too open. */
243 if ( mode
!= MODE_INVALID
)
244 if ( chmod ( path
, mode
) < 0 )
247 if ( uid
!= UID_INVALID
|| gid
!= GID_INVALID
)
248 if ( chown ( path
, uid
, gid
) < 0 )
254 int fchmod_and_fchown ( int fd
, mode_t mode
, uid_t uid
, gid_t gid
) {
257 /* Under the assumption that we are running privileged we
258 * first change the access mode and only then hand out
259 * ownership to avoid a window where access is too open. */
261 if ( mode
!= MODE_INVALID
)
262 if ( fchmod ( fd
, mode
) < 0 )
265 if ( uid
!= UID_INVALID
|| gid
!= GID_INVALID
)
266 if ( fchown ( fd
, uid
, gid
) < 0 )
272 int fchmod_umask ( int fd
, mode_t m
) {
277 r
= fchmod ( fd
, m
& (~ u
)) < 0 ? - errno
: 0 ;
283 int fd_warn_permissions ( const char * path
, int fd
) {
286 if ( fstat ( fd
, & st
) < 0 )
289 if ( st
. st_mode
& 0111 )
290 log_warning ( "Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway." , path
);
292 if ( st
. st_mode
& 0002 )
293 log_warning ( "Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway." , path
);
295 if ( getpid () == 1 && ( st
. st_mode
& 0044 ) != 0044 )
296 log_warning ( "Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway." , path
);
301 int touch_file ( const char * path
, bool parents
, usec_t stamp
, uid_t uid
, gid_t gid
, mode_t mode
) {
302 _cleanup_close_
int fd
;
308 mkdir_parents ( path
, 0755 );
310 fd
= open ( path
, O_WRONLY
| O_CREAT
| O_CLOEXEC
| O_NOCTTY
, mode
> 0 ? mode
: 0644 );
314 if ( mode
!= MODE_INVALID
) {
315 r
= fchmod ( fd
, mode
);
320 if ( uid
!= UID_INVALID
|| gid
!= GID_INVALID
) {
321 r
= fchown ( fd
, uid
, gid
);
326 if ( stamp
!= USEC_INFINITY
) {
327 struct timespec ts
[ 2 ];
329 timespec_store (& ts
[ 0 ], stamp
);
331 r
= futimens ( fd
, ts
);
333 r
= futimens ( fd
, NULL
);
340 int touch ( const char * path
) {
341 return touch_file ( path
, false , USEC_INFINITY
, UID_INVALID
, GID_INVALID
, MODE_INVALID
);
344 int symlink_idempotent ( const char * from
, const char * to
) {
345 _cleanup_free_
char * p
= NULL
;
351 if ( symlink ( from
, to
) < 0 ) {
355 r
= readlink_malloc ( to
, & p
);
366 int symlink_atomic ( const char * from
, const char * to
) {
367 _cleanup_free_
char * t
= NULL
;
373 r
= tempfn_random ( to
, NULL
, & t
);
377 if ( symlink ( from
, t
) < 0 )
380 if ( rename ( t
, to
) < 0 ) {
388 int mknod_atomic ( const char * path
, mode_t mode
, dev_t dev
) {
389 _cleanup_free_
char * t
= NULL
;
394 r
= tempfn_random ( path
, NULL
, & t
);
398 if ( mknod ( t
, mode
, dev
) < 0 )
401 if ( rename ( t
, path
) < 0 ) {
409 int mkfifo_atomic ( const char * path
, mode_t mode
) {
410 _cleanup_free_
char * t
= NULL
;
415 r
= tempfn_random ( path
, NULL
, & t
);
419 if ( mkfifo ( t
, mode
) < 0 )
422 if ( rename ( t
, path
) < 0 ) {
430 int get_files_in_directory ( const char * path
, char *** list
) {
431 _cleanup_closedir_
DIR * d
= NULL
;
432 size_t bufsize
= 0 , n
= 0 ;
433 _cleanup_strv_free_
char ** l
= NULL
;
437 /* Returns all files in a directory in *list, and the number
438 * of files as return value. If list is NULL returns only the
450 if (! de
&& errno
!= 0 )
455 dirent_ensure_type ( d
, de
);
457 if (! dirent_is_file ( de
))
461 /* one extra slot is needed for the terminating NULL */
462 if (! GREEDY_REALLOC ( l
, bufsize
, n
+ 2 ))
465 l
[ n
] = strdup ( de
-> d_name
);
476 l
= NULL
; /* avoid freeing */