diff options
Diffstat (limited to 'libsbutil/src/file.c')
-rw-r--r-- | libsbutil/src/file.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/libsbutil/src/file.c b/libsbutil/src/file.c new file mode 100644 index 0000000..b712d62 --- /dev/null +++ b/libsbutil/src/file.c @@ -0,0 +1,488 @@ +/* + * file.c + * + * Miscellaneous file related macro's and functions. + * + * Copyright 2004-2007 Martin Schlemmer <azarah@nosferatu.za.org> + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Header$ + */ + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> + +#include "rcscripts/rcutil.h" + +bool +rc_file_exists (const char *pathname) +{ + struct stat buf; + int retval; + + if (!check_str (pathname)) + return FALSE; + + retval = lstat (pathname, &buf); + if (-1 != retval) + retval = TRUE; + else + retval = FALSE; + + return retval; +} + +bool +rc_is_file (const char *pathname, bool follow_link) +{ + struct stat buf; + int retval; + + if (!check_str (pathname)) + return FALSE; + + retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + if ((-1 != retval) && (S_ISREG (buf.st_mode))) + retval = TRUE; + else + retval = FALSE; + + return retval; +} + +bool +rc_is_link (const char *pathname) +{ + struct stat buf; + int retval; + + if (!check_str (pathname)) + return FALSE; + + retval = lstat (pathname, &buf); + if ((-1 != retval) && (S_ISLNK (buf.st_mode))) + retval = TRUE; + else + retval = FALSE; + + return retval; +} + +bool +rc_is_dir (const char *pathname, bool follow_link) +{ + struct stat buf; + int retval; + + if (!check_str (pathname)) + return FALSE; + + retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + if ((-1 != retval) && (S_ISDIR (buf.st_mode))) + retval = TRUE; + else + retval = FALSE; + + return retval; +} + +off_t +rc_get_size (const char *pathname, bool follow_link) +{ + struct stat buf; + int retval; + + if (!check_str (pathname)) + return 0; + + retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + if (-1 != retval) + retval = buf.st_size; + else + retval = 0; + + return retval; +} + +time_t +rc_get_mtime (const char *pathname, bool follow_link) +{ + struct stat buf; + int retval; + + if (!check_str (pathname)) + return 0; + + retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf); + if (-1 != retval) + retval = buf.st_mtime; + else + retval = 0; + + return retval; +} + +#if !defined(HAVE_REMOVE) +int +remove (const char *pathname) +{ + int retval; + + if (!check_arg_str (pathname)) + return -1; + + if (rc_is_dir (pathname, FALSE)) + retval = rmdir (pathname); + else + retval = unlink (pathname); + + if (0 != errno) + { + rc_errno_set (errno); + DBG_MSG ("Failed to remove() '%s'!\n", pathname); + } + + return retval; +} +#endif + +int +rc_mktree (const char *pathname, mode_t mode) +{ + char *temp_name = NULL; + char *temp_token = NULL; + char *token_p; + char *token; + int retval; + int lenght; + + if (!check_arg_str (pathname)) + return -1; + + /* Lenght of 'pathname' + extra for "./" if needed */ + lenght = strlen (pathname) + 2; + /* lenght + '\0' */ + temp_name = xmalloc (lenght + 1); + if (NULL == temp_name) + return -1; + + temp_token = xstrndup (pathname, strlen (pathname)); + if (NULL == temp_token) + goto error; + + token_p = temp_token; + + if (pathname[0] == '/') + temp_name[0] = '\0'; + else + /* If not an absolute path, make it local */ + strncpy (temp_name, ".", lenght); + + token = strsep (&token_p, "/"); + /* First token might be "", but that is OK as it will be when the + * pathname starts with '/' */ + while (NULL != token) + { + strncat (temp_name, "/", lenght - strlen (temp_name)); + strncat (temp_name, token, lenght - strlen (temp_name)); + + /* If it does not exist, create the dir. If it does exit, + * but is not a directory, we will catch it below. */ + if (!rc_file_exists (temp_name)) + { + retval = mkdir (temp_name, mode); + if (-1 == retval) + { + rc_errno_set (errno); + DBG_MSG ("Failed to create directory '%s'!\n", temp_name); + goto error; + } + /* Not a directory or symlink pointing to a directory */ + } + else if (!rc_is_dir (temp_name, TRUE)) + { + rc_errno_set (ENOTDIR); + DBG_MSG ("Component in '%s' is not a directory!\n", temp_name); + goto error; + } + + do + { + token = strsep (&token_p, "/"); + /* The first "" was Ok, but rather skip double '/' after that */ + } + while ((NULL != token) && (0 == strlen (token))); + } + + free (temp_name); + free (temp_token); + + return 0; + +error: + free (temp_name); + free (temp_token); + + return -1; +} + +int +rc_rmtree (const char *pathname) +{ + char **dirlist = NULL; + int i = 0; + + if (!check_arg_str (pathname)) + return -1; + + if (!rc_file_exists (pathname)) + { + rc_errno_set (ENOENT); + DBG_MSG ("'%s' does not exist!\n", pathname); + return -1; + } + + if (!rc_is_dir (pathname, FALSE)) + { + rc_errno_set (ENOTDIR); + DBG_MSG ("'%s' is not a directory!\n", pathname); + return -1; + } + + + dirlist = rc_ls_dir (pathname, TRUE, FALSE); + if ((NULL == dirlist) && (rc_errno_is_set ())) + { + /* Do not error out - caller should decide itself if it + * it is an issue */ + DBG_MSG ("Failed to ls_dir() directory '%s'!\n", pathname); + return -1; + } + + while ((NULL != dirlist) && (NULL != dirlist[i])) + { + /* If it is a directory, call rc_rmtree() again with + * it as argument */ + if (rc_is_dir (dirlist[i], FALSE)) + { + if (-1 == rc_rmtree (dirlist[i])) + { + DBG_MSG ("Failed to rm_tree() directory '%s'!\n", dirlist[i]); + goto error; + } + } + + /* Now actually remove it. Note that if it was a directory, + * it should already be removed by above rc_rmtree() call */ + if ((rc_file_exists (dirlist[i]) && (-1 == remove (dirlist[i])))) + { + rc_errno_set (errno); + DBG_MSG ("Failed to remove() '%s'!\n", dirlist[i]); + goto error; + } + i++; + } + + str_list_free (dirlist); + + /* Now remove the parent */ + if (-1 == remove (pathname)) + { + rc_errno_set (errno); + DBG_MSG ("Failed to remove '%s'!\n", pathname); + goto error; + } + + return 0; +error: + str_list_free (dirlist); + + return -1; +} + +char ** +rc_ls_dir (const char *pathname, bool hidden, bool sort) +{ + DIR *dp; + struct dirent *dir_entry; + char **dirlist = NULL; + + if (!check_arg_str (pathname)) + return NULL; + + if (!rc_is_dir (pathname, TRUE)) + { + /* XXX: Should we error here? */ + DBG_MSG ("'%s' is not a directory.\n", pathname); + return NULL; + } + + dp = opendir (pathname); + if (NULL == dp) + { + rc_errno_set (errno); + DBG_MSG ("Failed to opendir() '%s'!\n", pathname); + /* errno will be set by opendir() */ + goto error; + } + + do + { + /* Clear errno to distinguish between EOF and error */ + errno = 0; + dir_entry = readdir (dp); + /* Only an error if 'errno' != 0, else EOF */ + if ((NULL == dir_entry) && (0 != errno)) + { + rc_errno_set (errno); + DBG_MSG ("Failed to readdir() '%s'!\n", pathname); + goto error; + } + if ((NULL != dir_entry) + /* Should we display hidden files? */ + && (hidden ? 1 : dir_entry->d_name[0] != '.')) + { + char *d_name = dir_entry->d_name; + char *str_ptr; + + /* Do not list current or parent entries */ + if ((0 == strcmp (d_name, ".")) || (0 == strcmp (d_name, ".."))) + continue; + + str_ptr = rc_strcatpaths (pathname, d_name); + if (NULL == str_ptr) + { + DBG_MSG ("Failed to allocate buffer!\n"); + goto error; + } + + if (sort) + str_list_add_item_sorted (dirlist, str_ptr, error); + else + str_list_add_item (dirlist, str_ptr, error); + } + } + while (NULL != dir_entry); + + if (!check_strv (dirlist)) + { + if (NULL != dirlist) + str_list_free (dirlist); + + DBG_MSG ("Directory '%s' is empty.\n", pathname); + } + + closedir (dp); + + return dirlist; + +error: + /* Free dirlist on error */ + str_list_free (dirlist); + + if (NULL != dp) + closedir (dp); + + return NULL; +} + + +/* + * Below two functions (rc_file_map and rc_file_unmap) are + * from udev-050 (udev_utils.c). + * (Some are slightly modified, please check udev for originals.) + * + * Copyright 2004 Kay Sievers <kay@vrfy.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +int +rc_file_map (const char *filename, char **buf, size_t * bufsize) +{ + struct stat stats; + int fd; + + fd = open (filename, O_RDONLY); + if (fd < 0) + { + rc_errno_set (errno); + DBG_MSG ("Failed to open file!\n"); + return -1; + } + + if (fstat (fd, &stats) < 0) + { + rc_errno_set (errno); + DBG_MSG ("Failed to stat file!\n"); + + close (fd); + + return -1; + } + + if (0 == stats.st_size) + { + rc_errno_set (EINVAL); + DBG_MSG ("Failed to mmap file with 0 size!\n"); + + close (fd); + + return -1; + } + + *buf = mmap (NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (*buf == MAP_FAILED) + { + rc_errno_set (errno); + DBG_MSG ("Failed to mmap file!\n"); + + close (fd); + + return -1; + } + *bufsize = stats.st_size; + + close (fd); + + return 0; +} + +void +rc_file_unmap (char *buf, size_t bufsize) +{ + munmap (buf, bufsize); +} + |