| /* source: xio-named.c */ |
| /* Copyright Gerhard Rieger and contributors (see file CHANGES) */ |
| /* Published under the GNU General Public License V.2, see file COPYING */ |
| |
| /* this file contains the source for filesystem entry functions */ |
| |
| #include "xiosysincludes.h" |
| |
| #if _WITH_NAMED |
| |
| #include "xioopen.h" |
| #include "xio-named.h" |
| |
| |
| #if WITH_NAMED |
| const struct optdesc opt_group_early = { "group-early", NULL, OPT_GROUP_EARLY, GROUP_NAMED, PH_PREOPEN, TYPE_GIDT, OFUNC_SPEC }; |
| const struct optdesc opt_perm_early = { "perm-early", NULL, OPT_PERM_EARLY, GROUP_NAMED, PH_PREOPEN, TYPE_MODET,OFUNC_SPEC }; |
| const struct optdesc opt_user_early = { "user-early", NULL, OPT_USER_EARLY, GROUP_NAMED, PH_PREOPEN, TYPE_UIDT, OFUNC_SPEC }; |
| /*0 const struct optdesc opt_force = { "force", NULL, OPT_FORCE, GROUP_NAMED, PH_???, TYPE_BOOL, OFUNC_SPEC };*/ |
| const struct optdesc opt_unlink = { "unlink", NULL, OPT_UNLINK, GROUP_NAMED, PH_PREOPEN, TYPE_BOOL, OFUNC_SPEC }; |
| const struct optdesc opt_unlink_early= { "unlink-early",NULL, OPT_UNLINK_EARLY,GROUP_NAMED, PH_EARLY, TYPE_BOOL, OFUNC_SPEC }; |
| const struct optdesc opt_unlink_late = { "unlink-late", NULL, OPT_UNLINK_LATE, GROUP_NAMED, PH_PASTOPEN, TYPE_BOOL, OFUNC_SPEC }; |
| const struct optdesc opt_unlink_close = { "unlink-close", NULL, OPT_UNLINK_CLOSE, GROUP_NAMED, PH_LATE, TYPE_BOOL, OFUNC_SPEC }; |
| #endif /* WITH_NAMED */ |
| |
| /* applies to filesystem entry all options belonging to phase */ |
| int applyopts_named(const char *filename, struct opt *opts, unsigned int phase) { |
| struct opt *opt; |
| |
| if (!opts) return 0; |
| |
| opt = opts; while (opt->desc != ODESC_END) { |
| if (opt->desc == ODESC_DONE || |
| opt->desc->phase != phase && phase != PH_ALL || |
| !(opt->desc->group & GROUP_NAMED)) { |
| ++opt; continue; } |
| switch (opt->desc->optcode) { |
| case OPT_GROUP_EARLY: |
| case OPT_GROUP: |
| if (Chown(filename, -1, opt->value.u_gidt) < 0) { |
| Error3("chown(\"%s\", -1, "F_gid"): %s", filename, |
| opt->value.u_gidt, strerror(errno)); |
| } |
| break; |
| case OPT_USER_EARLY: |
| case OPT_USER: |
| if (Chown(filename, opt->value.u_uidt, -1) < 0) { |
| Error3("chown(\"%s\", "F_uid", -1): %s", filename, |
| opt->value.u_uidt, strerror(errno)); |
| } |
| break; |
| case OPT_PERM_EARLY: |
| case OPT_PERM: |
| if (Chmod(filename, opt->value.u_modet) < 0) { |
| Error3("chmod(\"%s\", "F_mode"): %s", |
| filename, opt->value.u_modet, strerror(errno)); |
| } |
| break; |
| case OPT_UNLINK_EARLY: |
| case OPT_UNLINK: |
| xio_unlink(filename, E_ERROR); |
| break; |
| case OPT_UNLINK_LATE: |
| if (Unlink(filename) < 0) { |
| if (errno == ENOENT) { |
| /* We have just created/opened it, that's - surprising! */ |
| Warn2("unlink(\"%s\"): %s", filename, strerror(errno)); |
| } else { |
| Error2("unlink(\"%s\"): %s", filename, strerror(errno)); |
| } |
| } |
| break; |
| default: Error1("applyopts_named(): option \"%s\" not implemented", |
| opt->desc->defname); |
| break; |
| } |
| opt->desc = ODESC_DONE; |
| ++opt; |
| } |
| return 0; |
| } |
| |
| |
| /* perform actions that are common to all NAMED group addresses: checking if |
| the entry exists, parsing options, ev.removing old filesystem entry or |
| setting early owners and permissions. |
| It applies options of PH_EARLY and PH_PREOPEN. |
| If the path exists, its st_mode field is returned. |
| After this sub you may proceed with open() or whatever... |
| */ |
| int _xioopen_named_early( |
| int argc, |
| const char *argv[], |
| xiofile_t *xfd, |
| groups_t groups, |
| bool *exists, |
| struct opt *opts, |
| const char *syntax) |
| { |
| const char *path = argv[1]; |
| struct single *sfd = &xfd->stream; |
| #if HAVE_STAT64 |
| struct stat64 statbuf; |
| #else |
| struct stat statbuf; |
| #endif /* !HAVE_STAT64 */ |
| bool opt_unlink_early = false; |
| |
| if (argc != 2) { |
| xio_syntax(argv[0], 1, argc-1, syntax); |
| return STAT_NORETRY; |
| } |
| statbuf.st_mode = 0; |
| /* find the appropriate groupbits */ |
| if ( |
| #if HAVE_STAT64 |
| Stat64(path, &statbuf) < 0 |
| #else |
| Stat(path, &statbuf) < 0 |
| #endif /* !HAVE_STAT64 */ |
| ) { |
| if (errno != ENOENT) { |
| Error2("stat(\"%s\"): %s", path, strerror(errno)); |
| return STAT_RETRYLATER; |
| } |
| *exists = false; |
| } else { |
| *exists = true; |
| } |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) |
| return -1; |
| applyopts(sfd, -1, opts, PH_INIT); |
| |
| retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); |
| if (*exists && opt_unlink_early) { |
| Info1("\"%s\" already exists; removing it", path); |
| if (Unlink(path) < 0) { |
| Error2("unlink(\"%s\"): %s", path, strerror(errno)); |
| } else { |
| *exists = false; |
| } |
| } |
| |
| applyopts_named(path, opts, PH_EARLY); |
| applyopts(sfd, -1, opts, PH_EARLY); |
| if (*exists) { |
| applyopts_named(path, opts, PH_PREOPEN); |
| } else { |
| dropopts(opts, PH_PREOPEN); |
| } |
| |
| return statbuf.st_mode; |
| } |
| |
| |
| /* retrieve the OPEN group options and perform the open() call. |
| returns the file descriptor or a negative value. |
| Applies options of phases PREOPEN, OPEN, PASTOPEN, and FD |
| */ |
| int _xioopen_open(const char *path, int rw, struct opt *opts) { |
| mode_t mode = 0666; |
| flags_t flags = rw; |
| bool flag; |
| int fd; |
| |
| applyopts_named(path, opts, PH_PREOPEN); |
| |
| /* this only applies pure OPEN flags, not mixed OPEN/FCNTL options */ |
| applyopts_flags(opts, GROUP_OPEN, &flags); |
| |
| /* we have to handle mixed OPEN/FCNTL flags specially */ |
| if (retropt_bool(opts, OPT_O_APPEND, &flag) >= 0 && flag) |
| flags |= O_APPEND; |
| if (retropt_bool(opts, OPT_O_NONBLOCK, &flag) >= 0 && flag) |
| flags |= O_NONBLOCK; |
| #ifdef O_ASYNC |
| if (retropt_bool(opts, OPT_O_ASYNC, &flag) >= 0 && flag) |
| flags |= O_ASYNC; |
| #endif |
| if (retropt_bool(opts, OPT_O_TRUNC, &flag) >= 0 && flag) |
| flags |= O_TRUNC; |
| #ifdef O_BINARY |
| if (retropt_bool(opts, OPT_O_BINARY, &flag) >= 0 && flag) |
| flags |= O_BINARY; |
| #endif |
| #ifdef O_TEXT |
| if (retropt_bool(opts, OPT_O_TEXT, &flag) >= 0 && flag) |
| flags |= O_TEXT; |
| #endif |
| #ifdef O_NOINHERIT |
| if (retropt_bool(opts, OPT_O_NOINHERIT, &flag) >= 0 && flag) |
| flags |= O_NOINHERIT; |
| #endif |
| #ifdef O_NOATIME |
| if (retropt_bool(opts, OPT_O_NOATIME, &flag) >= 0 && flag) |
| flags |= O_NOATIME; |
| #endif |
| |
| retropt_modet(opts, OPT_PERM, &mode); |
| |
| do { |
| fd = Open(path, flags, mode); |
| } while (fd < 0 && errno == EINTR); |
| if (fd < 0) { |
| Error4("open(\"%s\", 0%lo, 0%03o): %s", |
| path, flags, mode, strerror(errno)); |
| return STAT_RETRYLATER; |
| } |
| /*0 Info4("open(\"%s\", 0%o, 0%03o) -> %d", path, flags, mode, fd);*/ |
| applyopts_named(path, opts, PH_PASTOPEN); |
| #if 0 |
| applyopts_named(path, opts, PH_FD); |
| applyopts(fd, opts, PH_FD); |
| applyopts_cloexec(fd, opts); |
| #endif |
| return fd; |
| } |
| |
| /* Wrapper around Unlink() that handles the case of non existing file (ENOENT) |
| just as E_INFO. All other errors are handled with level. */ |
| int xio_unlink( |
| const char *filename, /* the file to be removed */ |
| int level) /* the severity level for other errors, e.g.E_ERROR */ |
| { |
| int _errno; |
| |
| if (Unlink(filename) < 0) { |
| _errno = errno; |
| if (errno == ENOENT) { |
| Info2("unlink(\"%s\"): %s", filename, strerror(errno)); |
| } else { |
| Msg2(level, "unlink(\"%s\"): %s", filename, strerror(errno)); |
| errno = _errno; |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| #endif /* _WITH_NAMED */ |