From 41af4be846bd839e150aa0eff94d98aa878a7b3f Mon Sep 17 00:00:00 2001 From: Marco van Wieringen Date: Tue, 29 Jul 2014 12:47:01 +0200 Subject: [PATCH] Add support for newer APIs The API world hasn't been frozen over the years and POSIX nowadays defines some new APIs which we might want to start exploring. This patch adds support for the following new APIs: fchown()/lchown() replacing the chown call. fchmod()/lchmod() replacing the chmod call. utimes()/futimes()/futimens()/lutimes() replacing the utime call. If those APIs are available they are prefered rather then using the standard chown(), chmod() and utime() APIs. We move away from using utime when possible because POSIX.1-2008 marks utime() as obsolete. --- autoconf/configure.in | 3 +- src/findlib/attribs.c | 286 ++++++++++++++++++++++++++++++++--------- src/findlib/find.h | 3 + src/findlib/find_one.c | 67 ++++++---- src/findlib/mkpath.c | 20 ++- src/findlib/protos.h | 2 +- 6 files changed, 295 insertions(+), 86 deletions(-) diff --git a/autoconf/configure.in b/autoconf/configure.in index db3ed1d8c2d..ea55f373397 100644 --- a/autoconf/configure.in +++ b/autoconf/configure.in @@ -847,7 +847,8 @@ AC_HEADER_STAT AC_HEADER_DIRENT AC_CHECK_HEADER(poll.h, [AC_DEFINE(HAVE_POLL_H, 1, [Define to 1 if you have the header file.])] , ) AC_CHECK_HEADER(sys/poll.h, [AC_DEFINE(HAVE_SYS_POLL_H, 1, [Define to 1 if you have the header file.])] , ) -AC_CHECK_FUNCS(strcasecmp select poll setenv putenv tcgetattr lstat lchown) +AC_CHECK_FUNCS(strcasecmp select poll setenv putenv tcgetattr) +AC_CHECK_FUNCS(lstat lchown lchmod utimes lutimes futimes futimens fchmod fchown) AC_CHECK_FUNCS(nanosleep nl_langinfo) AC_CHECK_HEADERS(varargs.h) diff --git a/src/findlib/attribs.c b/src/findlib/attribs.c index cd8f5974861..f8cfa3704d7 100644 --- a/src/findlib/attribs.c +++ b/src/findlib/attribs.c @@ -43,11 +43,20 @@ static bool set_win32_attributes(JCR *jcr, ATTR *attr, BFILE *ofd); void win_error(JCR *jcr, const char *prefix, POOLMEM *ofile); #endif /* HAVE_WIN32 */ -/* For old systems that don't have lchown() use chown() */ +/* + * For old systems that don't have lchown() use chown() + */ #ifndef HAVE_LCHOWN #define lchown chown #endif +/* + * For old systems that don't have lchmod() use chmod() + */ +#ifndef HAVE_LCHMOD +#define lchmod chmod +#endif + /*=============================================================*/ /* */ /* *** A l l S y s t e m s *** */ @@ -189,6 +198,161 @@ int select_data_stream(FF_PKT *ff_pkt, bool compatible) return stream; } +/* + * Restore all file attributes like owner, mode and file times. + */ +static inline bool restore_file_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) +{ + bool ok = true; + bool suppress_errors; +#if defined(HAVE_FCHOWN) || defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(FUTIMENS) + bool file_is_open; + + /* + * Save if we are working on an open file. + */ + file_is_open = is_bopen(ofd); +#endif + + /* + * See if we want to print errors. + */ + suppress_errors = (debug_level >= 100 || my_uid != 0); + + /* + * Restore owner and group. + */ +#ifdef HAVE_FCHOWN + if (file_is_open) { + if (fchown(ofd->fid, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } + } else { +#else + { +#endif + if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } + } + + /* + * Restore filemode. + */ +#ifdef HAVE_FCHMOD + if (file_is_open) { + if (fchmod(ofd->fid, attr->statp.st_mode) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } + } else { +#else + { +#endif +#if defined(HAVE_WIN32) + if (win32_chmod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) < 0 && !suppress_errors) { +#else + if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) { +#endif + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } + } + + /* + * Reset file times. + */ +#if defined(HAVE_FUTIMES) + if (file_is_open) { + struct timeval restore_times[2]; + + restore_times[0].tv_sec = attr->statp.st_atime; + restore_times[0].tv_usec = 0; + restore_times[1].tv_sec = attr->statp.st_mtime; + restore_times[1].tv_usec = 0; + + if (futimes(ofd->fid, restore_times) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } + } else { +#elif defined(HAVE_FUTIMENS) + if (file_is_open) { + struct timespec restore_times[2]; + + restore_times[0].tv_sec = attr->statp.st_atime; + restore_times[0].tv_nsec = 0; + restore_times[1].tv_sec = attr->statp.st_mtime; + restore_times[1].tv_nsec = 0; + + if (futimens(ofd->fid, restore_times) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } + } else { +#else + { +#endif +#if defined(HAVE_LUTIMES) + struct timeval restore_times[2]; + + restore_times[0].tv_sec = attr->statp.st_atime; + restore_times[0].tv_usec = 0; + restore_times[1].tv_sec = attr->statp.st_mtime; + restore_times[1].tv_usec = 0; + + if (lutimes(attr->ofname, restore_times) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } +#elif defined(HAVE_UTIMES) + struct timeval restore_times[2]; + + restore_times[0].tv_sec = attr->statp.st_atime; + restore_times[0].tv_usec = 0; + restore_times[1].tv_sec = attr->statp.st_mtime; + restore_times[1].tv_usec = 0; + + if (utimes(attr->ofname, restore_times) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } +#else + struct utimbuf restore_times; + + restore_times.actime = attr->statp.st_atime; + restore_times.modtime = attr->statp.st_mtime; + + if (utime(attr->ofname, &restore_times) < 0 && !suppress_errors) { + berrno be; + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } +#endif /* HAVE_LUTIMES */ + } + + return ok; +} + /** * Set file modes, permissions and times * @@ -200,10 +364,9 @@ int select_data_stream(FF_PKT *ff_pkt, bool compatible) */ bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) { - struct utimbuf ut; mode_t old_mask; bool ok = true; - boffset_t fsize; + bool suppress_errors; if (uid_set) { my_uid = getuid(); @@ -211,6 +374,11 @@ bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) uid_set = true; } + /* + * See if we want to print errors. + */ + suppress_errors = (debug_level >= 100 || my_uid != 0); + #if defined(HAVE_WIN32) if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX && set_win32_attributes(jcr, attr, ofd)) { @@ -220,6 +388,7 @@ bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) pm_strcpy(attr->ofname, "*none*"); return true; } + if (attr->data_stream == STREAM_WIN32_DATA || attr->data_stream == STREAM_WIN32_GZIP_DATA || attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) { @@ -231,97 +400,98 @@ bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd) } /** - * If Windows stuff failed, e.g. attempt to restore Unix file - * to Windows, simply fall through and we will do it the - * universal way. + * If Windows stuff failed, e.g. attempt to restore Unix file to Windows, simply fall + * through and we will do it the universal way. */ #endif old_mask = umask(0); if (is_bopen(ofd)) { + boffset_t fsize; char ec1[50], ec2[50]; + fsize = blseek(ofd, 0, SEEK_END); - bclose(ofd); /* first close file */ - if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 && - fsize != (boffset_t)attr->statp.st_size) { + if (attr->type == FT_REG && + fsize > 0 && + attr->statp.st_size > 0 && + fsize != (boffset_t)attr->statp.st_size) { Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"), - attr->ofname, edit_uint64(attr->statp.st_size, ec1), - edit_uint64(fsize, ec2)); + attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(fsize, ec2)); + } + } else { + struct stat st; + char ec1[50], ec2[50]; + + if (lstat(attr->ofname, &st) == 0) { + if (attr->type == FT_REG && + st.st_size > 0 && + attr->statp.st_size > 0 && + st.st_size != attr->statp.st_size) { + Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"), + attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(st.st_size, ec2)); + } } } /** - * We do not restore sockets, so skip trying to restore their - * attributes. + * We do not restore sockets, so skip trying to restore their attributes. */ if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) { goto bail_out; } - ut.actime = attr->statp.st_atime; - ut.modtime = attr->statp.st_mtime; - /* ***FIXME**** optimize -- don't do if already correct */ /** - * For link, change owner of link using lchown, but don't - * try to do a chmod as that will update the file behind it. + * For link, change owner of link using lchown, but don't try to do a chmod as that will update the file behind it. */ if (attr->type == FT_LNK) { - /** Change owner of link, not of real file */ - if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) { - berrno be; - Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), - attr->ofname, be.bstrerror()); - ok = false; - } - } else { - if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) { + /* + * Change owner of link, not of real file + */ + if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) { berrno be; - Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), - attr->ofname, be.bstrerror()); - ok = false; - } -#if defined(HAVE_WIN32) - if (win32_chmod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) < 0 && my_uid == 0) { -#else - if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) { -#endif - berrno be; - Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), - attr->ofname, be.bstrerror()); + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } - /** - * Reset file times. - */ - if (utime(attr->ofname, &ut) < 0 && my_uid == 0) { +#ifdef HAVE_LCHMOD + if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) { berrno be; - Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), - attr->ofname, be.bstrerror()); + + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror()); ok = false; } +#endif + } else { + if (!ofd->cmd_plugin) { + ok = restore_file_attributes(jcr, attr, ofd); + #ifdef HAVE_CHFLAGS - /** - * FreeBSD user flags - * - * Note, this should really be done before the utime() above, - * but if the immutable bit is set, it will make the utimes() - * fail. - */ - if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) { - berrno be; - Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"), - attr->ofname, be.bstrerror()); - ok = false; - } + /** + * FreeBSD user flags + * + * Note, this should really be done before the utime() above, + * but if the immutable bit is set, it will make the utimes() + * fail. + */ + if (chflags(attr->ofname, attr->statp.st_flags) < 0 && !suppress_errors) { + berrno be; + Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"), attr->ofname, be.bstrerror()); + ok = false; + } #endif + } } bail_out: + if (is_bopen(ofd)) { + bclose(ofd); + } + pm_strcpy(attr->ofname, "*none*"); umask(old_mask); + return ok; } diff --git a/src/findlib/find.h b/src/findlib/find.h index 724cbb16c73..462b7988ddf 100644 --- a/src/findlib/find.h +++ b/src/findlib/find.h @@ -43,6 +43,8 @@ #if !defined(HAVE_WIN32) || defined(HAVE_MINGW) #include #endif + +#if !defined(HAVE_UTIMES) && !defined(HAVE_LUTIMES) #if HAVE_UTIME_H #include #else @@ -51,6 +53,7 @@ struct utimbuf { long modtime; }; #endif +#endif #define MODE_RALL (S_IRUSR|S_IRGRP|S_IROTH) diff --git a/src/findlib/find_one.c b/src/findlib/find_one.c index 8cbe4654f61..f7c784ebef6 100644 --- a/src/findlib/find_one.c +++ b/src/findlib/find_one.c @@ -404,6 +404,39 @@ void ff_pkt_set_link_digest(FF_PKT *ff_pkt, int32_t digest_stream, } } +/* + * Restore file times. + */ +static inline void restore_file_times(FF_PKT *ff_pkt, char *fname) +{ +#if defined(HAVE_LUTIMES) + struct timeval restore_times[2]; + + restore_times[0].tv_sec = ff_pkt->statp.st_atime; + restore_times[0].tv_usec = 0; + restore_times[1].tv_sec = ff_pkt->statp.st_mtime; + restore_times[1].tv_usec = 0; + + lutimes(fname, restore_times); +#elif defined(HAVE_UTIMES) + struct timeval restore_times[2]; + + restore_times[0].tv_sec = ff_pkt->statp.st_atime; + restore_times[0].tv_usec = 0; + restore_times[1].tv_sec = ff_pkt->statp.st_mtime; + restore_times[1].tv_usec = 0; + + utimes(fname, restore_times); +#else + struct utimbuf restore_times; + + restore_times.actime = ff_pkt->statp.st_atime; + restore_times.modtime = ff_pkt->statp.st_mtime; + + utime(fname, &restore_times); +#endif +} + #ifdef HAVE_DARWIN_OS /* * Handling of a HFS+ attributes. @@ -503,8 +536,7 @@ static inline int process_hardlink(JCR *jcr, FF_PKT *ff_pkt, */ static inline int process_regular_file(JCR *jcr, FF_PKT *ff_pkt, int handle_file(JCR *jcr, FF_PKT *ff, bool top_level), - char *fname, bool top_level, - struct utimbuf *restore_times) + char *fname, bool top_level) { int rtn_stat; boffset_t sizeleft; @@ -528,7 +560,7 @@ static inline int process_regular_file(JCR *jcr, FF_PKT *ff_pkt, Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex, ff_pkt->linked ? 1 : 0, fname); if (ff_pkt->flags & FO_KEEPATIME) { - utime(fname, restore_times); + restore_file_times(ff_pkt, fname); } return rtn_stat; } @@ -572,8 +604,7 @@ static inline int process_symlink(JCR *jcr, FF_PKT *ff_pkt, */ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, int handle_file(JCR *jcr, FF_PKT *ff, bool top_level), - char *fname, dev_t parent_device, bool top_level, - struct utimbuf *restore_times) + char *fname, dev_t parent_device, bool top_level) { int rtn_stat; DIR *directory; @@ -702,7 +733,7 @@ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, free_dir_ff_pkt(dir_ff_pkt); ff_pkt->link = ff_pkt->fname; /* reset "link" */ if (ff_pkt->flags & FO_KEEPATIME) { - utime(fname, restore_times); + restore_file_times(ff_pkt, fname); } return rtn_stat; } @@ -805,7 +836,7 @@ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, free_dir_ff_pkt(dir_ff_pkt); if (ff_pkt->flags & FO_KEEPATIME) { - utime(fname, restore_times); + restore_file_times(ff_pkt, fname); } ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */ return rtn_stat; @@ -858,14 +889,12 @@ static inline int process_special_file(JCR *jcr, FF_PKT *ff_pkt, /* * See if we need to perform any processing for a given file. */ -static inline int needs_processing(JCR *jcr, FF_PKT *ff_pkt, - char *fname, - struct utimbuf *restore_times) +static inline int needs_processing(JCR *jcr, FF_PKT *ff_pkt, char *fname) { if (!accept_fstype(ff_pkt, NULL)) { ff_pkt->type = FT_INVALIDFS; if (ff_pkt->flags & FO_KEEPATIME) { - utime(fname, restore_times); + restore_file_times(ff_pkt, fname); } char fs[100]; @@ -880,7 +909,7 @@ static inline int needs_processing(JCR *jcr, FF_PKT *ff_pkt, if (!accept_drivetype(ff_pkt, NULL)) { ff_pkt->type = FT_INVALIDDT; if (ff_pkt->flags & FO_KEEPATIME) { - utime(fname, restore_times); + restore_file_times(ff_pkt, fname); } char dt[100]; @@ -911,7 +940,6 @@ int find_one_file(JCR *jcr, FF_PKT *ff_pkt, { int rtn_stat; bool done = false; - struct utimbuf restore_times; ff_pkt->fname = ff_pkt->link = fname; if (lstat(fname, &ff_pkt->statp) != 0) { @@ -925,18 +953,11 @@ int find_one_file(JCR *jcr, FF_PKT *ff_pkt, Dmsg1(300, "File ----: %s\n", fname); - /* - * Save current times of this directory in case we need to - * reset them because the user doesn't want them changed. - */ - restore_times.actime = ff_pkt->statp.st_atime; - restore_times.modtime = ff_pkt->statp.st_mtime; - /* * We check for allowed fstypes and drivetypes at top_level and fstype change (below). */ if (top_level) { - if (needs_processing(jcr, ff_pkt, fname, &restore_times) == 1) + if (needs_processing(jcr, ff_pkt, fname) == 1) return 1; } @@ -1025,13 +1046,13 @@ int find_one_file(JCR *jcr, FF_PKT *ff_pkt, */ switch (ff_pkt->statp.st_mode & S_IFMT) { case S_IFREG: - return process_regular_file(jcr, ff_pkt, handle_file, fname, top_level, &restore_times); + return process_regular_file(jcr, ff_pkt, handle_file, fname, top_level); #ifdef S_IFLNK case S_IFLNK: return process_symlink(jcr, ff_pkt, handle_file, fname, top_level); #endif case S_IFDIR: - return process_directory(jcr, ff_pkt, handle_file, fname, parent_device, top_level, &restore_times); + return process_directory(jcr, ff_pkt, handle_file, fname, parent_device, top_level); default: return process_special_file(jcr, ff_pkt, handle_file, fname, top_level); } diff --git a/src/findlib/mkpath.c b/src/findlib/mkpath.c index f55f2066b5f..e73ffb90302 100644 --- a/src/findlib/mkpath.c +++ b/src/findlib/mkpath.c @@ -36,6 +36,20 @@ #define dbglvl 50 +/* + * * For old systems that don't have lchown() use chown() + * */ +#ifndef HAVE_LCHOWN +#define lchown chown +#endif + +/* + * * For old systems that don't have lchmod() use chmod() + * */ +#ifndef HAVE_LCHMOD +#define lchmod chmod +#endif + typedef struct PrivateCurDir { hlink link; char fname[1]; @@ -147,7 +161,7 @@ static bool makedir(JCR *jcr, char *path, mode_t mode, int *created) static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t mode) { - if (chown(path, owner, group) != 0 && attr->uid == 0 + if (lchown(path, owner, group) != 0 && attr->uid == 0 #ifdef AFS && errno != EPERM #endif @@ -159,7 +173,7 @@ static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t #if defined(HAVE_WIN32) if (win32_chmod(path, mode, 0) != 0 && attr->uid == 0) { #else - if (chmod(path, mode) != 0 && attr->uid == 0) { + if (lchmod(path, mode) != 0 && attr->uid == 0) { #endif berrno be; Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"), @@ -178,7 +192,7 @@ static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t * keep_dir_modes if set means don't change mode bits if dir exists */ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, - uid_t owner, gid_t group, int keep_dir_modes) + uid_t owner, gid_t group, bool keep_dir_modes) { struct stat statp; mode_t omask, tmode; diff --git a/src/findlib/protos.h b/src/findlib/protos.h index ec6874ed774..8989f2a11eb 100644 --- a/src/findlib/protos.h +++ b/src/findlib/protos.h @@ -85,7 +85,7 @@ int enable_backup_privileges(JCR *jcr, int ignore_errors); /* makepath.c */ bool makepath(ATTR *attr, const char *path, mode_t mode, mode_t parent_mode, uid_t owner, gid_t group, - int keep_dir_modes); + bool keep_dir_modes); void free_path_list(JCR *jcr); bool path_list_lookup(JCR *jcr, char *fname); bool path_list_add(JCR *jcr, uint32_t len, char *fname);