diff options
Diffstat (limited to '')
239 files changed, 13843 insertions, 1516 deletions
diff --git a/lib/buffer.c b/lib/buffer.c index fda2fc8..cc863fa 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -11,7 +11,7 @@ void ul_buffer_reset_data(struct ul_buffer *buf) { if (buf->begin) - buf->begin[0] = '\0'; + memset(buf->begin, 0, buf->sz); buf->end = buf->begin; if (buf->ptrs && buf->nptrs) @@ -49,7 +49,7 @@ int ul_buffer_is_empty(struct ul_buffer *buf) int ul_buffer_save_pointer(struct ul_buffer *buf, unsigned short ptr_idx) { if (ptr_idx >= buf->nptrs) { - char **tmp = realloc(buf->ptrs, (ptr_idx + 1) * sizeof(char *)); + char **tmp = reallocarray(buf->ptrs, ptr_idx + 1, sizeof(char *)); if (!tmp) return -EINVAL; @@ -134,12 +134,11 @@ int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz) if (!buf) return -EINVAL; - if (!data || !*data) + if (!data) return 0; if (buf->begin && buf->end) maxsz = buf->sz - (buf->end - buf->begin); - if (maxsz <= sz + 1) { int rc = ul_buffer_alloc_data(buf, buf->sz + sz + 1); if (rc) diff --git a/lib/c_strtod.c b/lib/c_strtod.c index d25ee27..d7d8cbe 100644 --- a/lib/c_strtod.c +++ b/lib/c_strtod.c @@ -50,10 +50,10 @@ double c_strtod(char const *str, char **end) return strtod_l(str, end, cl); #elif defined(HAVE_USELOCALE) /* - * B) classic strtod(), but switch to "C" locale by uselocal() + * B) classic strtod(), but switch to "C" locale by uselocale() */ if (cl) { - locale_t org_cl = uselocale(locale); + locale_t org_cl = uselocale(cl); if (!org_cl) return 0; diff --git a/lib/caputils.c b/lib/caputils.c index 987533a..23866c0 100644 --- a/lib/caputils.c +++ b/lib/caputils.c @@ -24,6 +24,7 @@ #include "caputils.h" #include "pathnames.h" #include "procfs.h" +#include "nls.h" static int test_cap(unsigned int cap) { @@ -87,6 +88,43 @@ int cap_last_cap(void) return cap; } +void cap_permitted_to_ambient(void) +{ + /* We use capabilities system calls to propagate the permitted + * capabilities into the ambient set because we may have + * already forked so be in async-signal-safe context. */ + struct __user_cap_header_struct header = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = 0, + }; + struct __user_cap_data_struct payload[_LINUX_CAPABILITY_U32S_3] = {{ 0 }}; + uint64_t effective, cap; + + if (capget(&header, payload) < 0) + err(EXIT_FAILURE, _("capget failed")); + + /* In order the make capabilities ambient, we first need to ensure + * that they are all inheritable. */ + payload[0].inheritable = payload[0].permitted; + payload[1].inheritable = payload[1].permitted; + + if (capset(&header, payload) < 0) + err(EXIT_FAILURE, _("capset failed")); + + effective = ((uint64_t)payload[1].effective << 32) | (uint64_t)payload[0].effective; + + for (cap = 0; cap < (sizeof(effective) * 8); cap++) { + /* This is the same check as cap_valid(), but using + * the runtime value for the last valid cap. */ + if (cap > (uint64_t) cap_last_cap()) + continue; + + if ((effective & (1ULL << cap)) + && prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0) + err(EXIT_FAILURE, _("prctl(PR_CAP_AMBIENT) failed")); + } +} + #ifdef TEST_PROGRAM_CAPUTILS int main(int argc, char *argv[]) { diff --git a/lib/colors.c b/lib/colors.c index 532e339..0928770 100644 --- a/lib/colors.c +++ b/lib/colors.c @@ -357,12 +357,10 @@ static char *colors_get_homedir(char *buf, size_t bufsz) /* * Adds one color sequence to array with color scheme. - * When returning success (0) this function takes ownership of - * @seq and @name, which have to be allocated strings. */ static int colors_add_scheme(struct ul_color_ctl *cc, - char *name, - char *seq0) + const char *name, + const char *seq0) { struct ul_color_scheme *cs = NULL; char *seq = NULL; @@ -380,8 +378,8 @@ static int colors_add_scheme(struct ul_color_ctl *cc, /* enlarge the array */ if (cc->nschemes == cc->schemes_sz) { - void *tmp = realloc(cc->schemes, (cc->nschemes + 10) - * sizeof(struct ul_color_scheme)); + void *tmp = reallocarray(cc->schemes, cc->nschemes + 10, + sizeof(struct ul_color_scheme)); if (!tmp) goto err; cc->schemes = tmp; diff --git a/lib/cpuset.c b/lib/cpuset.c index 098b8e5..533b8ab 100644 --- a/lib/cpuset.c +++ b/lib/cpuset.c @@ -287,7 +287,7 @@ static int nextnumber(const char *str, char **end, unsigned int *result) */ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail) { - size_t max = cpuset_nbits(setsize); + const size_t max = cpuset_nbits(setsize); const char *p, *q; char *end = NULL; @@ -326,8 +326,12 @@ int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail) if (!(a <= b)) return 1; while (a <= b) { - if (fail && (a >= max)) - return 2; + if (a >= max) { + if (fail) + return 2; + else + break; + } CPU_SET_S(a, setsize, set); a += s; } @@ -159,7 +159,7 @@ void __sanitize_env(struct ul_env_list **org) if (strncmp(*cur, *bad, strlen(*bad)) == 0) { if (org) *org = env_list_add(*org, *cur); - last = remote_entry(envp, cur - envp, last); + last = remove_entry(envp, cur - envp, last); cur--; break; } @@ -174,7 +174,7 @@ void __sanitize_env(struct ul_env_list **org) continue; /* OK */ if (org) *org = env_list_add(*org, *cur); - last = remote_entry(envp, cur - envp, last); + last = remove_entry(envp, cur - envp, last); cur--; break; } diff --git a/lib/exec_shell.c b/lib/exec_shell.c index 6fef6c7..96d3e95 100644 --- a/lib/exec_shell.c +++ b/lib/exec_shell.c @@ -42,9 +42,7 @@ void __attribute__((__noreturn__)) exec_shell(void) shellc = xstrdup(shell); shell_basename = basename(shellc); - arg0 = xmalloc(strlen(shell_basename) + 2); - arg0[0] = '-'; - strcpy(arg0 + 1, shell_basename); + xasprintf(&arg0, "-%s", shell_basename); execl(shell, arg0, (char *)NULL); errexec(shell); diff --git a/lib/fileeq.c b/lib/fileeq.c index af5b4e3..2a74af8 100644 --- a/lib/fileeq.c +++ b/lib/fileeq.c @@ -79,9 +79,9 @@ enum { }; struct ul_fileeq_method { - int id; const char *name; /* name used by applications */ const char *kname; /* name used by kernel crypto */ + int id; short digsiz; }; diff --git a/lib/jsonwrt.c b/lib/jsonwrt.c index 8ca1d4d..243ed82 100644 --- a/lib/jsonwrt.c +++ b/lib/jsonwrt.c @@ -26,12 +26,12 @@ * } * } */ -static void fputs_quoted_case_json(const char *data, FILE *out, int dir) +static void fputs_quoted_case_json(const char *data, FILE *out, int dir, size_t size) { const char *p; fputc('"', out); - for (p = data; p && *p; p++) { + for (p = data; p && *p && (!size || p < data + size); p++) { const unsigned int c = (unsigned int) *p; @@ -98,9 +98,9 @@ static void fputs_quoted_case_json(const char *data, FILE *out, int dir) fputc('"', out); } -#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0) -#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1) -#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1) +#define fputs_quoted_json(_d, _o) fputs_quoted_case_json(_d, _o, 0, 0) +#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0) +#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1, 0) void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent) { @@ -154,12 +154,6 @@ void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type) void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) { - if (fmt->indent == 1) { - fputs("\n}\n", fmt->out); - fmt->indent--; - fmt->after_close = 1; - return; - } assert(fmt->indent > 0); switch (type) { @@ -168,6 +162,8 @@ void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type) fputc('\n', fmt->out); ul_jsonwrt_indent(fmt); fputs("}", fmt->out); + if (fmt->indent == 0) + fputs("\n", fmt->out); break; case UL_JSON_ARRAY: fmt->indent--; @@ -204,6 +200,17 @@ void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt, ul_jsonwrt_value_close(fmt); } +void ul_jsonwrt_value_s_sized(struct ul_jsonwrt *fmt, + const char *name, const char *data, size_t size) +{ + ul_jsonwrt_value_open(fmt, name); + if (data && *data) + fputs_quoted_case_json(data, fmt->out, 0, size); + else + fputs("null", fmt->out); + ul_jsonwrt_value_close(fmt); +} + void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, const char *name, uint64_t data) { @@ -212,6 +219,14 @@ void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt, ul_jsonwrt_value_close(fmt); } +void ul_jsonwrt_value_double(struct ul_jsonwrt *fmt, + const char *name, long double data) +{ + ul_jsonwrt_value_open(fmt, name); + fprintf(fmt->out, "%Lg", data); + ul_jsonwrt_value_close(fmt); +} + void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt, const char *name, int data) { diff --git a/lib/loopdev.c b/lib/loopdev.c index dd9ead3..c72fb2c 100644 --- a/lib/loopdev.c +++ b/lib/loopdev.c @@ -116,7 +116,9 @@ int loopcxt_set_device(struct loopdev_cxt *lc, const char *device) DBG(CXT, ul_debugobj(lc, "closing old open fd")); } lc->fd = -1; - lc->mode = 0; + lc->is_lost = 0; + lc->devno = 0; + lc->mode = O_RDONLY; lc->blocksize = 0; lc->has_info = 0; lc->info_failed = 0; @@ -153,6 +155,28 @@ int loopcxt_has_device(struct loopdev_cxt *lc) return lc && *lc->device; } +dev_t loopcxt_get_devno(struct loopdev_cxt *lc) +{ + if (!lc || !loopcxt_has_device(lc)) + return 0; + if (!lc->devno) + lc->devno = sysfs_devname_to_devno(lc->device); + return lc->devno; +} + +int loopcxt_is_lost(struct loopdev_cxt *lc) +{ + if (!lc || !loopcxt_has_device(lc)) + return 0; + if (lc->is_lost) + return 1; + + lc->is_lost = access(lc->device, F_OK) != 0 + && loopcxt_get_devno(lc) != 0; + + return lc->is_lost; +} + /* * @lc: context * @flags: LOOPDEV_FL_* flags @@ -165,12 +189,6 @@ int loopcxt_has_device(struct loopdev_cxt *lc) * * * LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls * - * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2) - * syscall to open loop device. By default is the device open read-only. - * - * The exception is loopcxt_setup_device(), where the device is open read-write - * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()). - * * Returns: <0 on error, 0 on success. */ int loopcxt_init(struct loopdev_cxt *lc, int flags) @@ -271,7 +289,7 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc) return NULL; if (!lc->sysfs) { - dev_t devno = sysfs_devname_to_devno(lc->device); + dev_t devno = loopcxt_get_devno(lc); if (!devno) { DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno")); return NULL; @@ -285,28 +303,50 @@ static struct path_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc) return lc->sysfs; } -/* - * @lc: context - * - * Returns: file descriptor to the open loop device or <0 on error. The mode - * depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is - * read-only. - */ -int loopcxt_get_fd(struct loopdev_cxt *lc) +static int __loopcxt_get_fd(struct loopdev_cxt *lc, mode_t mode) { + int old = -1; + if (!lc || !*lc->device) return -EINVAL; + /* It's okay to return a FD with read-write permissions if someone + * asked for read-only, but you shouldn't do the opposite. + * + * (O_RDONLY is a widely usable default.) + */ + if (lc->fd >= 0 && mode == O_RDWR && lc->mode == O_RDONLY) { + DBG(CXT, ul_debugobj(lc, "closing already open device (mode mismatch)")); + old = lc->fd; + lc->fd = -1; + } + if (lc->fd < 0) { - lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY; + lc->mode = mode; lc->fd = open(lc->device, lc->mode | O_CLOEXEC); DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device, - lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro")); + mode == O_RDONLY ? "ro" : + mode == O_RDWR ? "rw" : "??")); + + if (lc->fd < 0 && old >= 0) { + /* restore original on error */ + lc->fd = old; + old = -1; + } } + + if (old >= 0) + close(old); return lc->fd; } -int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode) +/* default is read-only file descriptor, it's enough for all ioctls */ +int loopcxt_get_fd(struct loopdev_cxt *lc) +{ + return __loopcxt_get_fd(lc, O_RDONLY); +} + +int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, mode_t mode) { if (!lc) return -EINVAL; @@ -400,13 +440,6 @@ static int loopiter_set_device(struct loopdev_cxt *lc, const char *device) !(lc->iter.flags & LOOPITER_FL_FREE)) return 0; /* caller does not care about device status */ - if (!is_loopdev(lc->device)) { - DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device)); - return -errno; - } - - DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device)); - used = loopcxt_get_offset(lc, NULL) == 0; if ((lc->iter.flags & LOOPITER_FL_USED) && used) @@ -479,7 +512,7 @@ static int loop_scandir(const char *dirname, int **ary, int hasprefix) arylen += 1; - tmp = realloc(*ary, arylen * sizeof(int)); + tmp = reallocarray(*ary, arylen, sizeof(int)); if (!tmp) { free(*ary); *ary = NULL; @@ -749,6 +782,26 @@ char *loopcxt_get_backing_file(struct loopdev_cxt *lc) /* * @lc: context + * + * Returns (allocated) string with loop reference. The same as backing file by + * default. + */ +char *loopcxt_get_refname(struct loopdev_cxt *lc) +{ + char *res = NULL; + struct loop_info64 *lo = loopcxt_get_info(lc); + + if (lo) { + lo->lo_file_name[LO_NAME_SIZE - 1] = '\0'; + res = strdup((char *) lo->lo_file_name); + } + + DBG(CXT, ul_debugobj(lc, "get_refname [%s]", res)); + return res; +} + +/* + * @lc: context * @offset: returns offset number for the given device * * Returns: <0 on error, 0 on success @@ -840,7 +893,7 @@ int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size) /* * @lc: context - * @devno: returns encryption type + * @type: returns encryption type * * Cryptoloop is DEPRECATED! * @@ -865,7 +918,6 @@ int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type) /* * @lc: context - * @devno: returns crypt name * * Cryptoloop is DEPRECATED! * @@ -1182,6 +1234,28 @@ int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags) /* * @lc: context + * @refname: reference name (used to overwrite lo_file_name where is backing + * file by default) + * + * The setting is removed by loopcxt_set_device() loopcxt_next()! + * + * Returns: 0 on success, <0 on error. + */ +int loopcxt_set_refname(struct loopdev_cxt *lc, const char *refname) +{ + if (!lc) + return -EINVAL; + + memset(lc->config.info.lo_file_name, 0, sizeof(lc->config.info.lo_file_name)); + if (refname) + xstrncpy((char *)lc->config.info.lo_file_name, refname, LO_NAME_SIZE); + + DBG(CXT, ul_debugobj(lc, "set refname=%s", (char *)lc->config.info.lo_file_name)); + return 0; +} + +/* + * @lc: context * @filename: backing file path (the path will be canonicalized) * * The setting is removed by loopcxt_set_device() loopcxt_next()! @@ -1197,9 +1271,10 @@ int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename) if (!lc->filename) return -errno; - xstrncpy((char *)lc->config.info.lo_file_name, lc->filename, LO_NAME_SIZE); + if (!lc->config.info.lo_file_name[0]) + loopcxt_set_refname(lc, lc->filename); - DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->config.info.lo_file_name)); + DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->filename)); return 0; } @@ -1314,7 +1389,8 @@ static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd) */ int loopcxt_setup_device(struct loopdev_cxt *lc) { - int file_fd, dev_fd, mode = O_RDWR, flags = O_CLOEXEC; + int file_fd, dev_fd; + mode_t flags = O_CLOEXEC, mode = O_RDWR; int rc = -1, cnt = 0; int errsv = 0; int fallback = 0; @@ -1344,25 +1420,22 @@ int loopcxt_setup_device(struct loopdev_cxt *lc) } DBG(SETUP, ul_debugobj(lc, "backing file open: OK")); - if (lc->fd != -1 && lc->mode != mode) { - DBG(SETUP, ul_debugobj(lc, "closing already open device (mode mismatch)")); - close(lc->fd); - lc->fd = -1; - lc->mode = 0; - } - - if (mode == O_RDONLY) { - lc->flags |= LOOPDEV_FL_RDONLY; /* open() mode */ + if (mode == O_RDONLY) lc->config.info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */ - } else { - lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */ + else lc->config.info.lo_flags &= ~LO_FLAGS_READ_ONLY; - lc->flags &= ~LOOPDEV_FL_RDONLY; - } do { errno = 0; - dev_fd = loopcxt_get_fd(lc); + + /* For the ioctls, it's enough to use O_RDONLY, but udevd + * monitor devices by inotify, and udevd needs IN_CLOSE_WRITE + * event to trigger probing of the new device. + * + * The mode used for the device does not have to match the mode + * used for the backing file. + */ + dev_fd = __loopcxt_get_fd(lc, O_RDWR); if (dev_fd >= 0 || lc->control_ok == 0) break; if (errno != EACCES && errno != ENOENT) diff --git a/lib/mbsalign.c b/lib/mbsalign.c index 7b8f5a6..b4ab7be 100644 --- a/lib/mbsalign.c +++ b/lib/mbsalign.c @@ -310,12 +310,33 @@ char *mbs_invalid_encode_to_buffer(const char *s, size_t *width, char *buf) return buf; } +/* + * Guess size + */ size_t mbs_safe_encode_size(size_t bytes) { return (bytes * 4) + 1; } /* + * Count size of the original string in bytes (count \x?? as one byte) + */ +size_t mbs_safe_decode_size(const char *p) +{ + size_t bytes = 0; + + while (p && *p) { + if (*p == '\\' && *(p + 1) == 'x' && + isxdigit(*(p + 2)) && isxdigit(*(p + 3))) + p += 4; + else + p++; + bytes++; + } + return bytes; +} + +/* * Returns allocated string where all control and non-printable chars are * replaced with \x?? hex sequence. */ diff --git a/lib/mbsedit.c b/lib/mbsedit.c index ecfa9f4..9cf4f0f 100644 --- a/lib/mbsedit.c +++ b/lib/mbsedit.c @@ -157,13 +157,14 @@ static size_t mbs_insert(char *str, wint_t c, size_t *ncells) #ifdef HAVE_WIDECHAR wchar_t wc = (wchar_t) c; - char in_buf[MB_CUR_MAX]; + in = malloc(MB_CUR_MAX); + if (!in) + return -1; - n = wctomb(in_buf, wc); + n = wctomb(in, wc); if (n == (size_t) -1) - return n; + goto out; *ncells = wcwidth(wc); - in = in_buf; #else *ncells = 1; in = (char *) &c; @@ -173,6 +174,10 @@ static size_t mbs_insert(char *str, wint_t c, size_t *ncells) memmove(str + n, str, bytes); memcpy(str, in, n); str[bytes + n] = '\0'; +out: +#ifdef HAVE_WIDECHAR + free(in); +#endif return n; } diff --git a/lib/monotonic.c b/lib/monotonic.c index cd554dd..63feec0 100644 --- a/lib/monotonic.c +++ b/lib/monotonic.c @@ -18,10 +18,10 @@ int get_boot_time(struct timeval *boot_time) { #ifdef CLOCK_BOOTTIME - struct timespec hires_uptime; - struct timeval lores_uptime; + struct timespec hires_uptime = { 0 }; + struct timeval lores_uptime = { 0 }; #endif - struct timeval now; + struct timeval now = { 0 }; #ifdef HAVE_SYSINFO struct sysinfo info; #endif diff --git a/lib/pager.c b/lib/pager.c index 9429032..98814b5 100644 --- a/lib/pager.c +++ b/lib/pager.c @@ -85,7 +85,9 @@ static int start_command(struct child_process *cmd) close(cmd->in); } - cmd->preexec_cb(); + if (cmd->preexec_cb) + cmd->preexec_cb(); + execvp(cmd->argv[0], (char *const*) cmd->argv); errexec(cmd->argv[0]); } @@ -140,7 +142,7 @@ static int finish_command(struct child_process *cmd) return wait_or_whine(cmd->pid); } -static void pager_preexec(void) +static void pager_preexec_less(void) { /* * Work around bug in "less" by not starting it until we @@ -178,35 +180,45 @@ static void wait_for_pager_signal(int signo) static int has_command(const char *cmd) { const char *path; - char *p, *s; + char *b, *c, *p, *s; int rc = 0; if (!cmd) goto done; - if (*cmd == '/') { - rc = access(cmd, X_OK) == 0; + + c = xstrdup(cmd); + if (!c) goto done; + b = strtok(c, " "); /* cmd may contain options */ + if (!b) + goto cleanup; + + if (*b == '/') { + rc = access(b, X_OK) == 0; + goto cleanup; } path = getenv("PATH"); if (!path) - goto done; + goto cleanup; p = xstrdup(path); if (!p) - goto done; + goto cleanup; - for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) { + for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) { int fd = open(s, O_RDONLY|O_CLOEXEC); if (fd < 0) continue; - rc = faccessat(fd, cmd, X_OK, 0) == 0; + rc = faccessat(fd, b, X_OK, 0) == 0; close(fd); if (rc) break; } free(p); +cleanup: + free(c); done: - /*fprintf(stderr, "has PAGER %s rc=%d\n", cmd, rc);*/ + /*fprintf(stderr, "has PAGER '%s': rc=%d\n", cmd, rc);*/ return rc; } @@ -230,7 +242,11 @@ static void __setup_pager(void) pager_argv[2] = pager; pager_process.argv = pager_argv; pager_process.in = -1; - pager_process.preexec_cb = pager_preexec; + + if (!strncmp(pager, "less", 4)) + pager_process.preexec_cb = pager_preexec_less; + else + pager_process.preexec_cb = NULL; if (start_command(&pager_process)) return; @@ -996,38 +996,25 @@ int ul_path_next_dirent(struct path_cxt *pc, DIR **sub, const char *dirname, str return 1; } -/* - * Like fopen() but, @path is always prefixed by @prefix. This function is - * useful in case when ul_path_* API is overkill. - */ -FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode) -{ - char buf[PATH_MAX]; - - if (!path) - return NULL; - if (!prefix) - return fopen(path, mode); - if (*path == '/') - path++; - - snprintf(buf, sizeof(buf), "%s/%s", prefix, path); - return fopen(buf, mode); -} - #ifdef HAVE_CPU_SET_T static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap) { FILE *f; size_t setsize, len = maxcpus * 7; - char buf[len]; + char *buf; int rc; *set = NULL; + buf = malloc(len); + if (!buf) + return -ENOMEM; + f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap); - if (!f) - return -errno; + if (!f) { + rc = -errno; + goto out; + } if (fgets(buf, len, f) == NULL) { errno = EIO; @@ -1038,32 +1025,38 @@ static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, i fclose(f); if (rc) - return rc; + goto out; len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; *set = cpuset_alloc(maxcpus, &setsize, NULL); - if (!*set) - return -ENOMEM; + if (!*set) { + rc = -EINVAL; + goto out; + } if (islist) { if (cpulist_parse(buf, *set, setsize, 0)) { - cpuset_free(*set); errno = EINVAL; rc = -errno; - return rc; + goto out; } } else { if (cpumask_parse(buf, *set, setsize)) { - cpuset_free(*set); errno = EINVAL; rc = -errno; - return rc; + goto out; } } - return 0; + rc = 0; + +out: + if (rc) + cpuset_free(*set); + free(buf); + return rc; } int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...) diff --git a/lib/procfs.c b/lib/procfs.c index aff20fb..59f40ff 100644 --- a/lib/procfs.c +++ b/lib/procfs.c @@ -169,6 +169,11 @@ ssize_t procfs_process_get_stat(struct path_cxt *pc, char *buf, size_t bufsz) return procfs_process_get_data_for(pc, buf, bufsz, "stat"); } +ssize_t procfs_process_get_syscall(struct path_cxt *pc, char *buf, size_t bufsz) +{ + return procfs_process_get_data_for(pc, buf, bufsz, "syscall"); +} + int procfs_process_get_stat_nth(struct path_cxt *pc, int n, uintmax_t *re) { ssize_t rc; diff --git a/lib/pty-session.c b/lib/pty-session.c index 3849065..815264d 100644 --- a/lib/pty-session.c +++ b/lib/pty-session.c @@ -241,6 +241,7 @@ int ul_pty_signals_setup(struct ul_pty *pty) sigaddset(&ourset, SIGCHLD); sigaddset(&ourset, SIGWINCH); sigaddset(&ourset, SIGALRM); + sigaddset(&ourset, SIGHUP); sigaddset(&ourset, SIGTERM); sigaddset(&ourset, SIGINT); sigaddset(&ourset, SIGQUIT); @@ -584,6 +585,8 @@ static int handle_signal(struct ul_pty *pty, int fd) &info, (void *) &pty->win); } break; + case SIGHUP: + /* fallthrough */ case SIGTERM: /* fallthrough */ case SIGINT: @@ -641,7 +644,7 @@ int ul_pty_proxy_master(struct ul_pty *pty) /* note, callback usually updates @next_callback_time */ if (timerisset(&pty->next_callback_time)) { - struct timeval now; + struct timeval now = { 0 };; DBG(IO, ul_debugobj(pty, " callback requested")); gettime_monotonic(&now); @@ -654,7 +657,7 @@ int ul_pty_proxy_master(struct ul_pty *pty) /* set timeout */ if (timerisset(&pty->next_callback_time)) { - struct timeval now, rest; + struct timeval now = { 0 }, rest = { 0 }; gettime_monotonic(&now); timersub(&pty->next_callback_time, &now, &rest); @@ -1,4 +1,7 @@ /* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * * SHA-1 in C by Steve Reid <steve@edmweb.com> * 100% Public Domain * @@ -153,7 +156,15 @@ void ul_SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) state[3] += d; state[4] += e; /* Wipe variables */ +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(&a, sizeof(a)); + explicit_bzero(&b, sizeof(b)); + explicit_bzero(&c, sizeof(c)); + explicit_bzero(&d, sizeof(d)); + explicit_bzero(&e, sizeof(e)); +#else a = b = c = d = e = 0; +#endif #ifdef UL_SHA1HANDSOFF memset(block, '\0', sizeof(block)); #endif diff --git a/lib/sha256.c b/lib/sha256.c index 934303c..10877d2 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -1,4 +1,7 @@ /* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * * public domain sha256 crypt implementation * * original sha crypt design: http://people.redhat.com/drepper/SHA-crypt.txt diff --git a/lib/shells.c b/lib/shells.c index 6693ab0..18e4cca 100644 --- a/lib/shells.c +++ b/lib/shells.c @@ -9,20 +9,11 @@ #include "closestream.h" #include "shells.h" -/* - * is_known_shell() -- if the given shell appears in /etc/shells - * or vendor defined files. - * Return 1 if found and return 0 if not found. - */ -extern int is_known_shell(const char *shell_name) -{ - int ret = 0; - #if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR) - size_t size = 0; +static econf_file *open_etc_shells(void) +{ econf_err error; - char **keys; - econf_file *key_file; + econf_file *key_file = NULL; error = econf_readDirs(&key_file, _PATH_VENDORDIR, @@ -35,31 +26,84 @@ extern int is_known_shell(const char *shell_name) syslog(LOG_ALERT, _("Cannot parse shells files: %s"), econf_errString(error)); - exit(EXIT_FAILURE); + return NULL; } - error = econf_getKeys(key_file, NULL, &size, &keys); - if (error) { - syslog(LOG_ALERT, - _("Cannot evaluate entries in shells files: %s"), - econf_errString(error)); - econf_free (key_file); - exit(EXIT_FAILURE); - } + return key_file; +} +#endif - for (size_t i = 0; i < size; i++) { - if (strcmp (keys[i], shell_name) == 0) { - ret = 1; - break; - } - } - econf_free (key_file); +/* + * print_shells () -- /etc/shells is outputted to stdout. + */ +extern void print_shells(FILE *out, const char *format) +{ +#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR) + size_t size = 0; + econf_err error; + char **keys = NULL; + econf_file *key_file = open_etc_shells(); + + if (!key_file) + return; + + error = econf_getKeys(key_file, NULL, &size, &keys); + if (error) { + econf_free(key_file); + errx(EXIT_FAILURE, + _("Cannot evaluate entries in shells files: %s"), + econf_errString(error)); + } + + for (size_t i = 0; i < size; i++) { + fprintf(out, format, keys[i]); + } + econf_free(keys); + econf_free(key_file); #else - char *s; + char *s; + + while ((s = getusershell())) + fprintf(out, format, s); + endusershell(); +#endif +} + + +/* + * is_known_shell() -- if the given shell appears in /etc/shells + * or vendor defined files. + * Return 1 if found and return 0 if not found. + */ +extern int is_known_shell(const char *shell_name) +{ + int ret = 0; if (!shell_name) return 0; +#if defined (HAVE_LIBECONF) && defined (USE_VENDORDIR) + char *val = NULL; + econf_err error; + econf_file *key_file = open_etc_shells(); + + if (!key_file) + return 0; + + error = econf_getStringValue (key_file, NULL, shell_name, &val); + if (error) { + if (error != ECONF_NOKEY) + syslog(LOG_ALERT, + _("Cannot evaluate entries in shells files: %s"), + econf_errString(error)); + } else + ret = 1; + + free(val); + econf_free(key_file); +#else + char *s; + setusershell(); while ((s = getusershell())) { if (*s != '#' && strcmp(shell_name, s) == 0) { @@ -71,4 +115,3 @@ extern int is_known_shell(const char *shell_name) #endif return ret; } - diff --git a/lib/strutils.c b/lib/strutils.c index 6229a6e..9ea5da7 100644 --- a/lib/strutils.c +++ b/lib/strutils.c @@ -456,21 +456,28 @@ err: errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); } -long double strtold_or_err(const char *str, const char *errmesg) +int ul_strtold(const char *str, long double *num) { - double num; char *end = NULL; errno = 0; if (str == NULL || *str == '\0') - goto err; - num = strtold(str, &end); + return -(errno = EINVAL); + *num = strtold(str, &end); - if (errno || str == end || (end && *end)) - goto err; + if (errno != 0) + return -errno; + if (str == end || (end && *end)) + return -(errno = EINVAL); + return 0; +} - return num; -err: +long double strtold_or_err(const char *str, const char *errmesg) +{ + long double num = 0; + + if (ul_strtold(str, &num) == 0) + return num; if (errno == ERANGE) err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); @@ -1010,6 +1017,34 @@ int strappend(char **a, const char *b) return 0; } +/* the hybrid version of strfconcat and strappend. */ +int strfappend(char **a, const char *format, ...) +{ + va_list ap; + int res; + + va_start(ap, format); + res = strvfappend(a, format, ap); + va_end(ap); + + return res; +} + +extern int strvfappend(char **a, const char *format, va_list ap) +{ + char *val; + int sz; + int res; + + sz = vasprintf(&val, format, ap); + if (sz < 0) + return -errno; + + res = strappend(a, val); + free(val); + return res; +} + static size_t strcspn_escaped(const char *s, const char *reject) { int escaped = 0; @@ -1386,6 +1421,15 @@ int main(int argc, char *argv[]) printf("\"%s\" --> \"%s\"\n", argv[2], ul_strchr_escaped(argv[2], *argv[3])); return EXIT_SUCCESS; + } else if (argc == 2 && strcmp(argv[1], "--next-string") == 0) { + char *buf = "abc\0Y\0\0xyz\0X"; + char *end = buf + 12; + char *p = buf; + + do { + printf("str: '%s'\n", p); + } while ((p = ul_next_string(p, end))); + } else { fprintf(stderr, "usage: %1$s --size <number>[suffix]\n" " %1$s --cmp-paths <path> <path>\n" @@ -74,7 +74,7 @@ unsigned strv_length(char * const *l) { return n; } -char **strv_new_ap(const char *x, va_list ap) { +static char **strv_new_ap(const char *x, va_list ap) { const char *s; char **a; unsigned n = 0, i = 0; @@ -265,7 +265,7 @@ int strv_push(char ***l, char *value) { if (m < n) return -ENOMEM; - c = realloc(*l, sizeof(char *) * m); + c = reallocarray(*l, m, sizeof(char *)); if (!c) return -ENOMEM; diff --git a/lib/terminal-colors.d.5 b/lib/terminal-colors.d.5 index 4b98389..1de42f4 100644 --- a/lib/terminal-colors.d.5 +++ b/lib/terminal-colors.d.5 @@ -2,12 +2,12 @@ .\" Title: terminal-colors.d .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: File formats -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "TERMINAL\-COLORS.D" "5" "2023-10-23" "util\-linux 2.39.3" "File formats" +.TH "TERMINAL\-COLORS.D" "5" "2024-01-31" "util\-linux 2.40" "File formats" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/lib/timeutils.c b/lib/timeutils.c index 34c7c8d..f53ec8f 100644 --- a/lib/timeutils.c +++ b/lib/timeutils.c @@ -161,6 +161,23 @@ static int parse_subseconds(const char *t, usec_t *usec) return 0; } +static const char *parse_epoch_seconds(const char *t, struct tm *tm) +{ + int64_t s; + time_t st; + int f, c; + + f = sscanf(t, "%"SCNd64"%n", &s, &c); + if (f < 1) + return NULL; + st = s; + if ((int64_t) st < s) + return NULL; + if (!localtime_r(&st, tm)) + return NULL; + return t + c; +} + static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) { static const struct { @@ -254,7 +271,7 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) goto finish; } else if (t[0] == '@') { - k = strptime(t + 1, "%s", &tm); + k = parse_epoch_seconds(t + 1, &tm); if (k && *k == 0) goto finish; else if (k && parse_subseconds(k, &ret) == 0) @@ -373,11 +390,13 @@ static int parse_timestamp_reference(time_t x, const char *t, usec_t *usec) ret += (usec_t) x * USEC_PER_SEC; + if (minus > ret) + return -ERANGE; + if ((ret + plus) < ret) + return -ERANGE; + ret += plus; - if (ret > minus) - ret -= minus; - else - ret = 0; + ret -= minus; *usec = ret; @@ -448,8 +467,9 @@ int get_gmtoff(const struct tm *tp) #endif } -static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf, size_t bufsz) +static int format_iso_time(const struct tm *tm, uint32_t nsec, int flags, char *buf, size_t bufsz) { + uint32_t usec = nsec / NSEC_PER_USEC; char *p = buf; int len; @@ -479,15 +499,28 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf p += len; } - if (flags & ISO_DOTUSEC) { - len = snprintf(p, bufsz, ".%06"PRId64, (int64_t) usec); + if (flags & ISO_DOTNSEC) { + len = snprintf(p, bufsz, ".%09"PRIu32, nsec); + if (len < 0 || (size_t) len > bufsz) + goto err; + bufsz -= len; + p += len; + + } else if (flags & ISO_COMMANSEC) { + len = snprintf(p, bufsz, ",%09"PRIu32, nsec); + if (len < 0 || (size_t) len > bufsz) + goto err; + bufsz -= len; + p += len; + } else if (flags & ISO_DOTUSEC) { + len = snprintf(p, bufsz, ".%06"PRIu32, usec); if (len < 0 || (size_t) len > bufsz) goto err; bufsz -= len; p += len; } else if (flags & ISO_COMMAUSEC) { - len = snprintf(p, bufsz, ",%06"PRId64, (int64_t) usec); + len = snprintf(p, bufsz, ",%06"PRIu32, usec); if (len < 0 || (size_t) len > bufsz) goto err; bufsz -= len; @@ -508,26 +541,37 @@ static int format_iso_time(struct tm *tm, suseconds_t usec, int flags, char *buf return -1; } -/* timeval to ISO 8601 */ -int strtimeval_iso(struct timeval *tv, int flags, char *buf, size_t bufsz) +/* timespec to ISO 8601 */ +int strtimespec_iso(const struct timespec *ts, int flags, char *buf, size_t bufsz) { struct tm tm; struct tm *rc; if (flags & ISO_GMTIME) - rc = gmtime_r(&tv->tv_sec, &tm); + rc = gmtime_r(&ts->tv_sec, &tm); else - rc = localtime_r(&tv->tv_sec, &tm); + rc = localtime_r(&ts->tv_sec, &tm); if (rc) - return format_iso_time(&tm, tv->tv_usec, flags, buf, bufsz); + return format_iso_time(&tm, ts->tv_nsec, flags, buf, bufsz); - warnx(_("time %"PRId64" is out of range."), (int64_t)(tv->tv_sec)); + warnx(_("time %"PRId64" is out of range."), (int64_t)(ts->tv_sec)); return -1; } +/* timeval to ISO 8601 */ +int strtimeval_iso(const struct timeval *tv, int flags, char *buf, size_t bufsz) +{ + struct timespec ts = { + .tv_sec = tv->tv_sec, + .tv_nsec = tv->tv_usec * NSEC_PER_USEC, + }; + + return strtimespec_iso(&ts, flags, buf, bufsz); +} + /* struct tm to ISO 8601 */ -int strtm_iso(struct tm *tm, int flags, char *buf, size_t bufsz) +int strtm_iso(const struct tm *tm, int flags, char *buf, size_t bufsz) { return format_iso_time(tm, 0, flags, buf, bufsz); } @@ -592,6 +636,63 @@ int strtime_short(const time_t *t, struct timeval *now, int flags, char *buf, si return rc <= 0 ? -1 : 0; } +int strtimespec_relative(const struct timespec *ts, char *buf, size_t bufsz) +{ + time_t secs = ts->tv_sec; + size_t i, parts = 0; + int rc; + + if (bufsz) + buf[0] = '\0'; + + static const struct { + const char * const suffix; + int width; + int64_t secs; + } table[] = { + { "y", 4, NSEC_PER_YEAR / NSEC_PER_SEC }, + { "d", 3, NSEC_PER_DAY / NSEC_PER_SEC }, + { "h", 2, NSEC_PER_HOUR / NSEC_PER_SEC }, + { "m", 2, NSEC_PER_MINUTE / NSEC_PER_SEC }, + { "s", 2, NSEC_PER_SEC / NSEC_PER_SEC }, + }; + + for (i = 0; i < ARRAY_SIZE(table); i++) { + if (secs >= table[i].secs) { + rc = snprintf(buf, bufsz, + "%*"PRId64"%s%s", + parts ? table[i].width : 0, + secs / table[i].secs, table[i].suffix, + secs % table[i].secs ? " " : ""); + if (rc < 0 || (size_t) rc > bufsz) + goto err; + parts++; + buf += rc; + bufsz -= rc; + secs %= table[i].secs; + } + } + + if (ts->tv_nsec) { + if (ts->tv_nsec % NSEC_PER_MSEC) { + rc = snprintf(buf, bufsz, "%*luns", + parts ? 10 : 0, ts->tv_nsec); + if (rc < 0 || (size_t) rc > bufsz) + goto err; + } else { + rc = snprintf(buf, bufsz, "%*llums", + parts ? 4 : 0, ts->tv_nsec / NSEC_PER_MSEC); + if (rc < 0 || (size_t) rc > bufsz) + goto err; + } + } + + return 0; + err: + warnx(_("format_reltime: buffer overflow.")); + return -1; +} + #ifndef HAVE_TIMEGM time_t timegm(struct tm *tm) { @@ -625,6 +726,7 @@ static int run_unittest_timestamp(void) { "2012-09-22 16:34:22.012", 1348331662012000 }, { "@1348331662" , 1348331662000000 }, { "@1348331662.234567" , 1348331662234567 }, + { "@0" , 0 }, { "2012-09-22 16:34" , 1348331640000000 }, { "2012-09-22" , 1348272000000000 }, { "16:34:22" , 1674232462000000 }, @@ -662,45 +764,142 @@ static int run_unittest_timestamp(void) return rc; } +static int run_unittest_format(void) +{ + int rc = EXIT_SUCCESS; + const struct timespec ts = { + .tv_sec = 1674180427, + .tv_nsec = 12345, + }; + char buf[FORMAT_TIMESTAMP_MAX]; + static const struct testcase { + int flags; + const char * const expected; + } testcases[] = { + { ISO_DATE, "2023-01-20" }, + { ISO_TIME, "02:07:07" }, + { ISO_TIMEZONE, "+00:00" }, + { ISO_TIMESTAMP_T, "2023-01-20T02:07:07+00:00" }, + { ISO_TIMESTAMP_COMMA_G, "2023-01-20 02:07:07,000012+00:00" }, + { ISO_TIME | ISO_DOTNSEC, "02:07:07.000012345" }, + }; + + setenv("TZ", "GMT", 1); + tzset(); + + for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) { + struct testcase t = testcases[i]; + int r = strtimespec_iso(&ts, t.flags, buf, sizeof(buf)); + if (r) { + fprintf(stderr, "Could not format '%s'\n", t.expected); + rc = EXIT_FAILURE; + } + + if (strcmp(buf, t.expected)) { + fprintf(stderr, "#%02zu %-20s != %-20s\n", i, buf, t.expected); + rc = EXIT_FAILURE; + } + } + + return rc; +} + +static int run_unittest_format_relative(void) +{ + int rc = EXIT_SUCCESS; + char buf[FORMAT_TIMESTAMP_MAX]; + static const struct testcase { + struct timespec ts; + const char * const expected; + } testcases[] = { + {{}, "" }, + {{ 1 }, "1s" }, + {{ 10 }, "10s" }, + {{ 100 }, "1m 40s" }, + {{ 1000 }, "16m 40s" }, + {{ 10000 }, "2h 46m 40s" }, + {{ 100000 }, "1d 3h 46m 40s" }, + {{ 1000000 }, "11d 13h 46m 40s" }, + {{ 10000000 }, "115d 17h 46m 40s" }, + {{ 100000000 }, "3y 61d 15h 46m 40s" }, + {{ 60 }, "1m" }, + {{ 3600 }, "1h" }, + + {{ 1, 1 }, "1s 1ns" }, + {{ 0, 1 }, "1ns" }, + {{ 0, 1000000 }, "1ms" }, + {{ 0, 1000001 }, "1000001ns" }, + }; + + setenv("TZ", "GMT", 1); + tzset(); + + for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) { + struct testcase t = testcases[i]; + int r = strtimespec_relative(&t.ts, buf, sizeof(buf)); + if (r) { + fprintf(stderr, "Could not format '%s'\n", t.expected); + rc = EXIT_FAILURE; + } + + if (strcmp(buf, t.expected)) { + fprintf(stderr, "#%02zu '%-20s' != '%-20s'\n", i, buf, t.expected); + rc = EXIT_FAILURE; + } + } + + return rc; +} + int main(int argc, char *argv[]) { - struct timeval tv = { 0 }; + struct timespec ts = { 0 }; char buf[ISO_BUFSIZ]; + int r; if (argc < 2) { fprintf(stderr, "usage: %s [<time> [<usec>]] | [--timestamp <str>] | [--unittest-timestamp]\n", argv[0]); exit(EXIT_FAILURE); } - if (strcmp(argv[1], "--unittest-timestamp") == 0) { + if (strcmp(argv[1], "--unittest-timestamp") == 0) return run_unittest_timestamp(); - } + else if (strcmp(argv[1], "--unittest-format") == 0) + return run_unittest_format(); + else if (strcmp(argv[1], "--unittest-format-relative") == 0) + return run_unittest_format_relative(); if (strcmp(argv[1], "--timestamp") == 0) { usec_t usec = 0; - parse_timestamp(argv[2], &usec); - tv.tv_sec = (time_t) (usec / 1000000); - tv.tv_usec = usec % 1000000; + r = parse_timestamp(argv[2], &usec); + if (r) + errx(EXIT_FAILURE, "Can not parse '%s': %s", argv[2], strerror(-r)); + ts.tv_sec = (time_t) (usec / USEC_PER_SEC); + ts.tv_nsec = (usec % USEC_PER_SEC) * NSEC_PER_USEC; } else { - tv.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>"); + ts.tv_sec = strtos64_or_err(argv[1], "failed to parse <time>"); if (argc == 3) - tv.tv_usec = strtos64_or_err(argv[2], "failed to parse <usec>"); + ts.tv_nsec = strtos64_or_err(argv[2], "failed to parse <usec>") + * NSEC_PER_USEC; } - strtimeval_iso(&tv, ISO_DATE, buf, sizeof(buf)); + strtimespec_iso(&ts, ISO_DATE, buf, sizeof(buf)); printf("Date: '%s'\n", buf); - strtimeval_iso(&tv, ISO_TIME, buf, sizeof(buf)); + strtimespec_iso(&ts, ISO_TIME, buf, sizeof(buf)); printf("Time: '%s'\n", buf); - strtimeval_iso(&tv, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T, + strtimespec_iso(&ts, ISO_DATE | ISO_TIME | ISO_COMMAUSEC | ISO_T, buf, sizeof(buf)); printf("Full: '%s'\n", buf); - strtimeval_iso(&tv, ISO_TIMESTAMP_DOT, buf, sizeof(buf)); + strtimespec_iso(&ts, ISO_TIMESTAMP_DOT, buf, sizeof(buf)); printf("Zone: '%s'\n", buf); + strtimespec_relative(&ts, buf, sizeof(buf)); + printf("Rel: '%s'\n", buf); + return EXIT_SUCCESS; } diff --git a/lib/ttyutils.c b/lib/ttyutils.c index 7064565..bacd730 100644 --- a/lib/ttyutils.c +++ b/lib/ttyutils.c @@ -7,9 +7,40 @@ #include <ctype.h> #include <unistd.h> +#ifdef HAVE_GETTTYNAM +# include <ttyent.h> +#endif + #include "c.h" #include "ttyutils.h" +#ifdef __linux__ +# ifndef DEFAULT_VCTERM +# define DEFAULT_VCTERM "linux" +# endif +# if defined (__s390__) || defined (__s390x__) +# define DEFAULT_TTYS0 "dumb" +# define DEFAULT_TTY32 "ibm327x" +# define DEFAULT_TTYS1 "vt220" +# endif +# ifndef DEFAULT_STERM +# define DEFAULT_STERM "vt102" +# endif +#elif defined(__GNU__) +# ifndef DEFAULT_VCTERM +# define DEFAULT_VCTERM "hurd" +# endif +# ifndef DEFAULT_STERM +# define DEFAULT_STERM "vt102" +# endif +#else +# ifndef DEFAULT_VCTERM +# define DEFAULT_VCTERM "vt100" +# endif +# ifndef DEFAULT_STERM +# define DEFAULT_STERM "vt100" +# endif +#endif static int get_env_int(const char *name) { @@ -130,6 +161,35 @@ int get_terminal_type(const char **type) return 0; } +char *get_terminal_default_type(const char *ttyname, int is_serial) +{ + if (ttyname) { +#ifdef HAVE_GETTTYNAM + struct ttyent *ent = getttynam(ttyname); + + if (ent && ent->ty_type) + return strdup(ent->ty_type); +#endif + +#if defined (__s390__) || defined (__s390x__) + /* + * Special terminal on first serial line on a S/390(x) which + * is due legacy reasons a block terminal of type 3270 or + * higher. Whereas the second serial line on a S/390(x) is + * a real character terminal which is compatible with VT220. + */ + if (strcmp(ttyname, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */ + return strdup(DEFAULT_TTYS0); + else if (strncmp(ttyname, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */ + return strdup(DEFAULT_TTY32); + else if (strcmp(ttyname, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */ + return strdup(DEFAULT_TTYS1); +#endif + } + + return strdup(is_serial ? DEFAULT_STERM : DEFAULT_VCTERM); +} + #ifdef TEST_PROGRAM_TTYUTILS # include <stdlib.h> int main(void) @@ -138,13 +198,21 @@ int main(void) int c, l; if (get_terminal_name(&path, &name, &num) == 0) { - fprintf(stderr, "tty path: %s\n", path); - fprintf(stderr, "tty name: %s\n", name); - fprintf(stderr, "tty number: %s\n", num); + char *term; + + fprintf(stderr, "tty path: %s\n", path); + fprintf(stderr, "tty name: %s\n", name); + fprintf(stderr, "tty number: %s\n", num); + + fprintf(stderr, "tty term: %s\n", getenv("TERM")); + + term = get_terminal_default_type(name, 0); + fprintf(stderr, "tty dflt term: %s\n", term); + free(term); } get_terminal_dimension(&c, &l); - fprintf(stderr, "tty cols: %d\n", c); - fprintf(stderr, "tty lines: %d\n", l); + fprintf(stderr, "tty cols: %d\n", c); + fprintf(stderr, "tty lines: %d\n", l); return EXIT_SUCCESS; diff --git a/libblkid/docs/Makefile.in b/libblkid/docs/Makefile.in index f0007f2..ddb5709 100644 --- a/libblkid/docs/Makefile.in +++ b/libblkid/docs/Makefile.in @@ -117,7 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_vscript.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/tls.m4 \ - $(top_srcdir)/m4/ul.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ul.m4 $(top_srcdir)/m4/year2038.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -159,10 +160,12 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BISON = @BISON@ BSD_WARN_CFLAGS = @BSD_WARN_CFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPTSETUP_CFLAGS = @CRYPTSETUP_CFLAGS@ @@ -192,6 +195,7 @@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ +FLEX = @FLEX@ FUZZING_ENGINE_LDFLAGS = @FUZZING_ENGINE_LDFLAGS@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ @@ -219,6 +223,8 @@ LIBFDISK_VERSION = @LIBFDISK_VERSION@ LIBFDISK_VERSION_INFO = @LIBFDISK_VERSION_INFO@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ +LIBLASTLOG2_VERSION = @LIBLASTLOG2_VERSION@ +LIBLASTLOG2_VERSION_INFO = @LIBLASTLOG2_VERSION_INFO@ LIBMOUNT_MAJOR_VERSION = @LIBMOUNT_MAJOR_VERSION@ LIBMOUNT_MINOR_VERSION = @LIBMOUNT_MINOR_VERSION@ LIBMOUNT_PATCH_VERSION = @LIBMOUNT_PATCH_VERSION@ @@ -244,6 +250,7 @@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MATH_LIBS = @MATH_LIBS@ MKDIR_P = @MKDIR_P@ +MQ_LIBS = @MQ_LIBS@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ @@ -257,7 +264,6 @@ NCURSES_CFLAGS = @NCURSES_CFLAGS@ NCURSES_LIBS = @NCURSES_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ -NO_UNUSED_WARN_CFLAGS = @NO_UNUSED_WARN_CFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -297,6 +303,8 @@ SHELL = @SHELL@ SOCKET_LIBS = @SOCKET_LIBS@ SOLIB_CFLAGS = @SOLIB_CFLAGS@ SOLIB_LDFLAGS = @SOLIB_LDFLAGS@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ STRIP = @STRIP@ SUID_CFLAGS = @SUID_CFLAGS@ SUID_LDFLAGS = @SUID_LDFLAGS@ @@ -382,6 +390,7 @@ sysconfdir = @sysconfdir@ sysconfstaticdir = @sysconfstaticdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ +tmpfilesdir = @tmpfilesdir@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @@ -391,6 +400,7 @@ usrsbin_execdir = @usrsbin_execdir@ vendordir = @vendordir@ with_bashcompletiondir = @with_bashcompletiondir@ with_systemdsystemunitdir = @with_systemdsystemunitdir@ +with_tmpfilesdir = @with_tmpfilesdir@ # We require automake 1.10 at least. AUTOMAKE_OPTIONS = 1.10 diff --git a/libblkid/docs/libblkid-sections.txt b/libblkid/docs/libblkid-sections.txt index 9297002..308f8ef 100644 --- a/libblkid/docs/libblkid-sections.txt +++ b/libblkid/docs/libblkid-sections.txt @@ -73,6 +73,7 @@ BLKID_PROBE_AMBIGUOUS <FILE>lowprobe-tags</FILE> blkid_do_fullprobe blkid_do_wipe +blkid_wipe_all blkid_do_probe blkid_do_safeprobe <SUBSECTION> diff --git a/libblkid/docs/version.xml b/libblkid/docs/version.xml index a69af57..4bdd32f 100644 --- a/libblkid/docs/version.xml +++ b/libblkid/docs/version.xml @@ -1 +1 @@ -2.39.3 +2.40 diff --git a/libblkid/libblkid.3 b/libblkid/libblkid.3 index 42ee837..0297e27 100644 --- a/libblkid/libblkid.3 +++ b/libblkid/libblkid.3 @@ -2,12 +2,12 @@ .\" Title: libblkid .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-11-21 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "LIBBLKID" "3" "2023-11-21" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "LIBBLKID" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libblkid/meson.build b/libblkid/meson.build index 0cb134f..5a28bdf 100644 --- a/libblkid/meson.build +++ b/libblkid/meson.build @@ -140,7 +140,7 @@ lib_blkid = both_libraries( version : libblkid_version, link_args : ['-Wl,--version-script=@0@'.format(libblkid_sym_path)], link_with : lib_common, - dependencies : build_libblkid ? [] : disabler(), + dependencies : build_libblkid ? [lib_econf] : disabler(), install : build_libblkid) blkid_dep = declare_dependency(link_with: lib_blkid, include_directories: '.') diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am index bc90eb8..3fae3f0 100644 --- a/libblkid/src/Makemodule.am +++ b/libblkid/src/Makemodule.am @@ -118,6 +118,9 @@ libblkid_la_SOURCES += \ endif libblkid_la_LIBADD = libcommon.la +if HAVE_ECONF +libblkid_la_LIBADD += -leconf +endif EXTRA_libblkid_la_DEPENDENCIES = \ libblkid/src/libblkid.sym diff --git a/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in index c232d72..8f6ec97 100644 --- a/libblkid/src/blkid.h.in +++ b/libblkid/src/blkid.h.in @@ -458,6 +458,8 @@ extern int blkid_probe_has_value(blkid_probe pr, const char *name) __ul_attribute__((nonnull)); extern int blkid_do_wipe(blkid_probe pr, int dryrun) __ul_attribute__((nonnull)); +extern int blkid_wipe_all(blkid_probe pr) + __ul_attribute__((nonnull)); extern int blkid_probe_step_back(blkid_probe pr) __ul_attribute__((nonnull)); diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h index 007cc35..ea7d81b 100644 --- a/libblkid/src/blkidP.h +++ b/libblkid/src/blkidP.h @@ -146,6 +146,7 @@ struct blkid_idmag { const char *magic; /* magic string */ unsigned int len; /* length of magic */ + unsigned int hint; /* hint for prober */ const char *hoff; /* hint which contains byte offset to kboff */ long kboff; /* kilobyte offset of superblock */ @@ -205,6 +206,7 @@ struct blkid_struct_probe int fd; /* device file descriptor */ uint64_t off; /* begin of data on the device */ uint64_t size; /* end of data on the device */ + uint64_t io_size; /* optimal size of IO */ dev_t devno; /* device number (st.st_rdev) */ dev_t disk_devno; /* devno of the whole-disk or 0 */ @@ -220,6 +222,7 @@ struct blkid_struct_probe struct blkid_chain *wipe_chain; /* superblock, partition, ... */ struct list_head buffers; /* list of buffers */ + struct list_head prunable_buffers; /* list of prunable buffers */ struct list_head hints; struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */ @@ -412,12 +415,12 @@ extern int blkdid_probe_is_opal_locked(blkid_probe pr) __attribute__((nonnull)) __attribute__((warn_unused_result)); -extern unsigned char *blkid_probe_get_buffer(blkid_probe pr, +extern const unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len) __attribute__((nonnull)) __attribute__((warn_unused_result)); -extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) +extern const unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) __attribute__((nonnull)) __attribute__((warn_unused_result)); @@ -433,10 +436,15 @@ extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, uint64_t *offset, const struct blkid_idmag **res) __attribute__((nonnull(1))); +extern void blkid_probe_prune_buffers(blkid_probe pr); + /* returns superblock according to 'struct blkid_idmag' */ -extern unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size); +extern const unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size); #define blkid_probe_get_sb(_pr, _mag, type) \ - ((type *) blkid_probe_get_sb_buffer((_pr), _mag, sizeof(type))) + ((const type *) blkid_probe_get_sb_buffer((_pr), _mag, sizeof(type))) + +extern uint64_t blkid_probe_get_idmag_off(blkid_probe pr, const struct blkid_idmag *mag) + __attribute__((nonnull)); extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr) __attribute__((nonnull)) diff --git a/libblkid/src/config.c b/libblkid/src/config.c index d3f5eea..7b8b04f 100644 --- a/libblkid/src/config.c +++ b/libblkid/src/config.c @@ -22,6 +22,9 @@ #endif #include <stdint.h> #include <stdarg.h> +#if defined (HAVE_LIBECONF) +#include <libeconf.h> +#endif #include "blkidP.h" #include "env.h" @@ -55,6 +58,7 @@ err: return -1; } +#ifndef HAVE_LIBECONF static int parse_next(FILE *fd, struct blkid_config *conf) { char buf[BUFSIZ]; @@ -111,25 +115,29 @@ static int parse_next(FILE *fd, struct blkid_config *conf) } return 0; } +#endif /* !HAVE_LIBECONF */ /* return real config data or built-in default */ struct blkid_config *blkid_read_config(const char *filename) { struct blkid_config *conf; - FILE *f; - - if (!filename) - filename = safe_getenv("BLKID_CONF"); - if (!filename) - filename = BLKID_CONFIG_FILE; conf = calloc(1, sizeof(*conf)); if (!conf) return NULL; conf->uevent = -1; - DBG(CONFIG, ul_debug("reading config file: %s.", filename)); + if (!filename) + filename = safe_getenv("BLKID_CONF"); + +#ifndef HAVE_LIBECONF + + FILE *f; + if (!filename) + filename = BLKID_CONFIG_FILE; + + DBG(CONFIG, ul_debug("reading config file: %s.", filename)); f = fopen(filename, "r" UL_CLOEXECSTR); if (!f) { DBG(CONFIG, ul_debug("%s: does not exist, using built-in default", filename)); @@ -141,6 +149,81 @@ struct blkid_config *blkid_read_config(const char *filename) goto err; } } + +#else /* !HAVE_LIBECONF */ + + static econf_file *file = NULL; + econf_err error; + + if (filename) { + DBG(CONFIG, ul_debug("reading config file: %s.", filename)); + error = econf_readFile(&file, filename, "= \t", "#"); + } else { + error = econf_readDirs(&file, +#if USE_VENDORDIR + _PATH_VENDORDIR, +#else + NULL, +#endif + "/etc", "blkid", "conf", "= \t", "#"); + } + + if (error) { + if (error == ECONF_NOFILE) { + if (filename) + DBG(CONFIG, + ul_debug("%s: does not exist, using built-in default", filename)); + else + DBG(CONFIG, + ul_debug("No configuration file blkid.conf found, using built-in default ")); + goto dflt; + } else { + if (filename) + DBG(CONFIG, ul_debug("%s: parse error:%s", filename, econf_errString(error))); + else + DBG(CONFIG, ul_debug("parse error:%s", econf_errString(error))); + + goto err; + } + } + + bool uevent = false; + if ((error = econf_getBoolValue(file, NULL, "SEND_UEVENT", &uevent))) { + if (error != ECONF_NOKEY) { + DBG(CONFIG, ul_debug("couldn't fetch SEND_UEVENT corrently: %s", econf_errString(error))); + goto err; + } else { + DBG(CONFIG, ul_debug("key SEND_UEVENT not found, using built-in default ")); + } + } else { + conf->uevent = uevent ? TRUE : FALSE; + } + + if ((error = econf_getStringValue(file, NULL, "CACHE_FILE", &(conf->cachefile)))) { + conf->cachefile = NULL; + if (error != ECONF_NOKEY) { + DBG(CONFIG, ul_debug("couldn't fetch CACHE_FILE correctly: %s", econf_errString(error))); + goto err; + } else { + DBG(CONFIG, ul_debug("key CACHE_FILE not found, using built-in default ")); + } + } + + char *line = NULL; + if ((error = econf_getStringValue(file, NULL, "EVALUATE", &line))) { + conf->nevals = 0; + if (error != ECONF_NOKEY) { + DBG(CONFIG, ul_debug("couldn't fetch EVALUATE correctly: %s", econf_errString(error))); + goto err; + } else { + DBG(CONFIG, ul_debug("key CACHE_FILE not found, using built-in default ")); + } + } else { + if (*line && parse_evaluate(conf, line) == -1) + goto err; + } + +#endif /* HAVE_LIBECONF */ dflt: if (!conf->nevals) { conf->eval[0] = BLKID_EVAL_UDEV; @@ -151,13 +234,23 @@ dflt: conf->cachefile = strdup(BLKID_CACHE_FILE); if (conf->uevent == -1) conf->uevent = TRUE; +#ifndef HAVE_LIBECONF if (f) fclose(f); +#else + econf_free (file); + free (line); +#endif return conf; err: free(conf->cachefile); free(conf); +#ifndef HAVE_LIBECONF fclose(f); +#else + econf_free (file); + free (line); +#endif return NULL; } diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c index c38ec3d..ca13f39 100644 --- a/libblkid/src/dev.c +++ b/libblkid/src/dev.c @@ -155,15 +155,13 @@ int blkid_dev_set_search(blkid_dev_iterate iter, if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type || !search_value) return -1; - new_type = malloc(strlen(search_type)+1); - new_value = malloc(strlen(search_value)+1); + new_type = strdup(search_type); + new_value = strdup(search_value); if (!new_type || !new_value) { free(new_type); free(new_value); return -1; } - strcpy(new_type, search_type); - strcpy(new_value, search_value); free(iter->search_type); free(iter->search_value); iter->search_type = new_type; diff --git a/libblkid/src/libblkid.sym b/libblkid/src/libblkid.sym index 775b024..7b2263a 100644 --- a/libblkid/src/libblkid.sym +++ b/libblkid/src/libblkid.sym @@ -187,3 +187,7 @@ BLKID_2_37 { BLKID_2_39 { blkid_topology_get_diskseq; } BLKID_2_37; + +BLKID_2_40 { + blkid_wipe_all; +} BLKID_2_39; diff --git a/libblkid/src/partitions/bsd.c b/libblkid/src/partitions/bsd.c index ba12019..20dab6b 100644 --- a/libblkid/src/partitions/bsd.c +++ b/libblkid/src/partitions/bsd.c @@ -47,7 +47,7 @@ static int probe_bsd_pt(blkid_probe pr, const struct blkid_idmag *mag) blkid_partition parent; blkid_partlist ls; int i, nparts = BSD_MAXPARTITIONS; - unsigned char *data; + const unsigned char *data; int rc = BLKID_PROBE_NONE; uint32_t abs_offset = 0; diff --git a/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c index 5c7718c..d5a8aa3 100644 --- a/libblkid/src/partitions/dos.c +++ b/libblkid/src/partitions/dos.c @@ -34,7 +34,7 @@ static const struct dos_subtypes { { MBR_MINIX_PARTITION, &minix_pt_idinfo } }; -static inline int is_extended(struct dos_partition *p) +static inline int is_extended(const struct dos_partition *p) { return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION || p->sys_ind == MBR_W95_EXTENDED_PARTITION || @@ -46,7 +46,7 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, { blkid_partlist ls = blkid_probe_get_partlist(pr); uint32_t cur_start = ex_start, cur_size = ex_size; - unsigned char *data; + const unsigned char *data; int ct_nodata = 0; /* count ext.partitions without data partitions */ int i; @@ -57,7 +57,7 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab, } while (1) { - struct dos_partition *p, *p0; + const struct dos_partition *p, *p0; uint32_t start = 0, size; if (++ct_nodata > 100) @@ -156,9 +156,9 @@ static inline int is_lvm(blkid_probe pr) return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0); } -static inline int is_empty_mbr(unsigned char *mbr) +static inline int is_empty_mbr(const unsigned char *mbr) { - struct dos_partition *p = mbr_get_partition(mbr, 0); + const struct dos_partition *p = mbr_get_partition(mbr, 0); int i, nparts = 0; for (i = 0; i < 4; i++) { @@ -177,8 +177,8 @@ static int probe_dos_pt(blkid_probe pr, int ssf; blkid_parttable tab = NULL; blkid_partlist ls; - struct dos_partition *p0, *p; - unsigned char *data; + const struct dos_partition *p0, *p; + const unsigned char *data; uint32_t start, size, id; char idstr[UUID_STR_LEN]; diff --git a/libblkid/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c index 89e7bb6..473bf5c 100644 --- a/libblkid/src/partitions/gpt.c +++ b/libblkid/src/partitions/gpt.c @@ -109,7 +109,7 @@ static inline uint32_t count_crc32(const unsigned char *buf, size_t len, return (ul_crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_len) ^ ~0L); } -static inline unsigned char *get_lba_buffer(blkid_probe pr, +static inline const unsigned char *get_lba_buffer(blkid_probe pr, uint64_t lba, size_t bytes) { return blkid_probe_get_buffer(pr, @@ -161,8 +161,8 @@ static int last_lba(blkid_probe pr, uint64_t *lba) static int is_pmbr_valid(blkid_probe pr, int *has) { int flags = blkid_partitions_get_flags(pr); - unsigned char *data; - struct dos_partition *p; + const unsigned char *data; + const struct dos_partition *p; int i; if (has) diff --git a/libblkid/src/partitions/mac.c b/libblkid/src/partitions/mac.c index 75a558b..36ce74e 100644 --- a/libblkid/src/partitions/mac.c +++ b/libblkid/src/partitions/mac.c @@ -56,7 +56,7 @@ struct mac_driver_desc { /* there is more stuff after this that we don't need */ } __attribute__((packed)); -static inline unsigned char *get_mac_block( +static inline const unsigned char *get_mac_block( blkid_probe pr, uint16_t block_size, uint32_t num) diff --git a/libblkid/src/partitions/minix.c b/libblkid/src/partitions/minix.c index 43c9d9a..dee7df9 100644 --- a/libblkid/src/partitions/minix.c +++ b/libblkid/src/partitions/minix.c @@ -17,11 +17,11 @@ static int probe_minix_pt(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) { - struct dos_partition *p; + const struct dos_partition *p; blkid_parttable tab = NULL; blkid_partition parent; blkid_partlist ls; - unsigned char *data; + const unsigned char *data; int i; data = blkid_probe_get_sector(pr, 0); diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c index 8ebf480..e096cf8 100644 --- a/libblkid/src/partitions/partitions.c +++ b/libblkid/src/partitions/partitions.c @@ -436,8 +436,8 @@ static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab) /* Linux kernel has DISK_MAX_PARTS=256, but it's too much for * generic Linux machine -- let start with 32 partitions. */ - void *tmp = realloc(ls->parts, (ls->nparts_max + 32) * - sizeof(struct blkid_struct_partition)); + void *tmp = reallocarray(ls->parts, ls->nparts_max + 32, + sizeof(struct blkid_struct_partition)); if (!tmp) return NULL; ls->parts = tmp; @@ -557,6 +557,7 @@ static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id, "%s: ---> call probefunc()", id->name)); errno = 0; rc = id->probefunc(pr, mag); + blkid_probe_prune_buffers(pr); if (rc < 0) { /* reset after error */ reset_partlist(blkid_probe_get_partlist(pr)); diff --git a/libblkid/src/partitions/ultrix.c b/libblkid/src/partitions/ultrix.c index 9c060be..a85712f 100644 --- a/libblkid/src/partitions/ultrix.c +++ b/libblkid/src/partitions/ultrix.c @@ -37,8 +37,8 @@ struct ultrix_disklabel { static int probe_ultrix_pt(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) { - unsigned char *data; - struct ultrix_disklabel *l; + const unsigned char *data; + const struct ultrix_disklabel *l; blkid_parttable tab = NULL; blkid_partlist ls; int i; @@ -50,7 +50,7 @@ static int probe_ultrix_pt(blkid_probe pr, goto nothing; } - l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET); + l = (const struct ultrix_disklabel *) (data + ULTRIX_OFFSET); if (l->pt_magic != ULTRIX_MAGIC || l->pt_valid != 1) goto nothing; diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c index fbf504a..76905e1 100644 --- a/libblkid/src/probe.c +++ b/libblkid/src/probe.c @@ -90,6 +90,7 @@ #include <unistd.h> #include <fcntl.h> #include <ctype.h> +#include <sys/mman.h> #include <sys/types.h> #ifdef HAVE_LINUX_CDROM_H #include <linux/cdrom.h> @@ -155,6 +156,7 @@ blkid_probe blkid_new_probe(void) pr->chains[i].enabled = chains_drvs[i]->dflt_enabled; } INIT_LIST_HEAD(&pr->buffers); + INIT_LIST_HEAD(&pr->prunable_buffers); INIT_LIST_HEAD(&pr->values); INIT_LIST_HEAD(&pr->hints); return pr; @@ -182,6 +184,7 @@ blkid_probe blkid_clone_probe(blkid_probe parent) pr->fd = parent->fd; pr->off = parent->off; pr->size = parent->size; + pr->io_size = parent->io_size; pr->devno = parent->devno; pr->disk_devno = parent->disk_devno; pr->blkssz = parent->blkssz; @@ -543,6 +546,16 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[ return 0; } +static void remove_buffer(struct blkid_bufinfo *bf) +{ + list_del(&bf->bufs); + + DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]", + bf->off, bf->len)); + munmap(bf->data, bf->len); + free(bf); +} + static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint64_t len) { ssize_t ret; @@ -560,13 +573,20 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint } /* allocate info and space for data by one malloc call */ - bf = calloc(1, sizeof(struct blkid_bufinfo) + len); + bf = calloc(1, sizeof(struct blkid_bufinfo)); if (!bf) { errno = ENOMEM; return NULL; } - bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo); + bf->data = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (bf->data == MAP_FAILED) { + free(bf); + errno = ENOMEM; + return NULL; + } + bf->len = len; bf->off = real_off; INIT_LIST_HEAD(&bf->bufs); @@ -577,7 +597,7 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint ret = read(pr->fd, bf->data, len); if (ret != (ssize_t) len) { DBG(LOWPROBE, ul_debug("\tread failed: %m")); - free(bf); + remove_buffer(bf); /* I/O errors on CDROMs are non-fatal to work with hybrid * audio+data disks */ @@ -601,6 +621,9 @@ static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint return NULL; } + if (mprotect(bf->data, len, PROT_READ)) + DBG(LOWPROBE, ul_debug("\tmprotect failed: %m")); + return bf; } @@ -626,6 +649,39 @@ static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uin } /* + * Mark smaller buffers that can be satisfied by bf as prunable + */ +static void mark_prunable_buffers(blkid_probe pr, const struct blkid_bufinfo *bf) +{ + struct list_head *p, *next; + + list_for_each_safe(p, next, &pr->buffers) { + struct blkid_bufinfo *x = + list_entry(p, struct blkid_bufinfo, bufs); + + if (bf->off <= x->off && bf->off + bf->len >= x->off + x->len) { + list_del(&x->bufs); + list_add(&x->bufs, &pr->prunable_buffers); + } + } +} + +/* + * Remove buffers that are marked as prunable + */ +void blkid_probe_prune_buffers(blkid_probe pr) +{ + struct list_head *p, *next; + + list_for_each_safe(p, next, &pr->prunable_buffers) { + struct blkid_bufinfo *x = + list_entry(p, struct blkid_bufinfo, bufs); + + remove_buffer(x); + } +} + +/* * Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer() * will return modified buffer. This is usable when you want to call the same probing * function more than once and hide previously detected magic strings. @@ -657,7 +713,9 @@ static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len) DBG(BUFFER, ul_debug("\thiding: off=%"PRIu64" len=%"PRIu64, off, len)); + mprotect(x->data, x->len, PROT_READ | PROT_WRITE); memset(data, 0, len); + mprotect(x->data, x->len, PROT_READ); ct++; } } @@ -669,16 +727,29 @@ static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len) * Note that @off is offset within probing area, the probing area is defined by * pr->off and pr->size. */ -unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len) +const unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len) { struct blkid_bufinfo *bf = NULL; - uint64_t real_off = pr->off + off; + uint64_t real_off, bias, len_align; + + bias = off % pr->io_size; + off -= bias; + len += bias; + + if (len % pr->io_size) { + len_align = pr->io_size - (len % pr->io_size); + + if (pr->off + off + len + len_align <= pr->size) + len += len_align; + } + + real_off = pr->off + off; /* DBG(BUFFER, ul_debug("\t>>>> off=%ju, real-off=%ju (probe <%ju..%ju>, len=%ju", off, real_off, pr->off, pr->off + pr->size, len)); */ - if (pr->size == 0) { + if (pr->size == 0 || pr->io_size == 0) { errno = EINVAL; return NULL; } @@ -688,6 +759,11 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len return NULL; } + if (len > 8388608 /* 8 Mib */ ) { + DBG(BUFFER, ul_debug("\t too large read request (ignore)")); + return NULL; + } + if (len == 0 || (!S_ISCHR(pr->mode) && (pr->size < off || pr->size < len)) || (!S_ISCHR(pr->mode) && (pr->off + pr->size < real_off + len))) { @@ -718,6 +794,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len if (!bf) return NULL; + mark_prunable_buffers(pr, bf); list_add_tail(&bf->bufs, &pr->buffers); } @@ -725,7 +802,7 @@ unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len assert(bf->off + bf->len >= real_off + len); errno = 0; - return real_off ? bf->data + (real_off - bf->off) : bf->data; + return real_off ? bf->data + (real_off - bf->off + bias) : bf->data + bias; } /** @@ -747,6 +824,8 @@ int blkid_probe_reset_buffers(blkid_probe pr) pr->flags &= ~BLKID_FL_MODIF_BUFF; + blkid_probe_prune_buffers(pr); + if (list_empty(&pr->buffers)) return 0; @@ -757,11 +836,8 @@ int blkid_probe_reset_buffers(blkid_probe pr) struct blkid_bufinfo, bufs); ct++; len += bf->len; - list_del(&bf->bufs); - DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]", - bf->off, bf->len)); - free(bf); + remove_buffer(bf); } DBG(LOWPROBE, ul_debug(" buffers summary: %"PRIu64" bytes by %"PRIu64" read() calls", @@ -891,6 +967,24 @@ failed: #endif +#ifdef BLKIOOPT +static uint64_t blkid_get_io_size(int fd) +{ + static const int ioctls[] = { BLKIOOPT, BLKIOMIN, BLKBSZGET }; + unsigned int s; + size_t i; + int r; + + for (i = 0; i < ARRAY_SIZE(ioctls); i++) { + r = ioctl(fd, ioctls[i], &s); + if (r == 0 && is_power_of_2(s) && s >= DEFAULT_SECTOR_SIZE) + return min(s, 1U << 16); + } + + return DEFAULT_SECTOR_SIZE; +} +#endif + /** * blkid_probe_set_device: * @pr: probe @@ -934,6 +1028,7 @@ int blkid_probe_set_device(blkid_probe pr, int fd, pr->fd = fd; pr->off = (uint64_t) off; pr->size = 0; + pr->io_size = DEFAULT_SECTOR_SIZE; pr->devno = 0; pr->disk_devno = 0; pr->mode = 0; @@ -1097,8 +1192,13 @@ int blkid_probe_set_device(blkid_probe pr, int fd, } # endif - DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64, - pr->off, pr->size, pr->zone_size)); +#ifdef BLKIOOPT + if (S_ISBLK(sb.st_mode) && !is_floppy && !blkid_probe_is_tiny(pr)) + pr->io_size = blkid_get_io_size(fd); +#endif + + DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64", iosize=%"PRIu64, + pr->off, pr->size, pr->zone_size, pr->io_size)); DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s", blkid_probe_is_wholedisk(pr) ?"YES" : "NO", S_ISREG(pr->mode) ? "YES" : "NO")); @@ -1136,14 +1236,28 @@ int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size) return 0; } -unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size) +const unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size) { - uint64_t hint_offset; + uint64_t hint_offset, off; + + if (mag->kboff >= 0) { + if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0) + hint_offset = 0; - if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0) - hint_offset = 0; + off = hint_offset + (mag->kboff << 10); + } else { + off = pr->size - (-mag->kboff << 10); + } + + return blkid_probe_get_buffer(pr, off, size); +} - return blkid_probe_get_buffer(pr, hint_offset + (mag->kboff << 10), size); +uint64_t blkid_probe_get_idmag_off(blkid_probe pr, const struct blkid_idmag *mag) +{ + if (mag->kboff >= 0) + return mag->kboff << 10; + else + return pr->size - (-mag->kboff << 10); } /* @@ -1164,8 +1278,8 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, /* try to detect by magic string */ while(mag && mag->magic) { - unsigned char *buf; - uint64_t kboff; + const unsigned char *buf; + long kboff; uint64_t hint_offset; if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0) @@ -1182,19 +1296,20 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id, else kboff = ((mag->zonenum * pr->zone_size) >> 10) + mag->kboff_inzone; - off = hint_offset + ((kboff + (mag->sboff >> 10)) << 10); - buf = blkid_probe_get_buffer(pr, off, 1024); + if (kboff >= 0) + off = hint_offset + (kboff << 10) + mag->sboff; + else + off = pr->size - (-kboff << 10) + mag->sboff; + buf = blkid_probe_get_buffer(pr, off, mag->len); if (!buf && errno) return -errno; - if (buf && !memcmp(mag->magic, - buf + (mag->sboff & 0x3ff), mag->len)) { - - DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%" PRIu64, + if (buf && !memcmp(mag->magic, buf, mag->len)) { + DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%ld", mag->sboff, kboff)); if (offset) - *offset = off + (mag->sboff & 0x3ff); + *offset = off; if (res) *res = mag; return BLKID_PROBE_OK; @@ -1384,6 +1499,8 @@ static inline int is_conventional(blkid_probe pr __attribute__((__unused__)), * See also blkid_probe_step_back() if you cannot use this built-in wipe * function, but you want to use libblkid probing as a source for wiping. * + * See also blkid_wipe_all() which works the same as the example above. + * * Returns: 0 on success, and -1 in case of error. */ int blkid_do_wipe(blkid_probe pr, int dryrun) @@ -1485,6 +1602,46 @@ int blkid_do_wipe(blkid_probe pr, int dryrun) } /** + * blkid_wipe_all: + * @pr: prober + * + * This function erases all detectable signatures from &pr. + * The @pr has to be open in O_RDWR mode. All other necessary configurations + * will be enabled automatically. + * + * <example> + * <title>wipe all filesystems or raids from the device</title> + * <programlisting> + * fd = open(devname, O_RDWR|O_CLOEXEC); + * blkid_probe_set_device(pr, fd, 0, 0); + * + * blkid_wipe_all(pr); + * </programlisting> + * </example> + * + * Returns: 0 on success, and -1 in case of error. + */ +int blkid_wipe_all(blkid_probe pr) +{ + DBG(LOWPROBE, ul_debug("wiping all signatures")); + + blkid_probe_enable_superblocks(pr, 1); + blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC | + BLKID_SUBLKS_BADCSUM); + + blkid_probe_enable_partitions(pr, 1); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC | + BLKID_PARTS_FORCE_GPT); + + while (blkid_do_probe(pr) == 0) { + DBG(LOWPROBE, ul_debug("wiping one signature")); + blkid_do_wipe(pr, 0); + } + + return BLKID_PROBE_OK; +} + +/** * blkid_probe_step_back: * @pr: prober * @@ -1690,7 +1847,7 @@ done: } /* same sa blkid_probe_get_buffer() but works with 512-sectors */ -unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) +const unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector) { return blkid_probe_get_buffer(pr, ((uint64_t) sector) << 9, 0x200); } diff --git a/libblkid/src/superblocks/adaptec_raid.c b/libblkid/src/superblocks/adaptec_raid.c index 5fc5fc4..813d30e 100644 --- a/libblkid/src/superblocks/adaptec_raid.c +++ b/libblkid/src/superblocks/adaptec_raid.c @@ -79,9 +79,6 @@ static int probe_adraid(blkid_probe pr, uint64_t off; struct adaptec_metadata *ad; - if (pr->size < 0x10000) - return BLKID_PROBE_NONE; - if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return BLKID_PROBE_NONE; @@ -109,6 +106,7 @@ static int probe_adraid(blkid_probe pr, const struct blkid_idinfo adraid_idinfo = { .name = "adaptec_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_adraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/apfs.c b/libblkid/src/superblocks/apfs.c index 048423a..b7f09f3 100644 --- a/libblkid/src/superblocks/apfs.c +++ b/libblkid/src/superblocks/apfs.c @@ -39,7 +39,7 @@ struct apfs_super_block { static int probe_apfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct apfs_super_block *sb; + const struct apfs_super_block *sb; sb = blkid_probe_get_sb(pr, mag, struct apfs_super_block); if (!sb) diff --git a/libblkid/src/superblocks/bcache.c b/libblkid/src/superblocks/bcache.c index d3afc41..47d9060 100644 --- a/libblkid/src/superblocks/bcache.c +++ b/libblkid/src/superblocks/bcache.c @@ -189,7 +189,7 @@ static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, return 0; /* up to the end of bcs->d[] */ - csummed_size = offsetof(typeof(*bcs), d) + + csummed_size = offsetof(__typeof__(*bcs), d) + sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys); csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START, @@ -199,7 +199,7 @@ static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag) { - struct bcache_super_block *bcs; + const struct bcache_super_block *bcs; bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block); if (!bcs) @@ -211,9 +211,18 @@ static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag) if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512) return BLKID_PROBE_NONE; + if (blkid_probe_sprintf_version(pr, "%"PRIu64, le64_to_cpu(bcs->version)) < 0) + return BLKID_PROBE_NONE; + if (blkid_probe_set_uuid(pr, bcs->uuid) < 0) return BLKID_PROBE_NONE; + if (blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label)) < 0) + return BLKID_PROBE_NONE; + + if (blkid_probe_set_block_size(pr, le16_to_cpu(bcs->block_size) * 512)) + return BLKID_PROBE_NONE; + blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF); return BLKID_PROBE_OK; @@ -229,7 +238,7 @@ static void probe_bcachefs_sb_members(blkid_probe pr, uint64_t sectors = 0; uint8_t i; - if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices])) + if (BYTES(field) != offsetof(__typeof__(*members), members[bcs->nr_devices])) return; blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB"); @@ -249,7 +258,7 @@ static void probe_bcachefs_sb_disk_groups(blkid_probe pr, struct bcachefs_sb_field_disk_groups *disk_groups = (struct bcachefs_sb_field_disk_groups *) field; - if (BYTES(field) != offsetof(typeof(*disk_groups), disk_groups[bcs->nr_devices])) + if (BYTES(field) != offsetof(__typeof__(*disk_groups), disk_groups[bcs->nr_devices])) return; blkid_probe_set_id_label(pr, "LABEL_SUB", @@ -257,7 +266,7 @@ static void probe_bcachefs_sb_disk_groups(blkid_probe pr, sizeof(disk_groups->disk_groups[dev_idx].label)); } -static int is_within_range(void *start, uint64_t size, void *end) +static int is_within_range(const void *start, uint64_t size, const void *end) { ptrdiff_t diff; @@ -269,9 +278,9 @@ static int is_within_range(void *start, uint64_t size, void *end) } static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs, - unsigned char *sb_start, unsigned char *sb_end) + const unsigned char *sb_start, const unsigned char *sb_end) { - unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF; + const unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF; while (1) { struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr; @@ -304,10 +313,10 @@ static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super } static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs, - unsigned char *sb, unsigned char *sb_end) + const unsigned char *sb, const unsigned char *sb_end) { uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58; - unsigned char *checksummed_data_start = sb + sizeof(bcs->csum); + const unsigned char *checksummed_data_start = sb + sizeof(bcs->csum); size_t checksummed_data_size = sb_end - checksummed_data_start; switch (checksum_type) { @@ -333,16 +342,17 @@ static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_supe static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag) { - struct bcachefs_super_block *bcs; - unsigned char *sb, *sb_end; - uint64_t sb_size, blocksize; + const struct bcachefs_super_block *bcs; + const unsigned char *sb, *sb_end; + uint64_t sb_size, blocksize, offset_sectors; uint16_t version; bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block); if (!bcs) return errno ? -errno : BLKID_PROBE_NONE; - if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE) + offset_sectors = blkid_probe_get_idmag_off(pr, mag) / BCACHEFS_SECTOR_SIZE; + if (le64_to_cpu(bcs->offset) != offset_sectors) return BLKID_PROBE_NONE; if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices) @@ -420,6 +430,18 @@ const struct blkid_idinfo bcachefs_idinfo = .kboff = BCACHE_SB_KBOFF, .sboff = BCACHE_SB_MAGIC_OFF, }, + { + .magic = BCACHEFS_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = 1 << 11, + .sboff = BCACHE_SB_MAGIC_OFF, + }, + { + .magic = BCACHEFS_SB_MAGIC, + .len = BCACHE_SB_MAGIC_LEN, + .kboff = -(1 << 10), + .sboff = BCACHE_SB_MAGIC_OFF, + }, { NULL } } }; diff --git a/libblkid/src/superblocks/befs.c b/libblkid/src/superblocks/befs.c index 5112d44..d501eb1 100644 --- a/libblkid/src/superblocks/befs.c +++ b/libblkid/src/superblocks/befs.c @@ -122,7 +122,7 @@ struct bplustree_node { char name[0]; } __attribute__((packed)); -static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs, +static const unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs, const struct block_run *br, int fs_le) { return blkid_probe_get_buffer(pr, @@ -135,7 +135,7 @@ static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_bloc << FS32_TO_CPU(bs->block_shift, fs_le)); } -static unsigned char *get_custom_block_run(blkid_probe pr, +static const unsigned char *get_custom_block_run(blkid_probe pr, const struct befs_super_block *bs, const struct block_run *br, int64_t offset, uint32_t length, int fs_le) @@ -154,7 +154,7 @@ static unsigned char *get_custom_block_run(blkid_probe pr, length); } -static unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs, +static const unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs, const struct data_stream *ds, int64_t start, uint32_t length, int fs_le) { diff --git a/libblkid/src/superblocks/bluestore.c b/libblkid/src/superblocks/bluestore.c index 2ff1f35..f9178d9 100644 --- a/libblkid/src/superblocks/bluestore.c +++ b/libblkid/src/superblocks/bluestore.c @@ -31,7 +31,7 @@ struct bluestore_phdr { static int probe_bluestore(blkid_probe pr, const struct blkid_idmag *mag) { - struct bluestore_phdr *header; + const struct bluestore_phdr *header; header = blkid_probe_get_sb(pr, mag, struct bluestore_phdr); if (header == NULL) diff --git a/libblkid/src/superblocks/btrfs.c b/libblkid/src/superblocks/btrfs.c index b9cf4bd..114d348 100644 --- a/libblkid/src/superblocks/btrfs.c +++ b/libblkid/src/superblocks/btrfs.c @@ -250,7 +250,7 @@ static int btrfs_verify_csum(blkid_probe pr, const struct btrfs_super_block *bfs static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct btrfs_super_block *bfs; + const struct btrfs_super_block *bfs; if (pr->zone_size) { #ifdef HAVE_LINUX_BLKZONED_H diff --git a/libblkid/src/superblocks/cramfs.c b/libblkid/src/superblocks/cramfs.c index 2a87acd..6a8731f 100644 --- a/libblkid/src/superblocks/cramfs.c +++ b/libblkid/src/superblocks/cramfs.c @@ -37,12 +37,6 @@ struct cramfs_super #define CRAMFS_FLAG_FSID_VERSION_2 0x00000001 /* fsid version #2 */ -static int cramfs_is_little_endian(const struct blkid_idmag *mag) -{ - assert(mag->len == 4); - return memcmp(mag->magic, "\x45\x3d\xcd\x28", 4) == 0; -} - static uint32_t cfs32_to_cpu(int le, uint32_t value) { if (le) @@ -52,10 +46,10 @@ static uint32_t cfs32_to_cpu(int le, uint32_t value) } static int cramfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag, - struct cramfs_super *cs, int le) + const struct cramfs_super *cs, int le) { uint32_t crc, expected, csummed_size; - unsigned char *csummed; + const unsigned char *csummed; expected = cfs32_to_cpu(le, cs->info.crc); csummed_size = cfs32_to_cpu(le, cs->size); @@ -67,22 +61,23 @@ static int cramfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag, csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size); if (!csummed) return 0; - memset(csummed + offsetof(struct cramfs_super, info.crc), 0, sizeof(uint32_t)); - crc = ~ul_crc32(~0LL, csummed, csummed_size); + crc = ~ul_crc32_exclude_offset(~0LL, csummed, csummed_size, + offsetof(struct cramfs_super, info.crc), + sizeof_member(struct cramfs_super, info.crc)); return blkid_probe_verify_csum(pr, crc, expected); } static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct cramfs_super *cs; + const struct cramfs_super *cs; cs = blkid_probe_get_sb(pr, mag, struct cramfs_super); if (!cs) return errno ? -errno : 1; - int le = cramfs_is_little_endian(mag); + int le = mag->hint == BLKID_ENDIANNESS_LITTLE; int v2 = cfs32_to_cpu(le, cs->flags) & CRAMFS_FLAG_FSID_VERSION_2; if (v2 && !cramfs_verify_csum(pr, mag, cs, le)) @@ -91,8 +86,7 @@ static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag) blkid_probe_set_label(pr, cs->name, sizeof(cs->name)); blkid_probe_set_fssize(pr, cfs32_to_cpu(le, cs->size)); blkid_probe_sprintf_version(pr, "%d", v2 ? 2 : 1); - blkid_probe_set_fsendianness(pr, - le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG); + blkid_probe_set_fsendianness(pr, mag->hint); return 0; } @@ -103,8 +97,10 @@ const struct blkid_idinfo cramfs_idinfo = .probefunc = probe_cramfs, .magics = { - { .magic = "\x45\x3d\xcd\x28", .len = 4 }, - { .magic = "\x28\xcd\x3d\x45", .len = 4 }, + { .magic = "\x45\x3d\xcd\x28", .len = 4, + .hint = BLKID_ENDIANNESS_LITTLE }, + { .magic = "\x28\xcd\x3d\x45", .len = 4, + .hint = BLKID_ENDIANNESS_BIG }, { NULL } } }; diff --git a/libblkid/src/superblocks/cs_fvault2.c b/libblkid/src/superblocks/cs_fvault2.c index ef2b567..187258d 100644 --- a/libblkid/src/superblocks/cs_fvault2.c +++ b/libblkid/src/superblocks/cs_fvault2.c @@ -65,7 +65,7 @@ static int cs_fvault2_verify_csum(blkid_probe pr, const struct cs_fvault2_sb *sb static int probe_cs_fvault2(blkid_probe pr, const struct blkid_idmag *mag) { - struct cs_fvault2_sb *sb; + const struct cs_fvault2_sb *sb; sb = blkid_probe_get_sb(pr, mag, struct cs_fvault2_sb); if (!sb) diff --git a/libblkid/src/superblocks/ddf_raid.c b/libblkid/src/superblocks/ddf_raid.c index 0b82e73..a7cf32c 100644 --- a/libblkid/src/superblocks/ddf_raid.c +++ b/libblkid/src/superblocks/ddf_raid.c @@ -80,9 +80,6 @@ static int probe_ddf(blkid_probe pr, char version[DDF_REV_LENGTH + 1]; uint64_t off = 0, lba; - if (pr->size < 0x30000) - return 1; - for (i = 0; i < ARRAY_SIZE(hdrs); i++) { off = ((pr->size / 0x200) - hdrs[i]) * 0x200; @@ -106,7 +103,7 @@ static int probe_ddf(blkid_probe pr, if (lba > 0) { /* check primary header */ - unsigned char *buf; + const unsigned char *buf; buf = blkid_probe_get_buffer(pr, lba << 9, sizeof(ddf->signature)); @@ -134,6 +131,7 @@ static int probe_ddf(blkid_probe pr, const struct blkid_idinfo ddfraid_idinfo = { .name = "ddf_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x30000, .probefunc = probe_ddf, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/drbd.c b/libblkid/src/superblocks/drbd.c index f360186..96a2168 100644 --- a/libblkid/src/superblocks/drbd.c +++ b/libblkid/src/superblocks/drbd.c @@ -18,18 +18,24 @@ #include "superblocks.h" +enum { + DRBD_VERSION_08, + DRBD_VERSION_09, +}; + /* - * drbd/linux/drbd.h + * drbd/drbd_int.h */ -#define DRBD_MAGIC 0x83740267 +#define BM_BLOCK_SHIFT 12 /* 4k per bit */ +#define BM_BLOCK_SIZE (1<<BM_BLOCK_SHIFT) /* * user/drbdmeta.c * We support v08 and v09 */ -#define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4) -#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5) -#define DRBD_MD_MAGIC_09 (DRBD_MAGIC+6) +#define DRBD_MD_MAGIC_08 "\x83\x74\x02\x6b" +#define DRBD_MD_MAGIC_84_UNCLEAN "\x83\x74\x02\x6c" +#define DRBD_MD_MAGIC_09 "\x83\x74\x02\x6d" /* there is no DRBD_MD_MAGIC_09_UNCLEAN */ /* @@ -70,9 +76,8 @@ struct md_on_disk_08 { uint32_t bm_bytes_per_bit; uint32_t reserved_u32[4]; - /* Unnecessary for libblkid ** - * char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)]; - */ + unsigned char padding_start[0]; + unsigned char padding_end[0] __attribute__((aligned(4096))); }; /* @@ -118,32 +123,34 @@ struct meta_data_on_disk_9 { struct peer_dev_md_on_disk_9 peers[DRBD_PEERS_MAX]; uint64_t history_uuids[HISTORY_UUIDS]; - /* Unnecessary for libblkid ** - * char padding[0] __attribute__((aligned(4096))); - */ + unsigned char padding_start[0]; + unsigned char padding_end[0] __attribute__((aligned(4096))); } __attribute__((packed)); -static int probe_drbd_84(blkid_probe pr) +static int is_zero_padded(const unsigned char *padding_start, + const unsigned char *padding_end) { - struct md_on_disk_08 *md; - off_t off; - - off = pr->size - DRBD_MD_OFFSET; + for (; padding_start < padding_end; padding_start++) { + if (*padding_start != 0) + return 0; + } + return 1; +} - /* Small devices cannot be drbd (?) */ - if (pr->size < 0x10000) - return 1; +static int probe_drbd_84(blkid_probe pr, const struct blkid_idmag *mag) +{ + const struct md_on_disk_08 *md; - md = (struct md_on_disk_08 *) - blkid_probe_get_buffer(pr, - off, - sizeof(struct md_on_disk_08)); + md = blkid_probe_get_sb(pr, mag, struct md_on_disk_08); if (!md) return errno ? -errno : 1; - if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_08 && - be32_to_cpu(md->magic) != DRBD_MD_MAGIC_84_UNCLEAN) + if (be32_to_cpu(read_unaligned_member(md, bm_bytes_per_bit)) != BM_BLOCK_SIZE) + return 1; + + if (!is_zero_padded(member_ptr(md, padding_start), + member_ptr(md, padding_end))) return 1; /* @@ -151,43 +158,27 @@ static int probe_drbd_84(blkid_probe pr) * notion of uuids (64 bit, see struct above) */ blkid_probe_sprintf_uuid(pr, - (unsigned char *) &md->device_uuid, sizeof(md->device_uuid), - "%" PRIx64, be64_to_cpu(md->device_uuid)); + member_ptr(md, device_uuid), sizeof(md->device_uuid), + "%" PRIx64, be64_to_cpu(read_unaligned_member(md, device_uuid))); blkid_probe_set_version(pr, "v08"); - if (blkid_probe_set_magic(pr, - off + offsetof(struct md_on_disk_08, magic), - sizeof(md->magic), - (unsigned char *) &md->magic)) - return 1; - return 0; } -static int probe_drbd_90(blkid_probe pr) +static int probe_drbd_90(blkid_probe pr, const struct blkid_idmag *mag) { - struct meta_data_on_disk_9 *md; - off_t off; - - off = pr->size - DRBD_MD_OFFSET; - - /* - * Smaller ones are certainly not DRBD9 devices. - * Recent utils even refuse to generate larger ones, - * keep this as a sufficient lower bound. - */ - if (pr->size < 0x10000) - return 1; + const struct meta_data_on_disk_9 *md; - md = (struct meta_data_on_disk_9 *) - blkid_probe_get_buffer(pr, - off, - sizeof(struct meta_data_on_disk_9)); + md = blkid_probe_get_sb(pr, mag, struct meta_data_on_disk_9); if (!md) return errno ? -errno : 1; - if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_09) + if (be32_to_cpu(read_unaligned_member(md, bm_bytes_per_bit)) != BM_BLOCK_SIZE) + return 1; + + if (!is_zero_padded(member_ptr(md, padding_start), + member_ptr(md, padding_end))) return 1; /* @@ -195,30 +186,23 @@ static int probe_drbd_90(blkid_probe pr) * notion of uuids (64 bit, see struct above) */ blkid_probe_sprintf_uuid(pr, - (unsigned char *) &md->device_uuid, sizeof(md->device_uuid), - "%" PRIx64, be64_to_cpu(md->device_uuid)); + member_ptr(md, device_uuid), sizeof(md->device_uuid), + "%" PRIx64, be64_to_cpu(read_unaligned_member(md, device_uuid))); blkid_probe_set_version(pr, "v09"); - if (blkid_probe_set_magic(pr, - off + offsetof(struct meta_data_on_disk_9, magic), - sizeof(md->magic), - (unsigned char *) &md->magic)) - return 1; - return 0; } -static int probe_drbd(blkid_probe pr, - const struct blkid_idmag *mag __attribute__((__unused__))) +static int probe_drbd(blkid_probe pr, const struct blkid_idmag *mag) { - int ret; + if (mag->hint == DRBD_VERSION_08) + return probe_drbd_84(pr, mag); - ret = probe_drbd_84(pr); - if (ret <= 0) /* success or fatal (-errno) */ - return ret; + if (mag->hint == DRBD_VERSION_09) + return probe_drbd_90(pr, mag); - return probe_drbd_90(pr); + return 1; } const struct blkid_idinfo drbd_idinfo = @@ -226,6 +210,35 @@ const struct blkid_idinfo drbd_idinfo = .name = "drbd", .usage = BLKID_USAGE_RAID, .probefunc = probe_drbd, - .magics = BLKID_NONE_MAGIC + /* + * Smaller ones are certainly not DRBD9 devices. + * Recent utils even refuse to generate larger ones, + * keep this as a sufficient lower bound. + */ + .minsz = 0x10000, + .magics = { + { + .magic = DRBD_MD_MAGIC_08, + .len = sizeof(DRBD_MD_MAGIC_08) - 1, + .hint = DRBD_VERSION_08, + .kboff = -(DRBD_MD_OFFSET >> 10), + .sboff = offsetof(struct md_on_disk_08, magic), + }, + { + .magic = DRBD_MD_MAGIC_84_UNCLEAN, + .len = sizeof(DRBD_MD_MAGIC_84_UNCLEAN) - 1, + .hint = DRBD_VERSION_08, + .kboff = -(DRBD_MD_OFFSET >> 10), + .sboff = offsetof(struct md_on_disk_08, magic), + }, + { + .magic = DRBD_MD_MAGIC_09, + .len = sizeof(DRBD_MD_MAGIC_09) - 1, + .hint = DRBD_VERSION_09, + .kboff = -(DRBD_MD_OFFSET >> 10), + .sboff = offsetof(struct meta_data_on_disk_9, magic), + }, + { NULL } + } }; diff --git a/libblkid/src/superblocks/erofs.c b/libblkid/src/superblocks/erofs.c index 14d272e..0582246 100644 --- a/libblkid/src/superblocks/erofs.c +++ b/libblkid/src/superblocks/erofs.c @@ -46,7 +46,7 @@ static int erofs_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, { uint32_t expected, csum; size_t csummed_size; - unsigned char *csummed; + const unsigned char *csummed; if (!(le32_to_cpu(sb->feature_compat) & EROFS_FEATURE_SB_CSUM)) return 1; @@ -67,7 +67,7 @@ static int erofs_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag, static int probe_erofs(blkid_probe pr, const struct blkid_idmag *mag) { - struct erofs_super_block *sb; + const struct erofs_super_block *sb; sb = blkid_probe_get_sb(pr, mag, struct erofs_super_block); if (!sb) diff --git a/libblkid/src/superblocks/exfat.c b/libblkid/src/superblocks/exfat.c index fda1ecd..18a3e07 100644 --- a/libblkid/src/superblocks/exfat.c +++ b/libblkid/src/superblocks/exfat.c @@ -121,7 +121,7 @@ static struct exfat_entry_label *find_label(blkid_probe pr, } /* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */ -static uint32_t exfat_boot_checksum(unsigned char *sectors, +static uint32_t exfat_boot_checksum(const unsigned char *sectors, size_t sector_size) { uint32_t n_bytes = sector_size * 11; @@ -143,7 +143,7 @@ static int exfat_validate_checksum(blkid_probe pr, { size_t sector_size = BLOCK_SIZE(sb); /* 11 sectors will be checksummed, the 12th contains the expected */ - unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12); + const unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12); if (!data) return 0; @@ -191,7 +191,7 @@ extern int blkid_probe_is_exfat(blkid_probe pr); */ int blkid_probe_is_exfat(blkid_probe pr) { - struct exfat_super_block *sb; + const struct exfat_super_block *sb; const struct blkid_idmag *mag = NULL; int rc; @@ -213,7 +213,7 @@ int blkid_probe_is_exfat(blkid_probe pr) static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag) { - struct exfat_super_block *sb; + const struct exfat_super_block *sb; struct exfat_entry_label *label; sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); diff --git a/libblkid/src/superblocks/exfs.c b/libblkid/src/superblocks/exfs.c index 87dc0a0..0a169bd 100644 --- a/libblkid/src/superblocks/exfs.c +++ b/libblkid/src/superblocks/exfs.c @@ -85,7 +85,7 @@ struct exfs_super_block { (s)->sb_agblocks + EXFS_MIN_AG_BLOCKS) -static void sb_from_disk(struct exfs_super_block *from, +static void sb_from_disk(const struct exfs_super_block *from, struct exfs_super_block *to) { @@ -121,7 +121,7 @@ static void sb_from_disk(struct exfs_super_block *from, to->sb_frextents = be64_to_cpu(from->sb_frextents); } -static int exfs_verify_sb(struct exfs_super_block *ondisk) +static int exfs_verify_sb(const struct exfs_super_block *ondisk) { struct exfs_super_block sb, *sbp = &sb; @@ -158,7 +158,7 @@ static int exfs_verify_sb(struct exfs_super_block *ondisk) static int probe_exfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct exfs_super_block *xs; + const struct exfs_super_block *xs; xs = blkid_probe_get_sb(pr, mag, struct exfs_super_block); if (!xs) diff --git a/libblkid/src/superblocks/f2fs.c b/libblkid/src/superblocks/f2fs.c index 980111e..4e0fdbf 100644 --- a/libblkid/src/superblocks/f2fs.c +++ b/libblkid/src/superblocks/f2fs.c @@ -67,14 +67,14 @@ static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off, if (csum_off + sizeof(uint32_t) > 4096) return 0; - unsigned char *csum_data = blkid_probe_get_buffer(pr, + const unsigned char *csum_data = blkid_probe_get_buffer(pr, sb_off + csum_off, sizeof(uint32_t)); if (!csum_data) return 0; uint32_t expected = le32_to_cpu(*(uint32_t *) csum_data); - unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off); + const unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off); if (!csummed) return 0; @@ -85,7 +85,7 @@ static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off, static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag) { - struct f2fs_super_block *sb; + const struct f2fs_super_block *sb; uint16_t vermaj, vermin; sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block); diff --git a/libblkid/src/superblocks/gfs.c b/libblkid/src/superblocks/gfs.c index 445c0da..18d978a 100644 --- a/libblkid/src/superblocks/gfs.c +++ b/libblkid/src/superblocks/gfs.c @@ -59,7 +59,7 @@ struct gfs2_sb { static int probe_gfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct gfs2_sb *sbd; + const struct gfs2_sb *sbd; sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb); if (!sbd) @@ -91,7 +91,7 @@ static inline int gfs2_multiformat_is_valid(uint32_t multi) static int probe_gfs2(blkid_probe pr, const struct blkid_idmag *mag) { - struct gfs2_sb *sbd; + const struct gfs2_sb *sbd; sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb); if (!sbd) diff --git a/libblkid/src/superblocks/hfs.c b/libblkid/src/superblocks/hfs.c index 68cb30e..49a0e60 100644 --- a/libblkid/src/superblocks/hfs.c +++ b/libblkid/src/superblocks/hfs.c @@ -154,7 +154,7 @@ static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t le static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct hfs_mdb *hfs; + const struct hfs_mdb *hfs; int size; hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb); @@ -183,11 +183,11 @@ static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag) static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) { struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; - struct hfsplus_bnode_descriptor *descr; - struct hfsplus_bheader_record *bnode; - struct hfsplus_catalog_key *key; - struct hfsplus_vol_header *hfsplus; - struct hfs_mdb *sbd; + const struct hfsplus_bnode_descriptor *descr; + const struct hfsplus_bheader_record *bnode; + const struct hfsplus_catalog_key *key; + const struct hfsplus_vol_header *hfsplus; + const struct hfs_mdb *sbd; unsigned int alloc_block_size; unsigned int alloc_first_block; unsigned int embed_first_block; @@ -203,7 +203,7 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) unsigned int leaf_block; int ext; uint64_t leaf_off; - unsigned char *buf; + const unsigned char *buf; sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb); if (!sbd) @@ -217,6 +217,10 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) return 1; alloc_block_size = be32_to_cpu(sbd->al_blk_size); + if (alloc_block_size < HFSPLUS_SECTOR_SIZE || + alloc_block_size % HFSPLUS_SECTOR_SIZE) + return 1; + alloc_first_block = be16_to_cpu(sbd->al_bl_st); embed_first_block = be16_to_cpu(sbd->embed_startblock); off = (alloc_first_block * 512) + @@ -225,7 +229,7 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) buf = blkid_probe_get_buffer(pr, off + (mag->kboff * 1024), sizeof(struct hfsplus_vol_header)); - hfsplus = (struct hfsplus_vol_header *) buf; + hfsplus = (const struct hfsplus_vol_header *) buf; } else hfsplus = blkid_probe_get_sb(pr, mag, @@ -238,17 +242,23 @@ static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag) (memcmp(hfsplus->signature, "HX", 2) != 0)) return 1; - hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id)); - + /* Verify blocksize is initialized */ blocksize = be32_to_cpu(hfsplus->blocksize); - if (blocksize < HFSPLUS_SECTOR_SIZE) + if (blocksize < HFSPLUS_SECTOR_SIZE || !is_power_of_2(blocksize)) return 1; - blkid_probe_set_fsblocksize(pr, blocksize); - blkid_probe_set_block_size(pr, blocksize); - + /* Save extends (hfsplus buffer may be later overwritten) */ memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); + + /* Make sure start_block is properly initialized */ cat_block = be32_to_cpu(extents[0].start_block); + if (off + ((uint64_t) cat_block * blocksize) > pr->size) + return 1; + + hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id)); + + blkid_probe_set_fsblocksize(pr, blocksize); + blkid_probe_set_block_size(pr, blocksize); buf = blkid_probe_get_buffer(pr, off + ((uint64_t) cat_block * blocksize), 0x2000); diff --git a/libblkid/src/superblocks/highpoint_raid.c b/libblkid/src/superblocks/highpoint_raid.c index 2487930..c8b1297 100644 --- a/libblkid/src/superblocks/highpoint_raid.c +++ b/libblkid/src/superblocks/highpoint_raid.c @@ -29,8 +29,6 @@ static int probe_highpoint45x(blkid_probe pr, uint64_t off; uint32_t magic; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -62,6 +60,7 @@ static int probe_highpoint37x(blkid_probe pr, const struct blkid_idinfo highpoint45x_idinfo = { .name = "hpt45x_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_highpoint45x, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/hpfs.c b/libblkid/src/superblocks/hpfs.c index 09bf975..f7ee02d 100644 --- a/libblkid/src/superblocks/hpfs.c +++ b/libblkid/src/superblocks/hpfs.c @@ -60,9 +60,9 @@ struct hpfs_spare_super static int probe_hpfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct hpfs_super_block *hs; - struct hpfs_spare_super *hss; - struct hpfs_boot_block *hbb; + const struct hpfs_super_block *hs; + const struct hpfs_spare_super *hss; + const struct hpfs_boot_block *hbb; uint8_t version; /* super block */ diff --git a/libblkid/src/superblocks/iso9660.c b/libblkid/src/superblocks/iso9660.c index 536704b..a7a364f 100644 --- a/libblkid/src/superblocks/iso9660.c +++ b/libblkid/src/superblocks/iso9660.c @@ -22,7 +22,7 @@ #include "cctype.h" #include "iso9660.h" -struct iso9660_date { +struct hs_date { unsigned char year[4]; unsigned char month[2]; unsigned char day[2]; @@ -30,11 +30,16 @@ struct iso9660_date { unsigned char minute[2]; unsigned char second[2]; unsigned char hundredth[2]; +} __attribute__ ((packed)); + +struct iso9660_date { + struct hs_date common; unsigned char offset; } __attribute__ ((packed)); /* PVD - Primary volume descriptor */ struct iso_volume_descriptor { + /* High Sierra has 8 bytes before descriptor with Volume Descriptor LBN value, those are skipped by blkid_probe_get_buffer() */ unsigned char vd_type; unsigned char vd_id[5]; unsigned char vd_version; @@ -43,21 +48,59 @@ struct iso_volume_descriptor { unsigned char volume_id[32]; unsigned char unused[8]; unsigned char space_size[8]; - unsigned char escape_sequences[8]; - unsigned char unused2[32]; + unsigned char escape_sequences[32]; + unsigned char set_size[4]; + unsigned char vol_seq_num[4]; unsigned char logical_block_size[4]; - unsigned char unused3[58]; - unsigned char volume_set_id[128]; - unsigned char publisher_id[128]; - unsigned char data_preparer_id[128]; - unsigned char application_id[128]; - unsigned char unused4[111]; - struct iso9660_date created; - struct iso9660_date modified; + unsigned char path_table_size[8]; + union { + struct { + unsigned char type_l_path_table[4]; + unsigned char opt_type_l_path_table[4]; + unsigned char type_m_path_table[4]; + unsigned char opt_type_m_path_table[4]; + unsigned char root_dir_record[34]; + unsigned char volume_set_id[128]; + unsigned char publisher_id[128]; + unsigned char data_preparer_id[128]; + unsigned char application_id[128]; + unsigned char copyright_file_id[37]; + unsigned char abstract_file_id[37]; + unsigned char bibliographic_file_id[37]; + struct iso9660_date created; + struct iso9660_date modified; + struct iso9660_date expiration; + struct iso9660_date effective; + unsigned char std_version; + } iso; /* ISO9660 */ + struct { + unsigned char type_l_path_table[4]; + unsigned char opt1_type_l_path_table[4]; + unsigned char opt2_type_l_path_table[4]; + unsigned char opt3_type_l_path_table[4]; + unsigned char type_m_path_table[4]; + unsigned char opt1_type_m_path_table[4]; + unsigned char opt2_type_m_path_table[4]; + unsigned char opt3_type_m_path_table[4]; + unsigned char root_dir_record[34]; + unsigned char volume_set_id[128]; + unsigned char publisher_id[128]; + unsigned char data_preparer_id[128]; + unsigned char application_id[128]; + unsigned char copyright_file_id[32]; + unsigned char abstract_file_id[32]; + struct hs_date created; + struct hs_date modified; + struct hs_date expiration; + struct hs_date effective; + unsigned char std_version; + } hs; /* High Sierra */ + }; } __attribute__((packed)); /* Boot Record */ struct boot_record { + /* High Sierra has 8 bytes before descriptor with Volume Descriptor LBN value, those are skipped by blkid_probe_get_buffer() */ unsigned char vd_type; unsigned char vd_id[5]; unsigned char vd_version; @@ -74,35 +117,9 @@ struct boot_record { #define ISO_VD_END 0xff #define ISO_VD_MAX 16 /* maximal string field size used anywhere in ISO; update if necessary */ -#define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, volume_set_id) - -struct high_sierra_volume_descriptor { - unsigned char foo[8]; - unsigned char type; - unsigned char id[5]; - unsigned char version; - unsigned char unused1; - unsigned char system_id[32]; - unsigned char volume_id[32]; -} __attribute__((packed)); - -/* old High Sierra format */ -static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag) -{ - struct high_sierra_volume_descriptor *iso; +#define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, iso.volume_set_id) - iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor); - if (!iso) - return errno ? -errno : 1; - - blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE); - blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE); - blkid_probe_set_version(pr, "High Sierra"); - blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id)); - return 0; -} - -static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date) +static int probe_iso9660_set_uuid (blkid_probe pr, const struct hs_date *date, unsigned char offset) { unsigned char buffer[16]; unsigned int i, zeros = 0; @@ -130,7 +147,7 @@ static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *da zeros++; /* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */ - if (zeros == sizeof(buffer) && date->offset == 0) + if (zeros == sizeof(buffer) && offset == 0) return 0; /* generate an UUID using this date and return success */ @@ -216,9 +233,15 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) struct iso_volume_descriptor *joliet = NULL; /* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */ unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2]; + const struct hs_date *modified; + const struct hs_date *created; + unsigned char modified_offset; + unsigned char created_offset; size_t len; + int is_hs; int is_unicode_empty; - int is_ascii_empty; + int is_ascii_hs_empty; + int is_ascii_iso_empty; int i; uint64_t off; @@ -228,13 +251,12 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) if (off % ISO_SECTOR_SIZE) return 1; - if (strcmp(mag->magic, "CDROM") == 0) - return probe_iso9660_hsfs(pr, mag); + is_hs = (strcmp(mag->magic, "CDROM") == 0); - for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || !joliet); i++, off += ISO_SECTOR_SIZE) { - unsigned char *desc = + for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || (!is_hs && !joliet)); i++, off += ISO_SECTOR_SIZE) { + const unsigned char *desc = blkid_probe_get_buffer(pr, - off, + off + (is_hs ? 8 : 0), /* High Sierra has 8 bytes before descriptor with Volume Descriptor LBN value */ max(sizeof(struct boot_record), sizeof(struct iso_volume_descriptor))); @@ -244,7 +266,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) boot = (struct boot_record *)desc; else if (!pvd && desc[0] == ISO_VD_PRIMARY) pvd = (struct iso_volume_descriptor *)desc; - else if (!joliet && desc[0] == ISO_VD_SUPPLEMENTARY) { + else if (!is_hs && !joliet && desc[0] == ISO_VD_SUPPLEMENTARY) { joliet = (struct iso_volume_descriptor *)desc; if (memcmp(joliet->escape_sequences, "%/@", 3) != 0 && memcmp(joliet->escape_sequences, "%/C", 3) != 0 && @@ -270,43 +292,66 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) else blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id)); - if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0) + if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.volume_set_id, pvd->iso.volume_set_id, sizeof(pvd->iso.volume_set_id))) != 0) blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE); else if (joliet) - blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE); + blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->iso.volume_set_id, sizeof(joliet->iso.volume_set_id), UL_ENCODE_UTF16BE); + else if (is_hs) + blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->hs.volume_set_id, sizeof(pvd->hs.volume_set_id)); else - blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->volume_set_id, sizeof(pvd->volume_set_id)); + blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->iso.volume_set_id, sizeof(pvd->iso.volume_set_id)); - is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_'); - is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_')); - if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0) + is_ascii_hs_empty = (!is_hs || is_str_empty(pvd->hs.publisher_id, sizeof(pvd->hs.publisher_id)) || pvd->hs.publisher_id[0] == '_'); + is_ascii_iso_empty = (is_hs || is_str_empty(pvd->iso.publisher_id, sizeof(pvd->iso.publisher_id)) || pvd->iso.publisher_id[0] == '_'); + is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->iso.publisher_id, sizeof(joliet->iso.publisher_id)) || (joliet->iso.publisher_id[0] == 0x00 && joliet->iso.publisher_id[1] == '_')); + if (!is_unicode_empty && !is_ascii_iso_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.publisher_id, pvd->iso.publisher_id, sizeof(pvd->iso.publisher_id))) != 0) blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE); else if (!is_unicode_empty) - blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE); - else if (!is_ascii_empty) - blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->publisher_id, sizeof(pvd->publisher_id)); - - is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_'); - is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_')); - if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->iso.publisher_id, sizeof(joliet->iso.publisher_id), UL_ENCODE_UTF16BE); + else if (!is_ascii_hs_empty) + blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->hs.publisher_id, sizeof(pvd->hs.publisher_id)); + else if (!is_ascii_iso_empty) + blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->iso.publisher_id, sizeof(pvd->iso.publisher_id)); + + is_ascii_hs_empty = (!is_hs || is_str_empty(pvd->hs.data_preparer_id, sizeof(pvd->hs.data_preparer_id)) || pvd->hs.data_preparer_id[0] == '_'); + is_ascii_iso_empty = (is_hs || is_str_empty(pvd->iso.data_preparer_id, sizeof(pvd->iso.data_preparer_id)) || pvd->iso.data_preparer_id[0] == '_'); + is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->iso.data_preparer_id, sizeof(joliet->iso.data_preparer_id)) || (joliet->iso.data_preparer_id[0] == 0x00 && joliet->iso.data_preparer_id[1] == '_')); + if (!is_unicode_empty && !is_ascii_iso_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.data_preparer_id, pvd->iso.data_preparer_id, sizeof(pvd->iso.data_preparer_id))) != 0) blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE); else if (!is_unicode_empty) - blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE); - else if (!is_ascii_empty) - blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->data_preparer_id, sizeof(pvd->data_preparer_id)); - - is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_'); - is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_')); - if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0) + blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->iso.data_preparer_id, sizeof(joliet->iso.data_preparer_id), UL_ENCODE_UTF16BE); + else if (!is_ascii_hs_empty) + blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->hs.data_preparer_id, sizeof(pvd->hs.data_preparer_id)); + else if (!is_ascii_iso_empty) + blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->iso.data_preparer_id, sizeof(pvd->iso.data_preparer_id)); + + is_ascii_hs_empty = (!is_hs || is_str_empty(pvd->hs.application_id, sizeof(pvd->hs.application_id)) || pvd->hs.application_id[0] == '_'); + is_ascii_iso_empty = (is_hs || is_str_empty(pvd->iso.application_id, sizeof(pvd->iso.application_id)) || pvd->iso.application_id[0] == '_'); + is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->iso.application_id, sizeof(joliet->iso.application_id)) || (joliet->iso.application_id[0] == 0x00 && joliet->iso.application_id[1] == '_')); + if (!is_unicode_empty && !is_ascii_iso_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->iso.application_id, pvd->iso.application_id, sizeof(pvd->iso.application_id))) != 0) blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE); else if (!is_unicode_empty) - blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE); - else if (!is_ascii_empty) - blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->application_id, sizeof(pvd->application_id)); + blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->iso.application_id, sizeof(joliet->iso.application_id), UL_ENCODE_UTF16BE); + else if (!is_ascii_hs_empty) + blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->hs.application_id, sizeof(pvd->hs.application_id)); + else if (!is_ascii_iso_empty) + blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->iso.application_id, sizeof(pvd->iso.application_id)); + + if (is_hs) { + modified = &pvd->hs.modified; + created = &pvd->hs.created; + modified_offset = 0; + created_offset = 0; + } else { + modified = &pvd->iso.modified.common; + created = &pvd->iso.created.common; + modified_offset = pvd->iso.modified.offset; + created_offset = pvd->iso.created.offset; + } /* create an UUID using the modified/created date */ - if (! probe_iso9660_set_uuid(pr, &pvd->modified)) - probe_iso9660_set_uuid(pr, &pvd->created); + if (! probe_iso9660_set_uuid(pr, modified, modified_offset)) + probe_iso9660_set_uuid(pr, created, created_offset); if (boot) blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID", @@ -315,6 +360,8 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) if (joliet) blkid_probe_set_version(pr, "Joliet Extension"); + else if (is_hs) + blkid_probe_set_version(pr, "High Sierra"); /* Label in Joliet is UNICODE (UTF16BE) but can contain only 16 characters. Label in PVD is * subset of ASCII but can contain up to the 32 characters. Non-representable characters are @@ -340,6 +387,10 @@ const struct blkid_idinfo iso9660_idinfo = .flags = BLKID_IDINFO_TOLERANT, .magics = { + /* + * Due to different location of vd_id[] in ISO9660 and High Sierra, IS9660 can match + * also High Sierra vd_id[]. So always check ISO9660 (CD001) before High Sierra (CDROM). + */ { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" }, { NULL } diff --git a/libblkid/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c index 81d53a1..aa03bcd 100644 --- a/libblkid/src/superblocks/isw_raid.c +++ b/libblkid/src/superblocks/isw_raid.c @@ -32,8 +32,6 @@ static int probe_iswraid(blkid_probe pr, unsigned int sector_size; struct isw_metadata *isw; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -60,6 +58,7 @@ static int probe_iswraid(blkid_probe pr, const struct blkid_idinfo iswraid_idinfo = { .name = "isw_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_iswraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/jfs.c b/libblkid/src/superblocks/jfs.c index 7b75ecb..c277375 100644 --- a/libblkid/src/superblocks/jfs.c +++ b/libblkid/src/superblocks/jfs.c @@ -36,7 +36,7 @@ struct jfs_super_block { static int probe_jfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct jfs_super_block *js; + const struct jfs_super_block *js; js = blkid_probe_get_sb(pr, mag, struct jfs_super_block); if (!js) diff --git a/libblkid/src/superblocks/jmicron_raid.c b/libblkid/src/superblocks/jmicron_raid.c index 65e05b6..4587075 100644 --- a/libblkid/src/superblocks/jmicron_raid.c +++ b/libblkid/src/superblocks/jmicron_raid.c @@ -75,8 +75,6 @@ static int probe_jmraid(blkid_probe pr, uint64_t off; const struct jm_metadata *jm; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -109,6 +107,7 @@ static int probe_jmraid(blkid_probe pr, const struct blkid_idinfo jmraid_idinfo = { .name = "jmicron_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_jmraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/linux_raid.c b/libblkid/src/superblocks/linux_raid.c index 360cd4e..156b0bf 100644 --- a/libblkid/src/superblocks/linux_raid.c +++ b/libblkid/src/superblocks/linux_raid.c @@ -187,15 +187,14 @@ static int raid1_verify_csum(blkid_probe pr, off_t off, { size_t csummed_size = sizeof(struct mdp1_super_block) + le32_to_cpu(mdp1->max_dev) * sizeof(mdp1->dev_roles[0]); - unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size); + const unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size); if (!csummed) return 1; - memset(csummed + offsetof(struct mdp1_super_block, sb_csum), 0, - sizeof(mdp1->sb_csum)); - uint64_t csum = 0; + csum -= le32_to_cpu(*(uint32_t *) (csummed + offsetof(struct mdp1_super_block, sb_csum))); + while (csummed_size >= 4) { csum += le32_to_cpu(*(uint32_t *) csummed); csummed_size -= 4; diff --git a/libblkid/src/superblocks/lsi_raid.c b/libblkid/src/superblocks/lsi_raid.c index 697b0fe..2ceed9b 100644 --- a/libblkid/src/superblocks/lsi_raid.c +++ b/libblkid/src/superblocks/lsi_raid.c @@ -29,8 +29,6 @@ static int probe_lsiraid(blkid_probe pr, uint64_t off; struct lsi_metadata *lsi; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -53,6 +51,7 @@ static int probe_lsiraid(blkid_probe pr, const struct blkid_idinfo lsiraid_idinfo = { .name = "lsi_mega_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_lsiraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/luks.c b/libblkid/src/superblocks/luks.c index 0230b34..4623c98 100644 --- a/libblkid/src/superblocks/luks.c +++ b/libblkid/src/superblocks/luks.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2008 Karel Zak <kzak@redhat.com> - * Copyright (C) 2018 Milan Broz <gmazyland@gmail.com> + * Copyright (C) 2018-2024 Milan Broz <gmazyland@gmail.com> * * Inspired by libvolume_id by * Kay Sievers <kay.sievers@vrfy.org> @@ -15,6 +15,7 @@ #include <errno.h> #include <ctype.h> #include <stdint.h> +#include <stdbool.h> #include "superblocks.h" @@ -96,6 +97,19 @@ static int luks_attributes(blkid_probe pr, struct luks2_phdr *header, uint64_t o return BLKID_PROBE_OK; } +static bool luks_valid(struct luks2_phdr *header, const char *magic, uint64_t offset) +{ + if (memcmp(header->magic, magic, LUKS_MAGIC_L)) + return false; + + /* LUKS2 header is not at expected offset */ + if (be16_to_cpu(header->version) == 2 && + be64_to_cpu(header->hdr_offset) != offset) + return false; + + return true; +} + static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) { struct luks2_phdr *header; @@ -105,7 +119,7 @@ static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute_ if (!header) return errno ? -errno : BLKID_PROBE_NONE; - if (!memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L)) { + if (luks_valid(header, LUKS_MAGIC, 0)) { /* LUKS primary header was found. */ return luks_attributes(pr, header, 0); } @@ -118,7 +132,7 @@ static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute_ if (!header) return errno ? -errno : BLKID_PROBE_NONE; - if (!memcmp(header->magic, LUKS_MAGIC_2, LUKS_MAGIC_L)) + if (luks_valid(header, LUKS_MAGIC_2, secondary_offsets[i])) return luks_attributes(pr, header, secondary_offsets[i]); } diff --git a/libblkid/src/superblocks/lvm.c b/libblkid/src/superblocks/lvm.c index eea74d6..0a3d505 100644 --- a/libblkid/src/superblocks/lvm.c +++ b/libblkid/src/superblocks/lvm.c @@ -76,11 +76,11 @@ static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag) int sector = mag->kboff << 1; struct lvm2_pv_label_header *label; char uuid[LVM2_ID_LEN + 7]; - unsigned char *buf; + const unsigned char *buf; buf = blkid_probe_get_buffer(pr, mag->kboff << 10, - 512 + sizeof(struct lvm2_pv_label_header)); + 512 + LVM2_LABEL_SIZE); if (!buf) return errno ? -errno : 1; @@ -122,7 +122,7 @@ static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag) static int probe_lvm1(blkid_probe pr, const struct blkid_idmag *mag) { - struct lvm1_pv_label_header *label; + const struct lvm1_pv_label_header *label; char uuid[LVM2_ID_LEN + 7]; unsigned int version; @@ -158,7 +158,7 @@ struct verity_sb { static int probe_verity(blkid_probe pr, const struct blkid_idmag *mag) { - struct verity_sb *sb; + const struct verity_sb *sb; unsigned int version; sb = blkid_probe_get_sb(pr, mag, struct verity_sb); @@ -187,7 +187,7 @@ struct integrity_sb { static int probe_integrity(blkid_probe pr, const struct blkid_idmag *mag) { - struct integrity_sb *sb; + const struct integrity_sb *sb; sb = blkid_probe_get_sb(pr, mag, struct integrity_sb); if (sb == NULL) diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c index c68ade9..dba299d 100644 --- a/libblkid/src/superblocks/minix.c +++ b/libblkid/src/superblocks/minix.c @@ -74,7 +74,7 @@ static int get_minix_version(const unsigned char *data, int *other_endian) static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) { - unsigned char *ext; + const unsigned char *ext; const unsigned char *data; int version = 0, swabme = 0; unsigned long zones, ninodes, imaps, zmaps; diff --git a/libblkid/src/superblocks/mpool.c b/libblkid/src/superblocks/mpool.c index b27569e..8a2e29c 100644 --- a/libblkid/src/superblocks/mpool.c +++ b/libblkid/src/superblocks/mpool.c @@ -26,7 +26,7 @@ struct omf_sb_descriptor { static int probe_mpool(blkid_probe pr, const struct blkid_idmag *mag) { - struct omf_sb_descriptor *osd; + const struct omf_sb_descriptor *osd; uint32_t sb_crc; osd = blkid_probe_get_sb(pr, mag, struct omf_sb_descriptor); diff --git a/libblkid/src/superblocks/netware.c b/libblkid/src/superblocks/netware.c index af81cf5..a8c20cb 100644 --- a/libblkid/src/superblocks/netware.c +++ b/libblkid/src/superblocks/netware.c @@ -67,7 +67,7 @@ struct netware_super_block { static int probe_netware(blkid_probe pr, const struct blkid_idmag *mag) { - struct netware_super_block *nw; + const struct netware_super_block *nw; nw = blkid_probe_get_sb(pr, mag, struct netware_super_block); if (!nw) diff --git a/libblkid/src/superblocks/ntfs.c b/libblkid/src/superblocks/ntfs.c index ab8c921..8ce557a 100644 --- a/libblkid/src/superblocks/ntfs.c +++ b/libblkid/src/superblocks/ntfs.c @@ -80,13 +80,13 @@ struct file_attribute { static int __probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag, int save_info) { - struct ntfs_super_block *ns; - struct master_file_table_record *mft; + const struct ntfs_super_block *ns; + const struct master_file_table_record *mft; uint32_t sectors_per_cluster, mft_record_size; uint16_t sector_size; uint64_t nr_clusters, off, attr_off; - unsigned char *buf_mft; + const unsigned char *buf_mft; ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); if (!ns) diff --git a/libblkid/src/superblocks/nvidia_raid.c b/libblkid/src/superblocks/nvidia_raid.c index d930f72..1a88dbd 100644 --- a/libblkid/src/superblocks/nvidia_raid.c +++ b/libblkid/src/superblocks/nvidia_raid.c @@ -41,8 +41,6 @@ static int probe_nvraid(blkid_probe pr, uint64_t off; struct nv_metadata *nv; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -71,6 +69,7 @@ static int probe_nvraid(blkid_probe pr, const struct blkid_idinfo nvraid_idinfo = { .name = "nvidia_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_nvraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/ocfs.c b/libblkid/src/superblocks/ocfs.c index e213d66..f2b2ca2 100644 --- a/libblkid/src/superblocks/ocfs.c +++ b/libblkid/src/superblocks/ocfs.c @@ -100,7 +100,7 @@ struct oracle_asm_disk_label { static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag) { - unsigned char *buf; + const unsigned char *buf; struct ocfs_volume_header ovh; struct ocfs_volume_label ovl; uint32_t maj, min; @@ -142,7 +142,7 @@ static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag) static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag) { - struct ocfs2_super_block *osb; + const struct ocfs2_super_block *osb; osb = blkid_probe_get_sb(pr, mag, struct ocfs2_super_block); if (!osb) @@ -165,7 +165,7 @@ static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag) static int probe_oracleasm(blkid_probe pr, const struct blkid_idmag *mag) { - struct oracle_asm_disk_label *dl; + const struct oracle_asm_disk_label *dl; dl = blkid_probe_get_sb(pr, mag, struct oracle_asm_disk_label); if (!dl) diff --git a/libblkid/src/superblocks/promise_raid.c b/libblkid/src/superblocks/promise_raid.c index 75f3a20..f891b37 100644 --- a/libblkid/src/superblocks/promise_raid.c +++ b/libblkid/src/superblocks/promise_raid.c @@ -33,8 +33,6 @@ static int probe_pdcraid(blkid_probe pr, }; uint64_t nsectors; - if (pr->size < 0x40000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -70,6 +68,7 @@ static int probe_pdcraid(blkid_probe pr, const struct blkid_idinfo pdcraid_idinfo = { .name = "promise_fasttrack_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x40000, .probefunc = probe_pdcraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/reiserfs.c b/libblkid/src/superblocks/reiserfs.c index 23d10e2..dac5f0c 100644 --- a/libblkid/src/superblocks/reiserfs.c +++ b/libblkid/src/superblocks/reiserfs.c @@ -41,7 +41,7 @@ struct reiser4_super_block { static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag) { - struct reiserfs_super_block *rs; + const struct reiserfs_super_block *rs; unsigned int blocksize; rs = blkid_probe_get_sb(pr, mag, struct reiserfs_super_block); @@ -82,7 +82,7 @@ static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag) static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag) { - struct reiser4_super_block *rs4; + const struct reiser4_super_block *rs4; unsigned int blocksize; rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block); diff --git a/libblkid/src/superblocks/romfs.c b/libblkid/src/superblocks/romfs.c index 456cbfb..86c09ad 100644 --- a/libblkid/src/superblocks/romfs.c +++ b/libblkid/src/superblocks/romfs.c @@ -29,7 +29,7 @@ static int romfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag, { uint32_t csummed_size = min((uint32_t) 512, be32_to_cpu(ros->ros_full_size)); - unsigned char *csummed; + const unsigned char *csummed; uint32_t csum; if (csummed_size % sizeof(uint32_t) != 0) @@ -50,7 +50,7 @@ static int romfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag, static int probe_romfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct romfs_super_block *ros; + const struct romfs_super_block *ros; ros = blkid_probe_get_sb(pr, mag, struct romfs_super_block); if (!ros) diff --git a/libblkid/src/superblocks/silicon_raid.c b/libblkid/src/superblocks/silicon_raid.c index 399eea8..0fc223d 100644 --- a/libblkid/src/superblocks/silicon_raid.c +++ b/libblkid/src/superblocks/silicon_raid.c @@ -91,8 +91,6 @@ static int probe_silraid(blkid_probe pr, uint64_t off; struct silicon_metadata *sil; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -127,6 +125,7 @@ static int probe_silraid(blkid_probe pr, const struct blkid_idinfo silraid_idinfo = { .name = "silicon_medley_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_silraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/squashfs.c b/libblkid/src/superblocks/squashfs.c index 92ab77a..8246583 100644 --- a/libblkid/src/superblocks/squashfs.c +++ b/libblkid/src/superblocks/squashfs.c @@ -40,7 +40,7 @@ struct sqsh_super_block { static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct sqsh_super_block *sq; + const struct sqsh_super_block *sq; uint16_t vermaj; uint16_t vermin; @@ -63,10 +63,10 @@ static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag) static int probe_squashfs3(blkid_probe pr, const struct blkid_idmag *mag) { - struct sqsh_super_block *sq; + const struct sqsh_super_block *sq; uint16_t vermaj; uint16_t vermin; - enum BLKID_ENDIANNESS endianness; + enum blkid_endianness endianness; sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block); if (!sq) diff --git a/libblkid/src/superblocks/stratis.c b/libblkid/src/superblocks/stratis.c index fe4a452..2651107 100644 --- a/libblkid/src/superblocks/stratis.c +++ b/libblkid/src/superblocks/stratis.c @@ -22,7 +22,7 @@ /* Macro to avoid error in struct declaration. */ #define STRATIS_UUID_LEN 32 /* Contains 4 hyphens and trailing null byte. */ -const int STRATIS_UUID_STR_LEN = 37; +#define STRATIS_UUID_STR_LEN 37 struct stratis_sb { uint32_t crc32; @@ -49,7 +49,7 @@ const char STRATIS_MAGIC[] = "!Stra0tis\x86\xff\x02^\x41rh"; #define MAGIC_OFFSET_COPY_1 (FIRST_COPY_OFFSET + _MAGIC_OFFSET) #define MAGIC_OFFSET_COPY_2 (SECOND_COPY_OFFSET + _MAGIC_OFFSET) -static int stratis_valid_sb(uint8_t *p) +static int stratis_valid_sb(const uint8_t *p) { const struct stratis_sb *stratis = (const struct stratis_sb *)p; uint32_t crc = 0; @@ -83,7 +83,7 @@ static int probe_stratis(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) { const struct stratis_sb *stratis = NULL; - uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE); + const uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE); unsigned char uuid[STRATIS_UUID_STR_LEN]; if (!buf) diff --git a/libblkid/src/superblocks/superblocks.c b/libblkid/src/superblocks/superblocks.c index 96bc0ef..dd1e6dc 100644 --- a/libblkid/src/superblocks/superblocks.c +++ b/libblkid/src/superblocks/superblocks.c @@ -94,11 +94,6 @@ static int blkid_probe_set_usage(blkid_probe pr, int usage); */ static const struct blkid_idinfo *idinfos[] = { - /* In case the volume is locked with OPAL we are going to get - * an I/O error when reading past the LUKS header, so try it - * first. */ - &luks_idinfo, - /* RAIDs */ &linuxraid_idinfo, &ddfraid_idinfo, @@ -124,6 +119,7 @@ static const struct blkid_idinfo *idinfos[] = &snapcow_idinfo, &verity_hash_idinfo, &integrity_idinfo, + &luks_idinfo, &vmfs_volume_idinfo, &ubi_idinfo, &vdo_idinfo, @@ -423,6 +419,7 @@ static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn) DBG(LOWPROBE, ul_debug("\tcall probefunc()")); errno = 0; rc = id->probefunc(pr, mag); + blkid_probe_prune_buffers(pr); if (rc != BLKID_PROBE_OK) { blkid_probe_chain_reset_values(pr, chn); if (rc < 0) @@ -629,7 +626,7 @@ int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size) block_size); } -int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness) +int blkid_probe_set_fsendianness(blkid_probe pr, enum blkid_endianness endianness) { struct blkid_chain *chn = blkid_probe_get_chain(pr); const char *value; diff --git a/libblkid/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h index a990058..dc669e0 100644 --- a/libblkid/src/superblocks/superblocks.h +++ b/libblkid/src/superblocks/superblocks.h @@ -9,7 +9,7 @@ #include "blkidP.h" -enum BLKID_ENDIANNESS { +enum blkid_endianness { BLKID_ENDIANNESS_LITTLE, BLKID_ENDIANNESS_BIG, }; @@ -128,9 +128,21 @@ int blkid_probe_set_block_size(blkid_probe pr, unsigned block_size); int blkid_probe_set_fssize(blkid_probe pr, uint64_t size); int blkid_probe_set_fslastblock(blkid_probe pr, uint64_t lastblock); int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size); -int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness); +int blkid_probe_set_fsendianness(blkid_probe pr, enum blkid_endianness endianness); extern int blkid_probe_is_bitlocker(blkid_probe pr); extern int blkid_probe_is_ntfs(blkid_probe pr); +/* + * utility functions + */ +static inline int blkid32_to_cpu(enum blkid_endianness e, uint32_t i) +{ + if (e == BLKID_ENDIANNESS_LITTLE) + return le32_to_cpu(i); + else if (e == BLKID_ENDIANNESS_BIG) + return be32_to_cpu(i); + abort(); +} + #endif /* _BLKID_SUPERBLOCKS_H */ diff --git a/libblkid/src/superblocks/swap.c b/libblkid/src/superblocks/swap.c index 67224bc..4876905 100644 --- a/libblkid/src/superblocks/swap.c +++ b/libblkid/src/superblocks/swap.c @@ -40,7 +40,7 @@ static void swap_set_info_swap1(blkid_probe pr, const struct blkid_idmag *mag, const struct swap_header_v1_2 *hdr) { - enum BLKID_ENDIANNESS endianness = le32_to_cpu(hdr->version) == 1 ? + enum blkid_endianness endianness = le32_to_cpu(hdr->version) == 1 ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG; blkid_probe_set_fsendianness(pr, endianness); @@ -91,7 +91,7 @@ static int swap_set_info(blkid_probe pr, const struct blkid_idmag *mag, static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag) { - unsigned char *buf; + const unsigned char *buf; if (!mag) return 1; diff --git a/libblkid/src/superblocks/sysv.c b/libblkid/src/superblocks/sysv.c index 421660e..a9085c4 100644 --- a/libblkid/src/superblocks/sysv.c +++ b/libblkid/src/superblocks/sysv.c @@ -76,7 +76,7 @@ struct sysv_super_block static int probe_xenix(blkid_probe pr, const struct blkid_idmag *mag) { - struct xenix_super_block *sb; + const struct xenix_super_block *sb; sb = blkid_probe_get_sb(pr, mag, struct xenix_super_block); if (!sb) diff --git a/libblkid/src/superblocks/ubi.c b/libblkid/src/superblocks/ubi.c index fbb7c5e..b15199b 100644 --- a/libblkid/src/superblocks/ubi.c +++ b/libblkid/src/superblocks/ubi.c @@ -35,7 +35,7 @@ static int ubi_verify_csum(blkid_probe pr, const struct ubi_ec_hdr *hdr) static int probe_ubi(blkid_probe pr, const struct blkid_idmag *mag) { - struct ubi_ec_hdr *hdr; + const struct ubi_ec_hdr *hdr; hdr = blkid_probe_get_sb(pr, mag, struct ubi_ec_hdr); if (!hdr) diff --git a/libblkid/src/superblocks/ubifs.c b/libblkid/src/superblocks/ubifs.c index 8dece28..c9733a9 100644 --- a/libblkid/src/superblocks/ubifs.c +++ b/libblkid/src/superblocks/ubifs.c @@ -105,7 +105,7 @@ static int ubifs_verify_csum(blkid_probe pr, const struct ubifs_sb_node *sb) static int probe_ubifs(blkid_probe pr, const struct blkid_idmag *mag) { - struct ubifs_sb_node *sb; + const struct ubifs_sb_node *sb; sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node); if (!sb) diff --git a/libblkid/src/superblocks/udf.c b/libblkid/src/superblocks/udf.c index 36adf60..76d4236 100644 --- a/libblkid/src/superblocks/udf.c +++ b/libblkid/src/superblocks/udf.c @@ -233,6 +233,7 @@ static int probe_udf(blkid_probe pr, size_t i; uint32_t vsd_len; uint16_t udf_rev = 0; + int is_udf = 0; int vsd_2048_valid = -1; int have_label = 0; int have_uuid = 0; @@ -343,6 +344,8 @@ anchor: return 1; real_blksz: + /* At this stage we detected ISO/IEC 13346 or ECMA-167 filesystem recognition sequence, it does not have to be UDF */ + /* Use the actual block size from here on out */ bs = pbs[i]; @@ -454,11 +457,17 @@ real_blksz: lvid_loc = le32_to_cpu(vd->type.logical.lvid_location); } } - if (!udf_rev) { - /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document - * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */ - if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0) - udf_rev = le16_to_cpu(vd->type.logical.udf_rev); + if (!is_udf || !udf_rev) { + /* UDF-2.60: 2.2.4.3: This field shall indicate that the contents of + * this logical volume conforms to the domain defined in this document. + * This distinguish UDF from all other ISO/IEC 13346 and ECMA-167 filesystems. */ + if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0) { + is_udf = 1; + /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document + * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */ + if (!udf_rev) + udf_rev = le16_to_cpu(vd->type.logical.udf_rev); + } } if ((!have_logvolid || !have_label) && is_charset_udf(vd->type.logical.desc_charset)) { /* LogicalVolumeIdentifier in UDF 2.01 specification: @@ -520,10 +529,15 @@ real_blksz: vd->type.imp_use_volume.lvinfo1.c, clen, enc); } } - if (have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid) + if (is_udf && have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid) break; } + if (!is_udf) { + /* We detected some other ISO/IEC 13346 or ECMA-167 filesystem, not UDF */ + return 1; + } + /* Pick the first logical volume integrity descriptor and read UDF revision */ if (lvid_loc && lvid_len >= sizeof(*vd)) { vd = (struct volume_descriptor *) @@ -584,6 +598,7 @@ const struct blkid_idinfo udf_idinfo = .flags = BLKID_IDINFO_TOLERANT, .magics = { + /* These magics are generic to all ISO/IEC 13346 and ECMA-167 filesystems, not just UDF */ { .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, { .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" }, diff --git a/libblkid/src/superblocks/vdo.c b/libblkid/src/superblocks/vdo.c index bec686f..4e9f445 100644 --- a/libblkid/src/superblocks/vdo.c +++ b/libblkid/src/superblocks/vdo.c @@ -25,7 +25,7 @@ struct vdo_super_block { static int probe_vdo(blkid_probe pr, const struct blkid_idmag *mag) { - struct vdo_super_block *vsb; + const struct vdo_super_block *vsb; vsb = blkid_probe_get_sb(pr, mag, struct vdo_super_block); if (!vsb) diff --git a/libblkid/src/superblocks/vfat.c b/libblkid/src/superblocks/vfat.c index 6b4a3ad..5644d89 100644 --- a/libblkid/src/superblocks/vfat.c +++ b/libblkid/src/superblocks/vfat.c @@ -183,8 +183,8 @@ static int search_fat_label(blkid_probe pr, uint64_t offset, uint32_t entries, u static int fat_valid_superblock(blkid_probe pr, const struct blkid_idmag *mag, - struct msdos_super_block *ms, - struct vfat_super_block *vs, + const struct msdos_super_block *ms, + const struct vfat_super_block *vs, uint32_t *cluster_count, uint32_t *fat_size, uint32_t *sect_count) { @@ -276,8 +276,8 @@ extern int blkid_probe_is_vfat(blkid_probe pr); */ int blkid_probe_is_vfat(blkid_probe pr) { - struct vfat_super_block *vs; - struct msdos_super_block *ms; + const struct vfat_super_block *vs; + const struct msdos_super_block *ms; const struct blkid_idmag *mag = NULL; int rc; @@ -301,11 +301,12 @@ int blkid_probe_is_vfat(blkid_probe pr) * Sievers's volume_id library */ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag) { - struct vfat_super_block *vs; - struct msdos_super_block *ms; + const struct vfat_super_block *vs; + const struct msdos_super_block *ms; const unsigned char *vol_label = NULL; const unsigned char *boot_label = NULL; - unsigned char *vol_serno = NULL, vol_label_buf[11]; + const unsigned char *vol_serno = NULL; + unsigned char vol_label_buf[11]; uint16_t sector_size = 0, reserved; uint32_t cluster_count, fat_size, sect_count; const char *version = NULL; @@ -348,7 +349,7 @@ static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag) version = "FAT16"; } else if (vs->vs_fat32_length) { - unsigned char *buf; + const unsigned char *buf; uint16_t fsinfo_sect; int maxloop = 100; diff --git a/libblkid/src/superblocks/via_raid.c b/libblkid/src/superblocks/via_raid.c index ee3ab65..16e7fc2 100644 --- a/libblkid/src/superblocks/via_raid.c +++ b/libblkid/src/superblocks/via_raid.c @@ -51,8 +51,6 @@ static int probe_viaraid(blkid_probe pr, uint64_t off; struct via_metadata *v; - if (pr->size < 0x10000) - return 1; if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr)) return 1; @@ -84,6 +82,7 @@ static int probe_viaraid(blkid_probe pr, const struct blkid_idinfo viaraid_idinfo = { .name = "via_raid_member", .usage = BLKID_USAGE_RAID, + .minsz = 0x10000, .probefunc = probe_viaraid, .magics = BLKID_NONE_MAGIC }; diff --git a/libblkid/src/superblocks/vmfs.c b/libblkid/src/superblocks/vmfs.c index fac87ab..3017768 100644 --- a/libblkid/src/superblocks/vmfs.c +++ b/libblkid/src/superblocks/vmfs.c @@ -24,7 +24,7 @@ struct vmfs_volume_info { static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag) { - struct vmfs_fs_info *header; + const struct vmfs_fs_info *header; header = blkid_probe_get_sb(pr, mag, struct vmfs_fs_info); if (header == NULL) @@ -48,8 +48,8 @@ static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag) static int probe_vmfs_volume(blkid_probe pr, const struct blkid_idmag *mag) { - struct vmfs_volume_info *header; - unsigned char *lvm_uuid; + const struct vmfs_volume_info *header; + const unsigned char *lvm_uuid; header = blkid_probe_get_sb(pr, mag, struct vmfs_volume_info); if (header == NULL) diff --git a/libblkid/src/superblocks/vxfs.c b/libblkid/src/superblocks/vxfs.c index be66831..c933861 100644 --- a/libblkid/src/superblocks/vxfs.c +++ b/libblkid/src/superblocks/vxfs.c @@ -25,23 +25,19 @@ struct vxfs_super_block { static int probe_vxfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct vxfs_super_block *vxs; + const struct vxfs_super_block *vxs; + enum blkid_endianness e = mag->hint; vxs = blkid_probe_get_sb(pr, mag, struct vxfs_super_block); if (!vxs) return errno ? -errno : 1; - if (le32_to_cpu(vxs->vs_magic) == 0xa501fcf5) { - blkid_probe_sprintf_version(pr, "%u", (unsigned int)le32_to_cpu(vxs->vs_version)); - blkid_probe_set_fsblocksize(pr, le32_to_cpu(vxs->vs_bsize)); - blkid_probe_set_block_size(pr, le32_to_cpu(vxs->vs_bsize)); - blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_LITTLE); - } else if (be32_to_cpu(vxs->vs_magic) == 0xa501fcf5) { - blkid_probe_sprintf_version(pr, "%u", (unsigned int)be32_to_cpu(vxs->vs_version)); - blkid_probe_set_fsblocksize(pr, be32_to_cpu(vxs->vs_bsize)); - blkid_probe_set_block_size(pr, be32_to_cpu(vxs->vs_bsize)); - blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_BIG); - } + blkid_probe_sprintf_version(pr, "%d", + (unsigned int)blkid32_to_cpu(e, vxs->vs_version)); + blkid_probe_set_fsblocksize(pr, blkid32_to_cpu(e, vxs->vs_bsize)); + blkid_probe_set_block_size(pr, blkid32_to_cpu(e, vxs->vs_bsize)); + blkid_probe_set_fsendianness(pr, e); + return 0; } @@ -53,8 +49,10 @@ const struct blkid_idinfo vxfs_idinfo = .probefunc = probe_vxfs, .magics = { - { .magic = "\365\374\001\245", .len = 4, .kboff = 1 }, - { .magic = "\245\001\374\365", .len = 4, .kboff = 8 }, + { .magic = "\xf5\xfc\x01\xa5", .len = 4, .kboff = 1, + .hint = BLKID_ENDIANNESS_LITTLE }, + { .magic = "\xa5\x01\xfc\xf5", .len = 4, .kboff = 8, + .hint = BLKID_ENDIANNESS_BIG }, { NULL } } }; diff --git a/libblkid/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c index f0e099e..0c35982 100644 --- a/libblkid/src/superblocks/xfs.c +++ b/libblkid/src/superblocks/xfs.c @@ -111,7 +111,7 @@ struct xfs_super_block { #define XFS_SB_VERSION2_CRCBIT 0x00000100 -static void sb_from_disk(struct xfs_super_block *from, +static void sb_from_disk(const struct xfs_super_block *from, struct xfs_super_block *to) { @@ -170,7 +170,7 @@ static void sb_from_disk(struct xfs_super_block *from, to->sb_rrmapino = be64_to_cpu(from->sb_rrmapino); } -static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr, +static int xfs_verify_sb(const struct xfs_super_block *ondisk, blkid_probe pr, const struct blkid_idmag *mag) { struct xfs_super_block sb, *sbp = &sb; @@ -206,7 +206,7 @@ static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr, if ((sbp->sb_versionnum & 0x0f) == 5) { uint32_t expected, crc; - unsigned char *csummed; + const unsigned char *csummed; if (!(sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT)) return 0; @@ -230,7 +230,7 @@ static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr, return 1; } -static uint64_t xfs_fssize(struct xfs_super_block *xs) +static uint64_t xfs_fssize(const struct xfs_super_block *xs) { uint32_t lsize = xs->sb_logstart ? xs->sb_logblocks : 0; uint64_t avail_blocks = be64_to_cpu(xs->sb_dblocks) - be32_to_cpu(lsize); @@ -241,7 +241,7 @@ static uint64_t xfs_fssize(struct xfs_super_block *xs) static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag) { - struct xfs_super_block *xs; + const struct xfs_super_block *xs; xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block); if (!xs) @@ -331,7 +331,7 @@ static int probe_xfs_log(blkid_probe pr, { int i; struct xlog_rec_header *rhead; - unsigned char *buf; + const unsigned char *buf; buf = blkid_probe_get_buffer(pr, 0, 256*1024); if (!buf) diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c index 1fcc384..008a200 100644 --- a/libblkid/src/superblocks/zfs.c +++ b/libblkid/src/superblocks/zfs.c @@ -71,13 +71,13 @@ struct nvlist { struct nvpair nvl_nvpair; }; -static void zfs_process_value(blkid_probe pr, char *name, size_t namelen, - void *value, size_t max_value_size, unsigned directory_level) +static void zfs_process_value(blkid_probe pr, const char *name, size_t namelen, + const void *value, size_t max_value_size, unsigned directory_level) { if (strncmp(name, "name", namelen) == 0 && sizeof(struct nvstring) <= max_value_size && !directory_level) { - struct nvstring *nvs = value; + const struct nvstring *nvs = value; uint32_t nvs_type = be32_to_cpu(nvs->nvs_type); uint32_t nvs_strlen = be32_to_cpu(nvs->nvs_strlen); @@ -92,7 +92,7 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen, } else if (strncmp(name, "guid", namelen) == 0 && sizeof(struct nvuint64) <= max_value_size && !directory_level) { - struct nvuint64 *nvu = value; + const struct nvuint64 *nvu = value; uint32_t nvu_type = be32_to_cpu(nvu->nvu_type); uint64_t nvu_value; @@ -110,7 +110,7 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen, } else if (strncmp(name, "pool_guid", namelen) == 0 && sizeof(struct nvuint64) <= max_value_size && !directory_level) { - struct nvuint64 *nvu = value; + const struct nvuint64 *nvu = value; uint32_t nvu_type = be32_to_cpu(nvu->nvu_type); uint64_t nvu_value; @@ -128,7 +128,7 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen, "%"PRIu64, nvu_value); } else if (strncmp(name, "ashift", namelen) == 0 && sizeof(struct nvuint64) <= max_value_size) { - struct nvuint64 *nvu = value; + const struct nvuint64 *nvu = value; uint32_t nvu_type = be32_to_cpu(nvu->nvu_type); uint64_t nvu_value; @@ -147,9 +147,9 @@ static void zfs_process_value(blkid_probe pr, char *name, size_t namelen, static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) { - unsigned char *p; - struct nvlist *nvl; - struct nvpair *nvp; + const unsigned char *p; + const struct nvlist *nvl; + const struct nvpair *nvp; size_t left = 4096; unsigned directory_level = 0; @@ -166,16 +166,16 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) DBG(LOWPROBE, ul_debug("zfs_extract: nvlist offset %jd", (intmax_t)offset)); - nvl = (struct nvlist *) p; + nvl = (const struct nvlist *) p; nvp = &nvl->nvl_nvpair; - left -= (unsigned char *)nvp - p; /* Already used up 12 bytes */ + left -= (const unsigned char *)nvp - p; /* Already used up 12 bytes */ while (left > sizeof(*nvp)) { uint32_t nvp_size = be32_to_cpu(nvp->nvp_size); uint32_t nvp_namelen = be32_to_cpu(nvp->nvp_namelen); uint64_t namesize = ((uint64_t)nvp_namelen + 3) & ~3; size_t max_value_size; - void *value; + const void *value; if (!nvp->nvp_size) { if (!directory_level) @@ -201,7 +201,7 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset) value = nvp->nvp_name + namesize; if (sizeof(struct nvdirectory) <= max_value_size) { - struct nvdirectory *nvu = value; + const struct nvdirectory *nvu = value; if (be32_to_cpu(nvu->nvd_type) == DATA_TYPE_DIRECTORY) { nvp_size = sizeof(*nvp) + namesize + sizeof(*nvu); directory_level++; @@ -259,7 +259,7 @@ static int probe_zfs(blkid_probe pr, struct zfs_uberblock *ub = NULL; loff_t offset = 0, ub_offset = 0; int label_no, found = 0, found_in_label; - void *label; + const void *label; loff_t blk_align = (pr->size % (256 * 1024ULL)); DBG(PROBE, ul_debug("probe_zfs")); diff --git a/libblkid/src/superblocks/zonefs.c b/libblkid/src/superblocks/zonefs.c index 8aa45b0..e8fcab3 100644 --- a/libblkid/src/superblocks/zonefs.c +++ b/libblkid/src/superblocks/zonefs.c @@ -54,7 +54,7 @@ static int zonefs_verify_csum(blkid_probe pr, const struct zonefs_super *sb) uint32_t expected = le32_to_cpu(sb->s_crc); uint32_t crc = ul_crc32_exclude_offset( ~0LL, (unsigned char *) sb, sizeof(*sb), - offsetof(typeof(*sb), s_crc), sizeof(sb->s_crc)); + offsetof(__typeof__(*sb), s_crc), sizeof(sb->s_crc)); return blkid_probe_verify_csum(pr, crc, expected); } diff --git a/libblkid/src/topology/sysfs.c b/libblkid/src/topology/sysfs.c index 25debd0..d216cfc 100644 --- a/libblkid/src/topology/sysfs.c +++ b/libblkid/src/topology/sysfs.c @@ -26,7 +26,7 @@ static const struct topology_val { /* /sys/dev/block/<maj>:<min>/<ATTR> */ - const char *attr; + const char * const attr; /* functions to set probing result */ int (*set_ulong)(blkid_probe, unsigned long); diff --git a/libblkid/src/topology/topology.c b/libblkid/src/topology/topology.c index 67df3e3..f19a4f2 100644 --- a/libblkid/src/topology/topology.c +++ b/libblkid/src/topology/topology.c @@ -146,6 +146,7 @@ blkid_topology blkid_probe_get_topology(blkid_probe pr) static int topology_probe(blkid_probe pr, struct blkid_chain *chn) { size_t i; + int rc; if (chn->idx < -1) return -1; @@ -182,9 +183,10 @@ static int topology_probe(blkid_probe pr, struct blkid_chain *chn) if (id->probefunc) { DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name)); - errno = 0; - if (id->probefunc(pr, NULL) != 0) + rc = id->probefunc(pr, NULL); + blkid_probe_prune_buffers(pr); + if (rc != 0) continue; } diff --git a/libfdisk/docs/Makefile.in b/libfdisk/docs/Makefile.in index 2df4df0..7f4497e 100644 --- a/libfdisk/docs/Makefile.in +++ b/libfdisk/docs/Makefile.in @@ -117,7 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_vscript.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/tls.m4 \ - $(top_srcdir)/m4/ul.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ul.m4 $(top_srcdir)/m4/year2038.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -159,10 +160,12 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BISON = @BISON@ BSD_WARN_CFLAGS = @BSD_WARN_CFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPTSETUP_CFLAGS = @CRYPTSETUP_CFLAGS@ @@ -192,6 +195,7 @@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ +FLEX = @FLEX@ FUZZING_ENGINE_LDFLAGS = @FUZZING_ENGINE_LDFLAGS@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ @@ -219,6 +223,8 @@ LIBFDISK_VERSION = @LIBFDISK_VERSION@ LIBFDISK_VERSION_INFO = @LIBFDISK_VERSION_INFO@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ +LIBLASTLOG2_VERSION = @LIBLASTLOG2_VERSION@ +LIBLASTLOG2_VERSION_INFO = @LIBLASTLOG2_VERSION_INFO@ LIBMOUNT_MAJOR_VERSION = @LIBMOUNT_MAJOR_VERSION@ LIBMOUNT_MINOR_VERSION = @LIBMOUNT_MINOR_VERSION@ LIBMOUNT_PATCH_VERSION = @LIBMOUNT_PATCH_VERSION@ @@ -244,6 +250,7 @@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MATH_LIBS = @MATH_LIBS@ MKDIR_P = @MKDIR_P@ +MQ_LIBS = @MQ_LIBS@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ @@ -257,7 +264,6 @@ NCURSES_CFLAGS = @NCURSES_CFLAGS@ NCURSES_LIBS = @NCURSES_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ -NO_UNUSED_WARN_CFLAGS = @NO_UNUSED_WARN_CFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -297,6 +303,8 @@ SHELL = @SHELL@ SOCKET_LIBS = @SOCKET_LIBS@ SOLIB_CFLAGS = @SOLIB_CFLAGS@ SOLIB_LDFLAGS = @SOLIB_LDFLAGS@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ STRIP = @STRIP@ SUID_CFLAGS = @SUID_CFLAGS@ SUID_LDFLAGS = @SUID_LDFLAGS@ @@ -382,6 +390,7 @@ sysconfdir = @sysconfdir@ sysconfstaticdir = @sysconfstaticdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ +tmpfilesdir = @tmpfilesdir@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @@ -391,6 +400,7 @@ usrsbin_execdir = @usrsbin_execdir@ vendordir = @vendordir@ with_bashcompletiondir = @with_bashcompletiondir@ with_systemdsystemunitdir = @with_systemdsystemunitdir@ +with_tmpfilesdir = @with_tmpfilesdir@ # We require automake 1.10 at least. AUTOMAKE_OPTIONS = 1.10 diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index 1589413..efc1385 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -182,6 +182,7 @@ fdisk_partition_get_size fdisk_partition_get_start fdisk_partition_get_type fdisk_partition_get_uuid +fdisk_partition_get_max_size fdisk_partition_has_end fdisk_partition_has_partno fdisk_partition_has_size diff --git a/libfdisk/docs/version.xml b/libfdisk/docs/version.xml index a69af57..4bdd32f 100644 --- a/libfdisk/docs/version.xml +++ b/libfdisk/docs/version.xml @@ -1 +1 @@ -2.39.3 +2.40 diff --git a/libfdisk/samples/Makemodule.am b/libfdisk/samples/Makemodule.am index b67b121..0c34436 100644 --- a/libfdisk/samples/Makemodule.am +++ b/libfdisk/samples/Makemodule.am @@ -3,8 +3,7 @@ check_PROGRAMS += \ sample-fdisk-mkpart \ sample-fdisk-mkpart-fullspec -sample_fdisk_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \ - -I$(ul_libfdisk_incdir) +sample_fdisk_cflags = $(AM_CFLAGS) -I$(ul_libfdisk_incdir) sample_fdisk_ldadd = $(LDADD) libfdisk.la sample_fdisk_mkpart_SOURCES = libfdisk/samples/mkpart.c diff --git a/libfdisk/samples/mkpart-fullspec.c b/libfdisk/samples/mkpart-fullspec.c index 821a688..0dca3f1 100644 --- a/libfdisk/samples/mkpart-fullspec.c +++ b/libfdisk/samples/mkpart-fullspec.c @@ -27,7 +27,7 @@ static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)), struct fdisk_ask *ask, - void *data) + void *data __attribute__((unused))) { switch(fdisk_ask_get_type(ask)) { case FDISK_ASKTYPE_INFO: diff --git a/libfdisk/samples/mkpart.c b/libfdisk/samples/mkpart.c index 1e5fd99..766e751 100644 --- a/libfdisk/samples/mkpart.c +++ b/libfdisk/samples/mkpart.c @@ -39,7 +39,7 @@ static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)), struct fdisk_ask *ask, - void *data) + void *data __attribute__((unused))) { switch(fdisk_ask_get_type(ask)) { case FDISK_ASKTYPE_INFO: diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am index 9bd64c1..073d571 100644 --- a/libfdisk/src/Makemodule.am +++ b/libfdisk/src/Makemodule.am @@ -67,7 +67,7 @@ check_PROGRAMS += \ test_fdisk_version \ test_fdisk_item -libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) +libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS) libfdisk_tests_ldflags = libuuid.la -static libfdisk_tests_ldadd = libfdisk.la $(LDADD) @@ -102,7 +102,7 @@ check_PROGRAMS += test_fdisk_script_fuzz nodist_EXTRA_test_fdisk_script_fuzz_SOURCES = dummy.cxx test_fdisk_script_fuzz_SOURCES = libfdisk/src/script.c -test_fdisk_script_fuzz_CFLAGS = -DFUZZ_TARGET $(libfdisk_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) +test_fdisk_script_fuzz_CFLAGS = -DFUZZ_TARGET $(libfdisk_la_CFLAGS) test_fdisk_script_fuzz_LDFLAGS = $(libfdisk_tests_ldflags) -lpthread test_fdisk_script_fuzz_LDADD = $(libfdisk_tests_ldadd) $(LIB_FUZZING_ENGINE) endif diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c index 274f6ba..299f65b 100644 --- a/libfdisk/src/ask.c +++ b/libfdisk/src/ask.c @@ -1035,7 +1035,9 @@ int fdisk_info_new_partition( } #ifdef TEST_PROGRAM -static int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) +static int test_ranges(struct fdisk_test *ts __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) { /* 1 - 3, 6, 8, 9, 11 13 */ size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 }; diff --git a/libfdisk/src/bsd.c b/libfdisk/src/bsd.c index 313ae5a..cb45e3f 100644 --- a/libfdisk/src/bsd.c +++ b/libfdisk/src/bsd.c @@ -30,7 +30,7 @@ * */ -static const char *bsd_dktypenames[] = { +static const char * const bsd_dktypenames[] = { "unknown", "SMD", "MSCP", @@ -46,7 +46,7 @@ static const char *bsd_dktypenames[] = { }; #define BSD_DKMAXTYPES (ARRAY_SIZE(bsd_dktypenames) - 1) -static struct fdisk_parttype bsd_fstypes[] = { +static const struct fdisk_parttype bsd_fstypes[] = { {BSD_FS_UNUSED, "unused"}, {BSD_FS_SWAP, "swap"}, {BSD_FS_V6, "Version 6"}, diff --git a/libfdisk/src/context.c b/libfdisk/src/context.c index 0d22124..463a60f 100644 --- a/libfdisk/src/context.c +++ b/libfdisk/src/context.c @@ -773,7 +773,7 @@ int fdisk_deassign_device(struct fdisk_context *cxt, int nosync) cxt->dev_path); return -errno; } - if (!nosync) { + if (S_ISBLK(cxt->dev_st.st_mode) && !nosync) { fdisk_info(cxt, _("Syncing disks.")); sync(); } diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c index 7970ae1..ecf0b36 100644 --- a/libfdisk/src/dos.c +++ b/libfdisk/src/dos.c @@ -67,7 +67,7 @@ struct fdisk_dos_label { /* * Partition types */ -static struct fdisk_parttype dos_parttypes[] = { +static const struct fdisk_parttype dos_parttypes[] = { #include "pt-mbr-partnames.h" }; @@ -792,7 +792,7 @@ static void get_partition_table_geometry(struct fdisk_context *cxt, unsigned char *bufp = cxt->firstsector; struct { unsigned int c, h, o, v; } t[8]; unsigned int n1, n2, n3, n4, n5, n6; - struct dos_partition *p; + const struct dos_partition *p; unsigned int c, h, s, l; unsigned int hh, ss; unsigned int sects; @@ -1727,14 +1727,22 @@ static int dos_verify_disklabel(struct fdisk_context *cxt) { size_t i, j; fdisk_sector_t total = 1, n_sectors = cxt->total_sectors; - fdisk_sector_t first[cxt->label->nparts_max], - last[cxt->label->nparts_max]; + fdisk_sector_t *first, *last; struct dos_partition *p; struct fdisk_dos_label *l = self_label(cxt); int nerrors = 0; assert(fdisk_is_label(cxt, DOS)); + first = calloc(cxt->label->nparts_max, sizeof(*first)); + last = calloc(cxt->label->nparts_max, sizeof(*first)); + + if (!first || !last) { + free(first); + free(last); + return -ENOMEM; + } + fill_bounds(cxt, first, last); for (i = 0; i < cxt->label->nparts_max; i++) { struct pte *pe = self_pte(cxt, i); @@ -1818,6 +1826,8 @@ static int dos_verify_disklabel(struct fdisk_context *cxt) P_("%d error detected.", "%d errors detected.", nerrors), nerrors); + free(first); + free(last); return nerrors; } diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index 7c0830e..49e057f 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -286,7 +286,7 @@ enum { struct fdisk_label { const char *name; /* label name */ enum fdisk_labeltype id; /* FDISK_DISKLABEL_* */ - struct fdisk_parttype *parttypes; /* supported partitions types */ + const struct fdisk_parttype *parttypes; /* supported partitions types */ size_t nparttypes; /* number of items in parttypes[] */ const struct fdisk_shortcut *parttype_cuts; /* partition type shortcuts */ diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c index c3c0347..1c19d86 100644 --- a/libfdisk/src/gpt.c +++ b/libfdisk/src/gpt.c @@ -152,19 +152,20 @@ struct gpt_legacy_mbr { .name = (_n), \ } -static struct fdisk_parttype gpt_parttypes[] = +static const struct fdisk_parttype gpt_parttypes[] = { #include "pt-gpt-partnames.h" }; static const struct fdisk_shortcut gpt_parttype_cuts[] = { - { .shortcut = "L", .alias = "linux", .data = "0FC63DAF-8483-4772-8E79-3D69D8477DE4" }, /* Linux */ - { .shortcut = "S", .alias = "swap", .data = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" }, /* Swap */ - { .shortcut = "H", .alias = "home", .data = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915" }, /* Home */ - { .shortcut = "U", .alias = "uefi", .data = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" }, /* UEFI system */ - { .shortcut = "R", .alias = "raid", .data = "A19D880F-05FC-4D3B-A006-743F0F84911E" }, /* Linux RAID */ - { .shortcut = "V", .alias = "lvm", .data = "E6D6D379-F507-44C2-A23C-238F2A3DF928" } /* LVM */ + { .shortcut = "L", .alias = "linux", .data = "0FC63DAF-8483-4772-8E79-3D69D8477DE4" }, /* Linux */ + { .shortcut = "S", .alias = "swap", .data = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" }, /* Swap */ + { .shortcut = "H", .alias = "home", .data = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915" }, /* Home */ + { .shortcut = "U", .alias = "uefi", .data = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" }, /* UEFI system */ + { .shortcut = "R", .alias = "raid", .data = "A19D880F-05FC-4D3B-A006-743F0F84911E" }, /* Linux RAID */ + { .shortcut = "V", .alias = "lvm", .data = "E6D6D379-F507-44C2-A23C-238F2A3DF928" }, /* LVM */ + { .shortcut = "X", .alias = "xbootldr", .data = "BC13C2FF-59E6-4262-A352-B275FD6F7172" }, /* Linux extended boot */ }; #define alignment_required(_x) ((_x)->grain != (_x)->sector_size) @@ -3322,8 +3323,12 @@ void fdisk_gpt_enable_minimize(struct fdisk_label *lb, int enable) } #ifdef TEST_PROGRAM -static int test_getattr(struct fdisk_test *ts, int argc, char *argv[]) +static int test_getattr(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 3) + return -1; + const char *disk = argv[1]; size_t part = strtoul(argv[2], NULL, 0) - 1; struct fdisk_context *cxt; @@ -3344,8 +3349,12 @@ static int test_getattr(struct fdisk_test *ts, int argc, char *argv[]) return 0; } -static int test_setattr(struct fdisk_test *ts, int argc, char *argv[]) +static int test_setattr(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 4) + return -1; + const char *disk = argv[1]; size_t part = strtoul(argv[2], NULL, 0) - 1; uint64_t atters = strtoull(argv[3], NULL, 0); diff --git a/libfdisk/src/item.c b/libfdisk/src/item.c index 671f9ad..f46e646 100644 --- a/libfdisk/src/item.c +++ b/libfdisk/src/item.c @@ -199,8 +199,12 @@ int fdisk_labelitem_is_number(struct fdisk_labelitem *li) } #ifdef TEST_PROGRAM -static int test_listitems(struct fdisk_test *ts, int argc, char *argv[]) +static int test_listitems(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + const char *disk = argv[1]; struct fdisk_context *cxt; struct fdisk_labelitem *item; diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index 1c97149..9c20f44 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -540,6 +540,9 @@ extern int fdisk_reorder_partitions(struct fdisk_context *cxt); extern int fdisk_partition_has_wipe(struct fdisk_context *cxt, struct fdisk_partition *pa); +extern int fdisk_partition_get_max_size(struct fdisk_context *cxt, size_t n, + fdisk_sector_t *maxsz); + /* table.c */ extern struct fdisk_table *fdisk_new_table(void); diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index 71de805..bb69e93 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -320,3 +320,7 @@ FDISK_2.36 { FDISK_2.38 { fdisk_dos_fix_chs; } FDISK_2.36; + +FDISK_2.40 { + fdisk_partition_get_max_size; +} FDISK_2.38; diff --git a/libfdisk/src/partition.c b/libfdisk/src/partition.c index bf3afad..1fd3176 100644 --- a/libfdisk/src/partition.c +++ b/libfdisk/src/partition.c @@ -1625,3 +1625,55 @@ int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n) return cxt->label->op->part_is_used(cxt, n); } + +/** + * fdisk_partition_max_size: + * @cxt: context + * @n: partition number (0 is the first partition) + * @maxsz: returns maximum size of partition + * + * Find maximum size the partition can be resized to. + * Takes into account free space between this partition and the next one. + * + * Returns: 0 on success, <0 on error. + */ +int fdisk_partition_get_max_size(struct fdisk_context *cxt, size_t n, + fdisk_sector_t *maxsz) +{ + struct fdisk_partition *cur = NULL; + struct fdisk_table *tb = NULL; + int rc; + + rc = fdisk_get_partitions(cxt, &tb); + if (rc) + goto out; + + rc = fdisk_get_freespaces(cxt, &tb); + if (rc) + goto out; + + rc = fdisk_table_sort_partitions(tb, fdisk_partition_cmp_start); + if (rc) + goto out; + + cur = fdisk_table_get_partition_by_partno(tb, n); + if (!cur) + goto einval; + + if (!fdisk_partition_has_start(cur)) + goto einval; + + if (resize_get_last_possible(tb, cur, + fdisk_partition_get_start(cur), maxsz)) + goto einval; + +out: + fdisk_unref_partition(cur); + fdisk_unref_table(tb); + + return rc; + +einval: + rc = -EINVAL; + goto out; +} diff --git a/libfdisk/src/parttype.c b/libfdisk/src/parttype.c index 271b671..8566932 100644 --- a/libfdisk/src/parttype.c +++ b/libfdisk/src/parttype.c @@ -145,7 +145,7 @@ struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, si { if (!lb || n >= lb->nparttypes) return NULL; - return &lb->parttypes[n]; + return (struct fdisk_parttype *)&lb->parttypes[n]; } /** @@ -237,7 +237,7 @@ struct fdisk_parttype *fdisk_label_get_parttype_from_code( for (i = 0; i < lb->nparttypes; i++) if (lb->parttypes[i].code == code) - return &lb->parttypes[i]; + return (struct fdisk_parttype *)&lb->parttypes[i]; return NULL; } @@ -265,7 +265,7 @@ struct fdisk_parttype *fdisk_label_get_parttype_from_string( for (i = 0; i < lb->nparttypes; i++) if (lb->parttypes[i].typestr && strcasecmp(lb->parttypes[i].typestr, str) == 0) - return &lb->parttypes[i]; + return (struct fdisk_parttype *)&lb->parttypes[i]; return NULL; } @@ -324,7 +324,8 @@ static struct fdisk_parttype *parttype_from_data( unsigned int *xcode, int use_seqnum) { - struct fdisk_parttype *types, *ret = NULL; + const struct fdisk_parttype *types; + struct fdisk_parttype *ret = NULL; char *end = NULL; assert(lb); @@ -369,7 +370,7 @@ static struct fdisk_parttype *parttype_from_data( if (use_seqnum && errno == 0 && *end == '\0' && i > 0 && i - 1 < (int) lb->nparttypes) - ret = &types[i - 1]; + ret = (struct fdisk_parttype *)&types[i - 1]; } } @@ -428,7 +429,7 @@ static struct fdisk_parttype *parttype_from_name( const char *name = lb->parttypes[i].name; if (name && *name && ul_stralnumcmp(name, str) == 0) - return &lb->parttypes[i]; + return (struct fdisk_parttype *)&lb->parttypes[i]; } return NULL; diff --git a/libfdisk/src/script.c b/libfdisk/src/script.c index f537a7d..e357fad 100644 --- a/libfdisk/src/script.c +++ b/libfdisk/src/script.c @@ -1108,7 +1108,7 @@ static int parse_size_value(struct fdisk_script *dp, struct fdisk_partition *pa, done: DBG(SCRIPT, ul_debugobj(dp, " size parse result: rc=%d, move=%s, size=%ju, default=%s", rc, pa->resize == FDISK_RESIZE_REDUCE ? "reduce" : - pa->resize == FDISK_RESIZE_ENLARGE ? "enlage" : "none", + pa->resize == FDISK_RESIZE_ENLARGE ? "enlarge" : "none", pa->size, pa->end_follow_default ? "on" : "off")); return rc; @@ -1669,8 +1669,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) #endif #ifdef TEST_PROGRAM -static int test_dump(struct fdisk_test *ts, int argc, char *argv[]) +static int test_dump(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *devname = argv[1]; struct fdisk_context *cxt; struct fdisk_script *dp; @@ -1688,8 +1692,12 @@ static int test_dump(struct fdisk_test *ts, int argc, char *argv[]) return 0; } -static int test_read(struct fdisk_test *ts, int argc, char *argv[]) +static int test_read(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *filename = argv[1]; struct fdisk_script *dp; struct fdisk_context *cxt; @@ -1711,8 +1719,12 @@ static int test_read(struct fdisk_test *ts, int argc, char *argv[]) return 0; } -static int test_stdin(struct fdisk_test *ts, int argc, char *argv[]) +static int test_stdin(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[] __attribute__((unused))) { + if (argc != 1) + return -1; + char buf[BUFSIZ] = { '\0' }; struct fdisk_script *dp; struct fdisk_context *cxt; @@ -1746,8 +1758,12 @@ static int test_stdin(struct fdisk_test *ts, int argc, char *argv[]) return rc; } -static int test_apply(struct fdisk_test *ts, int argc, char *argv[]) +static int test_apply(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 3) + return -1; + char *devname = argv[1], *scriptname = argv[2]; struct fdisk_context *cxt; struct fdisk_script *dp; @@ -1788,8 +1804,12 @@ done: return 0; } -static int test_tokens(struct fdisk_test *ts, int argc, char *argv[]) +static int test_tokens(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *p, *str = argc == 2 ? strdup(argv[1]) : NULL; int i; diff --git a/libfdisk/src/sgi.c b/libfdisk/src/sgi.c index 6740535..23aafd9 100644 --- a/libfdisk/src/sgi.c +++ b/libfdisk/src/sgi.c @@ -41,7 +41,7 @@ struct fdisk_sgi_label { } freelist[SGI_MAXPARTITIONS + 1]; }; -static struct fdisk_parttype sgi_parttypes[] = +static const struct fdisk_parttype sgi_parttypes[] = { {SGI_TYPE_VOLHDR, N_("SGI volhdr")}, {SGI_TYPE_TRKREPL, N_("SGI trkrepl")}, @@ -123,10 +123,12 @@ static struct sgi_info *sgi_new_info(void) info->b3 = cpu_to_be16(1); /* You may want to replace this string !!!!!!! */ - strcpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30"); - strcpy((char *) info->serial, "0000"); + strncpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30", + sizeof(info->scsi_string)); + strncpy((char *) info->serial, "0000", sizeof(info->serial)); info->check1816 = cpu_to_be16(18 * 256 + 16); - strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994"); + strncpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994", + sizeof(info->installer)); return info; } @@ -1009,7 +1011,8 @@ static int sgi_create_disklabel(struct fdisk_context *cxt) /* sizeof(sgilabel->boot_file) = 16 > 6 */ memset(sgilabel->boot_file, 0, 16); - strcpy((char *) sgilabel->boot_file, "/unix"); + strncpy((char *) sgilabel->boot_file, "/unix", + sizeof(sgilabel->boot_file)); sgilabel->devparam.skew = (0); sgilabel->devparam.gap1 = (0); diff --git a/libfdisk/src/sun.c b/libfdisk/src/sun.c index dde9750..cd965ab 100644 --- a/libfdisk/src/sun.c +++ b/libfdisk/src/sun.c @@ -35,7 +35,7 @@ struct fdisk_sun_label { struct sun_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */ }; -static struct fdisk_parttype sun_parttypes[] = { +static const struct fdisk_parttype sun_parttypes[] = { {SUN_TAG_UNASSIGNED, N_("Unassigned")}, {SUN_TAG_BOOT, N_("Boot")}, {SUN_TAG_ROOT, N_("SunOS root")}, @@ -383,6 +383,10 @@ static void fetch_sun(struct fdisk_context *cxt, lens[i] = 0; } } + for (i = cxt->label->nparts_max; i < SUN_MAXPARTITIONS; i++) { + starts[i] = 0; + lens[i] = 0; + } } /* non-Linux qsort_r(3) has usually differently ordered arguments */ diff --git a/libfdisk/src/utils.c b/libfdisk/src/utils.c index 6b6167d..a7c8bfa 100644 --- a/libfdisk/src/utils.c +++ b/libfdisk/src/utils.c @@ -183,11 +183,15 @@ done: } #ifdef TEST_PROGRAM -struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; } -struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; } +struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt __attribute__((unused))) { return NULL; } +struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt __attribute__((unused))) { return NULL; } -static int test_partnames(struct fdisk_test *ts, int argc, char *argv[]) +static int test_partnames(struct fdisk_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + size_t i; const char *disk = argv[1]; diff --git a/libfdisk/src/version.c b/libfdisk/src/version.c index 9d84b4c..b0661e2 100644 --- a/libfdisk/src/version.c +++ b/libfdisk/src/version.c @@ -91,7 +91,9 @@ int fdisk_get_library_features(const char ***features) } #ifdef TEST_PROGRAM -static int test_version(struct fdisk_test *ts, int argc, char *argv[]) +static int test_version(struct fdisk_test *ts __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) { const char *ver; const char **features; diff --git a/libfdisk/src/wipe.c b/libfdisk/src/wipe.c index 54f4213..bb5f1bb 100644 --- a/libfdisk/src/wipe.c +++ b/libfdisk/src/wipe.c @@ -75,6 +75,7 @@ int fdisk_set_wipe_area(struct fdisk_context *cxt, free(wp); return 1; } + DBG(WIPE, ul_debug("not requested")); return 0; } @@ -133,17 +134,8 @@ int fdisk_do_wipe(struct fdisk_context *cxt) return rc; } - blkid_probe_enable_superblocks(pr, 1); - blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC | - BLKID_SUBLKS_BADCSUM); - blkid_probe_enable_partitions(pr, 1); - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC | - BLKID_PARTS_FORCE_GPT); - - while (blkid_do_probe(pr) == 0) { - DBG(WIPE, ul_debugobj(wp, " wiping...")); - blkid_do_wipe(pr, FALSE); - } + DBG(WIPE, ul_debugobj(wp, " wiping...")); + blkid_wipe_all(pr); } blkid_free_probe(pr); diff --git a/liblastlog2/COPYING b/liblastlog2/COPYING new file mode 100644 index 0000000..df71a98 --- /dev/null +++ b/liblastlog2/COPYING @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2023, Thorsten Kukuk + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/liblastlog2/Makemodule.am b/liblastlog2/Makemodule.am new file mode 100644 index 0000000..2f69f88 --- /dev/null +++ b/liblastlog2/Makemodule.am @@ -0,0 +1,10 @@ +if BUILD_LIBLASTLOG2 + +include liblastlog2/man/Makemodule.am +include liblastlog2/src/Makemodule.am + +pkgconfig_DATA += liblastlog2/lastlog2.pc +PATHFILES += liblastlog2/lastlog2.pc +EXTRA_DIST += liblastlog2/COPYING + +endif # BUILD_LIBLASTLOG2 diff --git a/liblastlog2/lastlog2.pc.in b/liblastlog2/lastlog2.pc.in new file mode 100644 index 0000000..39df5da --- /dev/null +++ b/liblastlog2/lastlog2.pc.in @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@usrlib_execdir@ +includedir=@includedir@ + +Name: lastlog2 +Description: Y2038 safe version of lastlog +Version: @LIBLASTLOG2_VERSION@ +Requires.private: sqlite3 +Cflags: -I${includedir}/liblastlog2 +Libs: -L${libdir} -llastlog2 diff --git a/liblastlog2/man/Makemodule.am b/liblastlog2/man/Makemodule.am new file mode 100644 index 0000000..803bfb7 --- /dev/null +++ b/liblastlog2/man/Makemodule.am @@ -0,0 +1,20 @@ + +MANPAGES += \ + liblastlog2/man/lastlog2.3 \ + liblastlog2/man/ll2_import_lastlog.3 \ + liblastlog2/man/ll2_read_all.3 \ + liblastlog2/man/ll2_read_entry.3 \ + liblastlog2/man/ll2_remove_entry.3 \ + liblastlog2/man/ll2_rename_user.3 \ + liblastlog2/man/ll2_update_login_time.3 \ + liblastlog2/man/ll2_write_entry.3 + +dist_noinst_DATA += \ + liblastlog2/man/lastlog2.3.adoc \ + liblastlog2/man/ll2_import_lastlog.3.adoc \ + liblastlog2/man/ll2_read_all.3.adoc \ + liblastlog2/man/ll2_read_entry.3.adoc \ + liblastlog2/man/ll2_remove_entry.3.adoc \ + liblastlog2/man/ll2_rename_user.3.adoc \ + liblastlog2/man/ll2_update_login_time.3.adoc \ + liblastlog2/man/ll2_write_entry.3.adoc diff --git a/liblastlog2/man/lastlog2.3 b/liblastlog2/man/lastlog2.3 new file mode 100644 index 0000000..b2ac0d7 --- /dev/null +++ b/liblastlog2/man/lastlog2.3 @@ -0,0 +1,67 @@ +'\" t +.\" Title: lastlog2 +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LASTLOG2" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +lastlog2 \- Y2038 safe version of lastlog library. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.SH "DESCRIPTION" +.sp +\fBlastlog2\fP reports the last login of a given user or of all users who did ever login on a system. +.sp +It\(cqs using sqlite3 as database backend. Data is only collected via a PAM module, so that every +tools can make use of it, without modifying existing packages. +The output is as compatible as possible with the old lastlog implementation. +By default the database will be written as \f(CR/var/lib/lastlog/lastlog2.db\fP. +The size of the database depends on the amount of users, not how big the biggest UID is. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry\fP(3), +\fBll2_read_all\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/lastlog2.3.adoc b/liblastlog2/man/lastlog2.3.adoc new file mode 100644 index 0000000..05cab12 --- /dev/null +++ b/liblastlog2/man/lastlog2.3.adoc @@ -0,0 +1,51 @@ +//po4a: entry man manual += lastlog2(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +lastlog2 - Y2038 safe version of lastlog library. + +== SYNOPSIS + +*#include <lastlog2.h>* + +== DESCRIPTION + +*lastlog2* reports the last login of a given user or of all users who did ever login on a system. + +It's using sqlite3 as database backend. Data is only collected via a PAM module, so that every +tools can make use of it, without modifying existing packages. +The output is as compatible as possible with the old lastlog implementation. +By default the database will be written as `/var/lib/lastlog/lastlog2.db`. +The size of the database depends on the amount of users, not how big the biggest UID is. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry*(3), +*ll2_read_all*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_import_lastlog.3 b/liblastlog2/man/ll2_import_lastlog.3 new file mode 100644 index 0000000..23231c4 --- /dev/null +++ b/liblastlog2/man/ll2_import_lastlog.3 @@ -0,0 +1,84 @@ +'\" t +.\" Title: ll2_import_lastlog +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_IMPORT_LASTLOG" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_import_lastlog \- Import old lastlog file. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_import_lastlog (struct ll2_context *\fIcontext\fP, +const char *\fIlastlog_file\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Importing all entries from \fIlastlog_file\fP file (lastlog(8)) into +lastlog2 database defined with \fIcontext\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *lastlog_path = "/var/log/lastlog"; + +int ret = ll2_import_lastlog (NULL, lastlog_path, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_remove_entry\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_import_lastlog.3.adoc b/liblastlog2/man/ll2_import_lastlog.3.adoc new file mode 100644 index 0000000..9ddf54b --- /dev/null +++ b/liblastlog2/man/ll2_import_lastlog.3.adoc @@ -0,0 +1,65 @@ +//po4a: entry man manual += ll2_import_lastlog(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_import_lastlog - Import old lastlog file. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_import_lastlog (struct ll2_context *__context__, + const char *__lastlog_file__, + char **__error__);* + +== DESCRIPTION + +Importing all entries from _lastlog_file_ file (lastlog(8)) into +lastlog2 database defined with _context_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *lastlog_path = "/var/log/lastlog"; + +int ret = ll2_import_lastlog (NULL, lastlog_path, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_rename_user*(3), +*ll2_remove_entry*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_read_all.3 b/liblastlog2/man/ll2_read_all.3 new file mode 100644 index 0000000..753749f --- /dev/null +++ b/liblastlog2/man/ll2_read_all.3 @@ -0,0 +1,93 @@ +'\" t +.\" Title: ll2_read_all +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_READ_ALL" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_read_all \- Reads all entries from database and calls the callback function for each entry. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +\fBint ll2_read_all (struct ll2_context *\fIcontext\fP, +int (*\fIcallback\fP)(const char *\fIuser\fP, int64_t \fIll_time\fP, +const char *\fItty\fP, const char *\fIrhost\fP, +const char *\fIpam_service\fP, const char *\fIcb_error\fP), +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Reads all entries from database, defined in \fIcontext\fP, and calls callback fuction \fIcallback\fP for each entry. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; + +static int +callback (const char *res_user, int64_t ll_time, const char *res_tty, + const char *res_rhost, const char *res_service, const char *cb_error) +{ + /* returning != 0 if no further entry has to be handled by the callback */ + return 0; +} + +int ret = ll2_read_all (NULL, callback, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_read_all.3.adoc b/liblastlog2/man/ll2_read_all.3.adoc new file mode 100644 index 0000000..fbe589d --- /dev/null +++ b/liblastlog2/man/ll2_read_all.3.adoc @@ -0,0 +1,74 @@ +//po4a: entry man manual += ll2_read_all(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_read_all - Reads all entries from database and calls the callback function for each entry. + +== SYNOPSIS + +*#include <lastlog2.h>* +*int ll2_read_all (struct ll2_context *__context__, + int (*__callback__)(const char *__user__, int64_t __ll_time__, + const char *__tty__, const char *__rhost__, + const char *__pam_service__, const char *__cb_error__), + char **__error__);* + +== DESCRIPTION + +Reads all entries from database, defined in _context_, and calls callback fuction _callback_ for each entry. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; + +static int +callback (const char *res_user, int64_t ll_time, const char *res_tty, + const char *res_rhost, const char *res_service, const char *cb_error) +{ + /* returning != 0 if no further entry has to be handled by the callback */ + return 0; +} + +int ret = ll2_read_all (NULL, callback, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_read_entry.3 b/liblastlog2/man/ll2_read_entry.3 new file mode 100644 index 0000000..53f1e26 --- /dev/null +++ b/liblastlog2/man/ll2_read_entry.3 @@ -0,0 +1,91 @@ +'\" t +.\" Title: ll2_read_entry +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_READ_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_read_entry \- Reads one entry from database and returns that. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_read_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +int64_t *\fIll_time\fP, char \fB\fItty\fP, char \fP\fIrhost\fP, +char \fB\fIpam_service\fP, char \fP\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Reads the first entry from database, defined in \fIcontext\fP, for user \fIuser\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; +int64_t res_time; +char *res_tty = NULL; +char *res_rhost = NULL; +char *res_service = NULL; + +int ret = ll2_read_entry (NULL, user, &res_time, &res_tty, &res_rhost, &res_service, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. +.sp +The evaluated values are returned by \fIll_time\fP, \fItty\fP, \fIrhost\fP and \fIpam_service\fP. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_read_entry.3.adoc b/liblastlog2/man/ll2_read_entry.3.adoc new file mode 100644 index 0000000..3bef1a8 --- /dev/null +++ b/liblastlog2/man/ll2_read_entry.3.adoc @@ -0,0 +1,72 @@ +//po4a: entry man manual += ll2_read_entry(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_read_entry - Reads one entry from database and returns that. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_read_entry (struct ll2_context *__context__, const char *__user__, + int64_t *__ll_time__, char **__tty__, char **__rhost__, + char **__pam_service__, char **__error__);* + +== DESCRIPTION + +Reads the first entry from database, defined in _context_, for user _user_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; +int64_t res_time; +char *res_tty = NULL; +char *res_rhost = NULL; +char *res_service = NULL; + +int ret = ll2_read_entry (NULL, user, &res_time, &res_tty, &res_rhost, &res_service, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. +If lastlog2 database does not exist at all, the errno ENOENT has been set +and can be checked. + +The evaluated values are returned by _ll_time_, _tty_, _rhost_ and _pam_service_. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_remove_entry.3 b/liblastlog2/man/ll2_remove_entry.3 new file mode 100644 index 0000000..3cb444b --- /dev/null +++ b/liblastlog2/man/ll2_remove_entry.3 @@ -0,0 +1,82 @@ +'\" t +.\" Title: ll2_remove_entry +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_REMOVE_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_remove_entry \- Remove all entries of an user. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_remove_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Removing all database entries, defined in \fIcontext\fP, with the user name \fIuser\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; + +int ret = ll2_remove_entry (NULL, user, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_remove_entry.3.adoc b/liblastlog2/man/ll2_remove_entry.3.adoc new file mode 100644 index 0000000..f38ab66 --- /dev/null +++ b/liblastlog2/man/ll2_remove_entry.3.adoc @@ -0,0 +1,63 @@ +//po4a: entry man manual += ll2_remove_entry(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_remove_entry - Remove all entries of an user. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_remove_entry (struct ll2_context *__context__, const char *__user__, + char **__error__);* + +== DESCRIPTION + +Removing all database entries, defined in _context_, with the user name _user_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; + +int ret = ll2_remove_entry (NULL, user, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_rename_user.3 b/liblastlog2/man/ll2_rename_user.3 new file mode 100644 index 0000000..7644838 --- /dev/null +++ b/liblastlog2/man/ll2_rename_user.3 @@ -0,0 +1,85 @@ +'\" t +.\" Title: ll2_rename_user +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_RENAME_USER" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_rename_user \- Renames an user entry. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_rename_user (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +const char *\fInewname\fP, char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Changing user name from \fIuser\fP to \fInewname\fP of one entry in +database, which is defined by \fIcontext\fP. All other entries with the user \fIuser\fP +will be deleted. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; +const char *new_user = "notroot"; + +int ret = ll2_rename_user (NULL, user, new_user, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_rename_user.3.adoc b/liblastlog2/man/ll2_rename_user.3.adoc new file mode 100644 index 0000000..2202fe2 --- /dev/null +++ b/liblastlog2/man/ll2_rename_user.3.adoc @@ -0,0 +1,66 @@ +//po4a: entry man manual += ll2_rename_user(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_rename_user - Renames an user entry. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_rename_user (struct ll2_context *__context__, const char *__user__, + const char *__newname__, char **__error__);* + +== DESCRIPTION + +Changing user name from _user_ to _newname_ of one entry in +database, which is defined by _context_. All other entries with the user _user_ +will be deleted. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; +const char *new_user = "notroot"; + +int ret = ll2_rename_user (NULL, user, new_user, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_remove_entry*(3), +*ll2_update_login_time*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_update_login_time.3 b/liblastlog2/man/ll2_update_login_time.3 new file mode 100644 index 0000000..ce08bc4 --- /dev/null +++ b/liblastlog2/man/ll2_update_login_time.3 @@ -0,0 +1,86 @@ +'\" t +.\" Title: ll2_update_login_time +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_UPDATE_LOGIN_TIME" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_update_login_time \- Writes an *new* entry with updated login time. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_update_login_time (struct ll2_context *\fIcontext\fP, +const char *\fIuser\fP, int64_t \fIll_time\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Writes an \fBnew\fP entry to database, defined in \fIcontext\fP, for user \fIuser\fP. +Time is set by \fIll_time\fP whereas the other values are taken from +an already existing entry. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +char *error = NULL; +const char *user = "root"; +int64_t login_time = time(0); // Get the system time; + +int ret = ll2_update_login_time (NULL, user, login_time, &error); +.fam +.fi +.if n .RE +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string. It could also be NULL if the return value is \-1. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_write_entry\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_update_login_time.3.adoc b/liblastlog2/man/ll2_update_login_time.3.adoc new file mode 100644 index 0000000..9cc43e1 --- /dev/null +++ b/liblastlog2/man/ll2_update_login_time.3.adoc @@ -0,0 +1,67 @@ +//po4a: entry man manual += ll2_update_login_time(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_update_login_time - Writes an *new* entry with updated login time. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_update_login_time (struct ll2_context *__context__, + const char *__user__, int64_t __ll_time__, + char **__error__);* + +== DESCRIPTION + +Writes an *new* entry to database, defined in _context_, for user _user_. +Time is set by _ll_time_ whereas the other values are taken from +an already existing entry. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + +-------------------------------------- +char *error = NULL; +const char *user = "root"; +int64_t login_time = time(0); // Get the system time; + +int ret = ll2_update_login_time (NULL, user, login_time, &error); +-------------------------------------- + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string. It could also be NULL if the return value is -1. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_write_entry*(3), +*ll2_read_entry*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/man/ll2_write_entry.3 b/liblastlog2/man/ll2_write_entry.3 new file mode 100644 index 0000000..f16294a --- /dev/null +++ b/liblastlog2/man/ll2_write_entry.3 @@ -0,0 +1,88 @@ +'\" t +.\" Title: ll2_write_entry +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: Programmer's Manual +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "LL2_WRITE_ENTRY" "3" "2024-03-20" "util\-linux 2.40" "Programmer\*(Aqs Manual" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +ll2_write_entry \- Write a new entry into the database. +.SH "SYNOPSIS" +.sp +\fB#include <lastlog2.h>\fP +.sp +\fBint ll2_write_entry (struct ll2_context *\fIcontext\fP, const char *\fIuser\fP, +int64_t \fIll_time\fP, const char *\fItty\fP, +const char *\fIrhost\fP, const char *\fIpam_service\fP, +char **\fIerror\fP);\fP +.SH "DESCRIPTION" +.sp +Writes an new entry into database, which is defined in \fIcontext\fP. +If \fIcontext\fP is NULL, the default database, defined in \fILL2_DEFAULT_DATABASE\fP, +will be taken. +.sp +.if n .RS 4 +.nf +.fam C +time_t login_time = time(0); // Get the system time +char *error = NULL; +const char *user = "root"; + +int ret = ll2_write_entry (NULL, user, login_time, "pts/0", + "192.168.122.1", NULL, &error); +.fam +.fi +.if n .RE +.sp +\fIpam_service\fP is the service or instance name which has generated the entry (optional). +.SH "RETURN VALUE" +.sp +Returns 0 on success, \-ENOMEM or \-1 on other failure. +\fIerror\fP contains an error string if the return value is \-1. +\fIerror\fP is not guaranteed to contain an error string, could also be NULL. +\fIerror\fP should be freed by the caller. +.SH "AUTHORS" +.sp +Thorsten Kukuk (\c +.MTO "kukuk\(atsuse.de" "" ")" +.SH "SEE ALSO" +.sp +\fBlastlog2\fP(3), +\fBll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all\fP(3), +\fBll2_read_entry\fP(3), +\fBll2_update_login_time\fP(3), +\fBll2_remove_entry\fP(3), +\fBll2_rename_user\fP(3), +\fBll2_import_lastlog\fP(3) +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBliblastlog2\fP library is part of the util\-linux package since version 2.40. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/liblastlog2/man/ll2_write_entry.3.adoc b/liblastlog2/man/ll2_write_entry.3.adoc new file mode 100644 index 0000000..6dca1b5 --- /dev/null +++ b/liblastlog2/man/ll2_write_entry.3.adoc @@ -0,0 +1,70 @@ +//po4a: entry man manual += ll2_write_entry(3) +:doctype: manpage +:man manual: Programmer's Manual +:man source: util-linux {release-version} +:page-layout: base +:lib: liblastlog2 +:firstversion: 2.40 + +== NAME + +ll2_write_entry - Write a new entry into the database. + +== SYNOPSIS + +*#include <lastlog2.h>* + +*int ll2_write_entry (struct ll2_context *__context__, const char *__user__, + int64_t __ll_time__, const char *__tty__, + const char *__rhost__, const char *__pam_service__, + char **__error__);* + +== DESCRIPTION + +Writes an new entry into database, which is defined in _context_. +If _context_ is NULL, the default database, defined in _LL2_DEFAULT_DATABASE_, +will be taken. + + +-------------------------------------- +time_t login_time = time(0); // Get the system time +char *error = NULL; +const char *user = "root"; + +int ret = ll2_write_entry (NULL, user, login_time, "pts/0", + "192.168.122.1", NULL, &error); +-------------------------------------- + +_pam_service_ is the service or instance name which has generated the entry (optional). + +== RETURN VALUE + +Returns 0 on success, -ENOMEM or -1 on other failure. +_error_ contains an error string if the return value is -1. +_error_ is not guaranteed to contain an error string, could also be NULL. +_error_ should be freed by the caller. + +== AUTHORS + +Thorsten Kukuk (kukuk@suse.de) + +== SEE ALSO + +*lastlog2*(3), +*ll2_new_context(3), +*ll2_unref_context(3), +*ll2_read_all*(3), +*ll2_read_entry*(3), +*ll2_update_login_time*(3), +*ll2_remove_entry*(3), +*ll2_rename_user*(3), +*ll2_import_lastlog*(3) + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/liblastlog2/meson.build b/liblastlog2/meson.build new file mode 100644 index 0000000..6f8db53 --- /dev/null +++ b/liblastlog2/meson.build @@ -0,0 +1,63 @@ +cc = meson.get_compiler('c') +pkg = import('pkgconfig') +dir_liblastlog2 = include_directories('src') +lib_lastlog2_sources = ''' + src/lastlog2.h + src/lastlog2P.h + src/lastlog2.c +'''.split() + +liblastlog2_sym = 'src/liblastlog2.sym' +liblastlog2_sym_path = '@0@/@1@'.format(meson.current_source_dir(), liblastlog2_sym) + +if build_liblastlog2 + lib_lastlog2 = both_libraries( + 'lastlog2', + lib_lastlog2_sources, + include_directories : [dir_include], + link_args : ['-Wl,--version-script=@0@'.format(liblastlog2_sym_path)], + link_depends : liblastlog2_sym, + dependencies : [lib_sqlite3], + install : build_liblastlog2, + version : liblastlog2_version, + ) + + lastlog2_dep = declare_dependency(link_with: lib_lastlog2, include_directories: dir_liblastlog2) + + lastlog2_tests = [ + 'dlopen', + 'pam_lastlog2_output', + 'remove_entry', + 'rename_user', + 'write_read_user', + 'y2038_ll2_read_all', + 'y2038_sqlite3_time', + ] + libdl = cc.find_library('dl') + + pkg.generate( + lib_lastlog2, + description : 'library to manage last login data', + subdirs : 'lastlog2', + version : pc_version + ) + meson.override_dependency('lastlog2', lastlog2_dep) + + install_headers('src/lastlog2.h', subdir : 'liblastlog2') + + foreach lastlog2_test: lastlog2_tests + test_name = 'test_lastlog2_' + lastlog2_test + exe = executable( + test_name, + 'src/tests/tst_' + lastlog2_test + '.c', + include_directories : [dir_include], + link_with : [lib_common], + dependencies : [libdl, lastlog2_dep], + ) + # the test-setup expects the helpers in the toplevel build-directory + link = meson.project_build_root() / test_name + run_command('ln', '-srf', exe.full_path(), link, + check : true) + endforeach + +endif diff --git a/liblastlog2/src/Makemodule.am b/liblastlog2/src/Makemodule.am new file mode 100644 index 0000000..04b8e97 --- /dev/null +++ b/liblastlog2/src/Makemodule.am @@ -0,0 +1,99 @@ +# includes +lastlog2incdir = $(includedir)/liblastlog2 +lastlog2inc_HEADERS = liblastlog2/src/lastlog2.h + +usrlib_exec_LTLIBRARIES += liblastlog2.la + +liblastlog2_la_SOURCES = \ + liblastlog2/src/lastlog2.c \ + liblastlog2/src/lastlog2P.h + +EXTRA_liblastlog2_la_DEPENDENCIES = \ + liblastlog2/src/liblastlog2.sym + +liblastlog2_la_LIBADD = $(SQLITE3_LIBS) + +liblastlog2_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(SOLIB_CFLAGS) \ + -I$(ul_liblastlog2_incdir) \ + -I$(top_srcdir)/liblastlog2/src + +liblastlog2_la_LDFLAGS = $(SOLIB_LDFLAGS) +if HAVE_VSCRIPT +liblastlog2_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/liblastlog2/src/liblastlog2.sym +endif +liblastlog2_la_LDFLAGS += -version-info $(LIBLASTLOG2_VERSION_INFO) + +EXTRA_DIST += liblastlog2/src/liblastlog2.sym + + +if BUILD_LIBLASTLOG2_TESTS +check_PROGRAMS += \ + test_lastlog2_dlopen \ + test_lastlog2_pam_lastlog2_output \ + test_lastlog2_remove_entry \ + test_lastlog2_rename_user \ + test_lastlog2_write_read_user \ + test_lastlog2_y2038_ll2_read_all \ + test_lastlog2_y2038_sqlite3_time + +lastlog2_tests_cflags = -DTEST_PROGRAM $(liblastlog2_la_CFLAGS) +lastlog2_tests_ldflags = -static +lastlog2_tests_ldadd = $(LDADD) liblastlog2.la $(SOLIB_LDFLAGS) $(SQLITE3_LIBS) + +test_lastlog2_dlopen_SOURCES = liblastlog2/src/tests/tst_dlopen.c +test_lastlog2_dlopen_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_dlopen_LDFLAGS = $(lastlog2_tests_ldflags) -ldl +test_lastlog2_dlopen_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_pam_lastlog2_output_SOURCES = liblastlog2/src/tests/tst_pam_lastlog2_output.c +test_lastlog2_pam_lastlog2_output_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_pam_lastlog2_output_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_pam_lastlog2_output_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_remove_entry_SOURCES = liblastlog2/src/tests/tst_remove_entry.c +test_lastlog2_remove_entry_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_remove_entry_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_remove_entry_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_rename_user_SOURCES = liblastlog2/src/tests/tst_rename_user.c +test_lastlog2_rename_user_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_rename_user_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_rename_user_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_write_read_user_SOURCES = liblastlog2/src/tests/tst_write_read_user.c +test_lastlog2_write_read_user_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_write_read_user_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_write_read_user_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_y2038_ll2_read_all_SOURCES = liblastlog2/src/tests/tst_y2038_ll2_read_all.c +test_lastlog2_y2038_ll2_read_all_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_y2038_ll2_read_all_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_y2038_ll2_read_all_LDADD = $(lastlog2_tests_ldadd) + +test_lastlog2_y2038_sqlite3_time_SOURCES = liblastlog2/src/tests/tst_y2038_sqlite3_time.c +test_lastlog2_y2038_sqlite3_time_CFLAGS = $(lastlog2_tests_cflags) +test_lastlog2_y2038_sqlite3_time_LDFLAGS = $(lastlog2_tests_ldflags) +test_lastlog2_y2038_sqlite3_time_LDADD = $(lastlog2_tests_ldadd) + +endif #BUILD_LIBLIBLASTLOG2_TESTS + + +# move lib from $(usrlib_execdir) to $(libdir) if needed +install-exec-hook-liblastlog2: + if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/liblastlog2.so"; then \ + $(MKDIR_P) $(DESTDIR)$(libdir); \ + mv $(DESTDIR)$(usrlib_execdir)/liblastlog2.so.* $(DESTDIR)$(libdir); \ + so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/liblastlog2.so); \ + so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \ + (cd $(DESTDIR)$(usrlib_execdir) && \ + rm -f liblastlog2.so && \ + $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name liblastlog2.so); \ + fi + +uninstall-hook-liblastlog2: + rm -f $(DESTDIR)$(libdir)/liblastlog2.so* + +INSTALL_EXEC_HOOKS += install-exec-hook-liblastlog2 +UNINSTALL_HOOKS += uninstall-hook-liblastlog2 diff --git a/liblastlog2/src/lastlog2.c b/liblastlog2/src/lastlog2.c new file mode 100644 index 0000000..744d41f --- /dev/null +++ b/liblastlog2/src/lastlog2.c @@ -0,0 +1,595 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <pwd.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <sys/stat.h> +#include <sqlite3.h> +#include <lastlog.h> + +#include "lastlog2P.h" +#include "strutils.h" + +/* Set the ll2 context/environment */ +/* Returns the context or NULL if an error has happened. */ +extern struct ll2_context * ll2_new_context(const char *db_path) +{ + struct ll2_context *context = (struct ll2_context *)malloc(sizeof(struct ll2_context)); + + if (context) { + if (db_path) { + if ((context->lastlog2_path = strdup(db_path)) == NULL) { + free(context); + context = NULL; + } + } else { + if ((context->lastlog2_path = strdup(LL2_DEFAULT_DATABASE)) == NULL) { + free(context); + context = NULL; + } + } + } + return context; +} + +/* Release ll2 context/environment */ +extern void ll2_unref_context(struct ll2_context *context) +{ + if (context) + free(context->lastlog2_path); + free(context); +} + +/* Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +open_database_ro(struct ll2_context *context, sqlite3 **db, char **error) +{ + int ret = 0; + char *path = LL2_DEFAULT_DATABASE; + + if (context && context->lastlog2_path) + path = context->lastlog2_path; + + if (sqlite3_open_v2(path, db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) { + ret = -1; + if (error) + if (asprintf(error, "Cannot open database (%s): %s", + path, sqlite3_errmsg(*db)) < 0) + ret = -ENOMEM; + + sqlite3_close(*db); + } + + return ret; +} + +/* Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +open_database_rw(struct ll2_context *context, sqlite3 **db, char **error) +{ + int ret = 0; + char *path = LL2_DEFAULT_DATABASE; + + if (context && context->lastlog2_path) + path = context->lastlog2_path; + + if (sqlite3_open(path, db) != SQLITE_OK) { + ret = -1; + if (error) + if (asprintf(error, "Cannot create/open database (%s): %s", + path, sqlite3_errmsg(*db)) < 0) + ret = -ENOMEM; + + sqlite3_close(*db); + } + + return ret; +} + +/* Reads one entry from database and returns that. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +read_entry(sqlite3 *db, const char *user, + int64_t *ll_time, char **tty, char **rhost, + char **pam_service, char **error) +{ + int retval = 0; + sqlite3_stmt *res = NULL; + static const char *sql = "SELECT Name,Time,TTY,RemoteHost,Service FROM Lastlog2 WHERE Name = ?"; + + if (sqlite3_prepare_v2(db, sql, -1, &res, 0) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to execute statement: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_read_entry; + } + + if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create search query: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_read_entry; + } + + int step = sqlite3_step(res); + + if (step == SQLITE_ROW) { + const unsigned char *luser = sqlite3_column_text(res, 0); + const unsigned char *uc; + + if (strcmp((const char *)luser, user) != 0) { + retval = -1; + if (error) + if (asprintf(error, "Returned data is for %s, not %s", luser, user) < 0) + retval = -ENOMEM; + goto out_read_entry; + } + + if (ll_time) + *ll_time = sqlite3_column_int64(res, 1); + + if (tty) { + uc = sqlite3_column_text(res, 2); + if (uc != NULL && strlen((const char *)uc) > 0) + if ((*tty = strdup((const char *)uc)) == NULL) { + retval = -ENOMEM; + goto out_read_entry; + } + } + if (rhost) { + uc = sqlite3_column_text(res, 3); + if (uc != NULL && strlen((const char *)uc) > 0) + if ((*rhost = strdup((const char *)uc)) == NULL) { + retval = -ENOMEM; + goto out_read_entry; + } + } + if (pam_service) { + uc = sqlite3_column_text(res, 4); + if (uc != NULL && strlen((const char *)uc) > 0) + if ((*pam_service = strdup((const char *)uc)) == NULL) { + retval = -ENOMEM; + goto out_read_entry; + } + } + } else { + retval = -1; + if (error) + if (asprintf(error, "User '%s' not found (%d)", user, step) < 0) + retval = -ENOMEM; + } + +out_read_entry: + if (res) + sqlite3_finalize(res); + + return retval; +} + +/* reads 1 entry from database and returns that. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_read_entry(struct ll2_context *context, const char *user, + int64_t *ll_time, char **tty, char **rhost, + char **pam_service, char **error) +{ + sqlite3 *db; + int retval; + + if ((retval = open_database_ro(context, &db, error)) != 0) + return retval; + + retval = read_entry(db, user, ll_time, tty, rhost, pam_service, error); + + sqlite3_close(db); + + return retval; +} + +/* Write a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +write_entry(sqlite3 *db, const char *user, + int64_t ll_time, const char *tty, const char *rhost, + const char *pam_service, char **error) +{ + int retval = 0; + char *err_msg = NULL; + sqlite3_stmt *res = NULL; + static const char *sql_table = "CREATE TABLE IF NOT EXISTS Lastlog2(Name TEXT PRIMARY KEY, Time INTEGER, TTY TEXT, RemoteHost TEXT, Service TEXT);"; + static const char *sql_replace = "REPLACE INTO Lastlog2 VALUES(?,?,?,?,?);"; + + if (sqlite3_exec(db, sql_table, 0, 0, &err_msg) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "SQL error: %s", err_msg) < 0) + retval = -ENOMEM; + + sqlite3_free(err_msg); + goto out_ll2_read_entry; + } + + if (sqlite3_prepare_v2(db, sql_replace, -1, &res, 0) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to execute statement: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for user: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_int64(res, 2, ll_time) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for ll_time: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 3, tty, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for tty: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 4, rhost, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for rhost: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + if (sqlite3_bind_text(res, 5, pam_service, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create replace statement for PAM service: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } + + int step = sqlite3_step(res); + + if (step != SQLITE_DONE) { + retval = -1; + if (error) + if (asprintf(error, "Delete statement did not return SQLITE_DONE: %d", + step) < 0) + retval = -ENOMEM; + goto out_ll2_read_entry; + } +out_ll2_read_entry: + if (res) + sqlite3_finalize(res); + + return retval; +} + +/* Write a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_write_entry(struct ll2_context *context, const char *user, + int64_t ll_time, const char *tty, const char *rhost, + const char *pam_service, char **error) +{ + sqlite3 *db; + int retval; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + retval = write_entry(db, user, ll_time, tty, rhost, pam_service, error); + + sqlite3_close(db); + + return retval; +} + +/* Write a new entry with updated login time. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_update_login_time(struct ll2_context *context, const char *user, + int64_t ll_time, char **error) +{ + sqlite3 *db; + int retval; + char *tty; + char *rhost; + char *pam_service; + + if ((retval = open_database_rw(context , &db, error)) != 0) + return retval; + + if ((retval = read_entry(db, user, 0, &tty, &rhost, &pam_service, error)) != 0) { + sqlite3_close(db); + return retval; + } + + retval = write_entry(db, user, ll_time, tty, rhost, pam_service, error); + + sqlite3_close(db); + + free(tty); + free(rhost); + free(pam_service); + + return retval; +} + + +typedef int (*callback_f)(const char *user, int64_t ll_time, + const char *tty, const char *rhost, + const char *pam_service, const char *cb_error); + +static int +callback(void *cb_func, __attribute__((unused)) int argc, char **argv, __attribute__((unused)) char **azColName) +{ + char *endptr; + callback_f print_entry = cb_func; + + errno = 0; + char *cb_error = NULL; + int64_t ll_time = strtoll(argv[1], &endptr, 10); + if ((errno == ERANGE && (ll_time == INT64_MAX || ll_time == INT64_MIN)) + || (endptr == argv[1]) || (*endptr != '\0')) + if (asprintf(&cb_error, "Invalid numeric time entry for '%s': '%s'\n", argv[0], argv[1]) < 0) + return -1; + + print_entry(argv[0], ll_time, argv[2], argv[3], argv[4], cb_error); + free(cb_error); + + return 0; +} + +/* Reads all entries from database and calls the callback function for each entry. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_read_all(struct ll2_context *context, + int (*cb_func)(const char *user, int64_t ll_time, + const char *tty, const char *rhost, + const char *pam_service, const char *cb_error), + char **error) +{ + sqlite3 *db; + char *err_msg = 0; + int retval = 0; + + if ((retval = open_database_ro(context, &db, error)) != 0) + return retval; + + static const char *sql = "SELECT Name,Time,TTY,RemoteHost,Service FROM Lastlog2 ORDER BY Name ASC"; + + if (sqlite3_exec(db, sql, callback, cb_func, &err_msg) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "SQL error: %s", err_msg) < 0) + retval = -ENOMEM; + + sqlite3_free(err_msg); + } + + sqlite3_close(db); + + return retval; +} + +/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +static int +remove_entry(sqlite3 *db, const char *user, char **error) +{ + int retval = 0; + sqlite3_stmt *res = NULL; + static const char *sql = "DELETE FROM Lastlog2 WHERE Name = ?"; + + if (sqlite3_prepare_v2(db, sql, -1, &res, 0) != SQLITE_OK) { + if (error) + if (asprintf(error, "Failed to execute statement: %s", + sqlite3_errmsg(db)) < 0) + return -ENOMEM; + + return -1; + } + + if (sqlite3_bind_text(res, 1, user, -1, SQLITE_STATIC) != SQLITE_OK) { + retval = -1; + if (error) + if (asprintf(error, "Failed to create delete statement: %s", + sqlite3_errmsg(db)) < 0) + retval = -ENOMEM; + goto out_remove_entry; + } + + int step = sqlite3_step(res); + + if (step != SQLITE_DONE) { + retval = -1; + if (error) + if (asprintf(error, "Delete statement did not return SQLITE_DONE: %d", + step) < 0) + retval = -ENOMEM; + } +out_remove_entry: + if (res) + sqlite3_finalize(res); + + return retval; +} + +/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_remove_entry(struct ll2_context *context, const char *user, + char **error) +{ + sqlite3 *db; + int retval; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + retval = remove_entry(db, user, error); + + sqlite3_close(db); + + return retval; +} + +/* Renames an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_rename_user(struct ll2_context *context, const char *user, + const char *newname, char **error) +{ + sqlite3 *db; + int64_t ll_time; + char *tty; + char *rhost; + char *pam_service; + int retval; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + if ((retval = read_entry(db, user, &ll_time, &tty, &rhost, &pam_service, error) != 0)) { + sqlite3_close(db); + return retval; + } + + if ((retval = write_entry(db, newname, ll_time, tty, rhost, pam_service, error) != 0)) { + sqlite3_close(db); + free(tty); + free(rhost); + return retval; + } + + retval = remove_entry(db, user, error); + + sqlite3_close(db); + + free(tty); + free(rhost); + free(pam_service); + + return retval; +} + +/* Import old lastlog file. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +int +ll2_import_lastlog(struct ll2_context *context, const char *lastlog_file, + char **error) +{ + const struct passwd *pw; + struct stat statll; + sqlite3 *db; + FILE *ll_fp; + int retval = 0; + + if ((retval = open_database_rw(context, &db, error)) != 0) + return retval; + + ll_fp = fopen(lastlog_file, "r"); + if (ll_fp == NULL) { + if (error && asprintf(error, "Failed to open '%s': %s", + lastlog_file, strerror(errno)) < 0) + return -ENOMEM; + + return -1; + } + + + if (fstat(fileno(ll_fp), &statll) != 0) { + retval = -1; + if (error && asprintf(error, "Cannot get size of '%s': %s", + lastlog_file, strerror(errno)) < 0) + retval = -ENOMEM; + + goto done; + } + + setpwent(); + while ((pw = getpwent()) != NULL ) { + off_t offset; + struct lastlog ll; + + offset = (off_t) pw->pw_uid * sizeof (ll); + + if ((offset + (off_t)sizeof(ll)) <= statll.st_size) { + if (fseeko(ll_fp, offset, SEEK_SET) == -1) + continue; /* Ignore seek error */ + + if (fread(&ll, sizeof(ll), 1, ll_fp) != 1) { + retval = -1; + if (error) + if (asprintf(error, "Failed to get the entry for UID '%lu'", + (unsigned long int)pw->pw_uid) < 0) + retval = -ENOMEM; + goto out_import_lastlog; + } + + if (ll.ll_time != 0) { + int64_t ll_time; + char tty[sizeof(ll.ll_line) + 1]; + char rhost[sizeof(ll.ll_host) + 1]; + + ll_time = ll.ll_time; + mem2strcpy(tty, ll.ll_line, sizeof(ll.ll_line), sizeof(tty)); + mem2strcpy(rhost, ll.ll_host, sizeof(ll.ll_host), sizeof(rhost)); + + if ((retval = write_entry(db, pw->pw_name, ll_time, tty, + rhost, NULL, error)) != 0) + goto out_import_lastlog; + } + } + } +out_import_lastlog: + endpwent(); + sqlite3_close(db); +done: + fclose(ll_fp); + + return retval; +} diff --git a/liblastlog2/src/lastlog2.h b/liblastlog2/src/lastlog2.h new file mode 100644 index 0000000..280f387 --- /dev/null +++ b/liblastlog2/src/lastlog2.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _LIBLASTLOG2_H +#define _LIBLASTLOG2_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LL2_DEFAULT_DATABASE _PATH_LOCALSTATEDIR "/lib/lastlog/lastlog2.db" + +#include <stdint.h> + +struct ll2_context; + +/* Set the ll2 context/environment */ +/* Returns the context or NULL if an error has happened. */ +extern struct ll2_context * ll2_new_context(const char *db_path); + +/* Release ll2 context/environment */ +extern void ll2_unref_context(struct ll2_context *context); + +/* Writes a new entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_write_entry (struct ll2_context *context, const char *user, + int64_t ll_time, const char *tty, + const char *rhost, const char *pam_service, + char **error); + +/* Calling a defined function for each entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_read_all (struct ll2_context *context, + int (*callback)(const char *user, int64_t ll_time, + const char *tty, const char *rhost, + const char *pam_service, const char *cb_error), + char **error); + +/* Reads one entry from database and returns that. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_read_entry (struct ll2_context *context, const char *user, + int64_t *ll_time, char **tty, char **rhost, + char **pam_service, char **error); + +/* Write a new entry with updated login time. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_update_login_time (struct ll2_context *context, + const char *user, int64_t ll_time, + char **error); + +/* Remove an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_remove_entry (struct ll2_context *context, const char *user, + char **error); + +/* Renames an user entry. Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_rename_user (struct ll2_context *context, const char *user, + const char *newname, char **error); + + +/* Import old lastlog file. + Returns 0 on success, -ENOMEM or -1 on other failure. */ +extern int ll2_import_lastlog (struct ll2_context *context, + const char *lastlog_file, char **error); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBLASTLOG2_H */ diff --git a/liblastlog2/src/lastlog2P.h b/liblastlog2/src/lastlog2P.h new file mode 100644 index 0000000..b39f660 --- /dev/null +++ b/liblastlog2/src/lastlog2P.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2024, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _LIBLASTLOG2_P_H +#define _LIBLASTLOG2_P_H + +#include "lastlog2.h" + +struct ll2_context { + char *lastlog2_path; +}; + +#endif /* _LIBLASTLOG2_P_H */ diff --git a/liblastlog2/src/liblastlog2.sym b/liblastlog2/src/liblastlog2.sym new file mode 100644 index 0000000..c21cced --- /dev/null +++ b/liblastlog2/src/liblastlog2.sym @@ -0,0 +1,13 @@ +LIBLASTLOG2_2_40 { + global: + ll2_new_context; + ll2_unref_context; + ll2_read_all; + ll2_read_entry; + ll2_remove_entry; + ll2_rename_user; + ll2_write_entry; + ll2_update_login_time; + ll2_import_lastlog; + local: *; +}; diff --git a/liblastlog2/src/tests/tst_dlopen.c b/liblastlog2/src/tests/tst_dlopen.c new file mode 100644 index 0000000..112564b --- /dev/null +++ b/liblastlog2/src/tests/tst_dlopen.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + + Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2003 + + 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; either version 2 of the License, or + (at your option) any later version. +*/ + +#include <dlfcn.h> +#include <stdio.h> +#include <limits.h> +#include <sys/stat.h> + +/* Simple program to see if dlopen() would succeed. */ +int main(int argc, char **argv) +{ + int i; + struct stat st; + char buf[PATH_MAX]; + + for (i = 1; i < argc; i++) { + if (dlopen(argv[i], RTLD_NOW)) { + fprintf(stdout, "dlopen() of \"%s\" succeeded.\n", + argv[i]); + } else { + snprintf(buf, sizeof(buf), "./%s", argv[i]); + if ((stat(buf, &st) == 0) && dlopen(buf, RTLD_NOW)) { + fprintf(stdout, "dlopen() of \"./%s\" " + "succeeded.\n", argv[i]); + } else { + fprintf(stdout, "dlopen() of \"%s\" failed: " + "%s\n", argv[i], dlerror()); + return 1; + } + } + } + return 0; +} diff --git a/liblastlog2/src/tests/tst_pam_lastlog2_output.c b/liblastlog2/src/tests/tst_pam_lastlog2_output.c new file mode 100644 index 0000000..1fd1eec --- /dev/null +++ b/liblastlog2/src/tests/tst_pam_lastlog2_output.c @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Test case: + Store defined data into the database, read it, create the time + string like pam_lastlog2 does, compare the result. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "lastlog2P.h" + +const char *expected = "Last login: Mon Mar 13 07:13:41 UTC 2023 from 192.168.122.1 on pts/0"; +const time_t login_time = 1678691621; +int +main(void) +{ + const char *user = "root"; + struct ll2_context *context = ll2_new_context("pam_lastlog2-output.db"); + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *date = NULL; + char the_time[256]; + char *error = NULL; + char *output = NULL; + + if (ll2_write_entry (context, user, login_time, "pts/0", + "192.168.122.1", NULL, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, + NULL, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context ? context->lastlog2_path : "NULL"); + ll2_unref_context(context); + return 1; + } + + if (ll_time) { + struct tm *tm, tm_buf; + /* this is necessary if you compile this on architectures with + a 32bit time_t type. */ + time_t t_time = ll_time; + + if ((tm = localtime_r (&t_time, &tm_buf)) != NULL) { + strftime (the_time, sizeof (the_time), + " %a %b %e %H:%M:%S %Z %Y", tm); + date = the_time; + } + } + + if (asprintf (&output, "Last login:%s%s%s%s%s", + date ? date : "", + rhost ? " from " : "", + rhost ? rhost : "", + tty ? " on " : "", + tty ? tty : "") < 0) { + fprintf (stderr, "Out of memory!\n"); + ll2_unref_context(context); + return 1; + } + + if (strcmp (output, expected) != 0) { + fprintf (stderr, "Output '%s'\n does not match '%s'\n", + output, expected); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + free (output); + free (tty); + free (rhost); + + return 0; +} diff --git a/liblastlog2/src/tests/tst_remove_entry.c b/liblastlog2/src/tests/tst_remove_entry.c new file mode 100644 index 0000000..39079c8 --- /dev/null +++ b/liblastlog2/src/tests/tst_remove_entry.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Test case: + Create an entry, delete that entry, and try to read entry again. + Reading the entry should fail. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lastlog2.h" + +int +main(void) +{ + const char *user = "user"; + struct ll2_context *context = ll2_new_context("tst-delete-user.db"); + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *service = NULL; + char *error = NULL; + + if (ll2_write_entry (context, user, time (NULL), "test-tty", + "localhost", "sshd", &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_remove_entry (context, user, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_remove_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + /* this needs to fail, as the old entry shouldn't exist anymore. */ + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, &service, &error) == 0) { + fprintf (stderr, "Reading old user from database did not fail!\n"); + fprintf (stderr, "ll_time=%lld, tty='%s', rhost='%s', service='%s'\n", + (long long int)ll_time, tty, rhost, service); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + free (error); + free (tty); + free (rhost); + free (service); + + return 0; +} diff --git a/liblastlog2/src/tests/tst_rename_user.c b/liblastlog2/src/tests/tst_rename_user.c new file mode 100644 index 0000000..a1788b5 --- /dev/null +++ b/liblastlog2/src/tests/tst_rename_user.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Test case: + Create an entry, rename that entry, and try to read the old and + new entry again. Reading the old entry should fail. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lastlog2P.h" + +int +main(void) +{ + const char *user = "user"; + const char *newname = "new"; + struct ll2_context *context = ll2_new_context("tst-rename-user.db"); + int64_t ll_time = 0; + char *tty = NULL; + char *rhost = NULL; + char *service = NULL; + char *error = NULL; + + if (ll2_write_entry (context, user, time (NULL), "test-tty", + "localhost", "test-service", &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } + else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_rename_user (context, user, newname, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } + else + fprintf (stderr, "ll2_rename_user failed\n"); + ll2_unref_context(context); + return 1; + } + + /* this needs to fail, as the old entry shouldn't exist anymore. */ + if (ll2_read_entry (context, user, &ll_time, &tty, &rhost, + &service, &error) == 0) { + fprintf (stderr, "Reading old user from database did not fail!\n"); + fprintf (stderr, "ll_time=%lld, tty='%s', rhost='%s', service='%s'\n", + (long long int)ll_time, tty, rhost, service); + ll2_unref_context(context); + return 1; + } + + if (error) { + free (error); + error = NULL; + } + + if (ll2_read_entry (context, newname, &ll_time, &tty, &rhost, &service, + &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path); + ll2_unref_context(context); + return 1; + } + + if (strcmp (tty, "test-tty") != 0 || strcmp (rhost, "localhost") != 0 || + strcmp (service, "test-service") != 0) { + fprintf (stderr, "New entry data does not match old entry data!\n"); + } + + ll2_unref_context(context); + free (tty); + free (rhost); + free (service); + + return 0; +} diff --git a/liblastlog2/src/tests/tst_write_read_user.c b/liblastlog2/src/tests/tst_write_read_user.c new file mode 100644 index 0000000..dbf1db7 --- /dev/null +++ b/liblastlog2/src/tests/tst_write_read_user.c @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Test case: + Create an entry, rename that entry, and try to read the old and + new entry again. Reading the old entry should fail. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "lastlog2P.h" + +static int +test_args (struct ll2_context *context, const char *user, int64_t ll_time, + const char *tty, const char *rhost, const char *service) +{ + char *error = NULL; + int64_t res_time; + char *res_tty = NULL; + char *res_rhost = NULL; + char *res_service = NULL; + + if (ll2_write_entry (context, user, ll_time, tty, rhost, service, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + return 1; + } + + if (ll2_read_entry (context, user, &res_time, &res_tty, &res_rhost, &res_service, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path); + return 1; + } + + if (ll_time != res_time) { + fprintf (stderr, "Wrong time: got %lld, expect %lld\n", + (long long int)res_time, (long long int)ll_time); + return 1; + } + + if ((tty == NULL && res_tty != NULL) || + (tty != NULL && res_tty == NULL) || + (tty != NULL && res_tty != NULL && strcmp (tty, res_tty) != 0)) { + fprintf (stderr, "Wrong tty: got %s, expect %s\n", tty, res_tty); + return 1; + } + + if ((rhost == NULL && res_rhost != NULL) || + (rhost != NULL && res_rhost == NULL) || + (rhost != NULL && res_rhost != NULL && strcmp (rhost, res_rhost) != 0)) { + fprintf (stderr, "Wrong rhost: got %s, expect %s\n", rhost, res_rhost); + return 1; + } + + if ((service == NULL && res_service != NULL) || + (service != NULL && res_service == NULL) || + (service != NULL && res_service != NULL && strcmp (service, res_service) != 0)) { + fprintf (stderr, "Wrong service: got %s, expect %s\n", service, res_service); + return 1; + } + + + free (res_tty); + free (res_rhost); + free (res_service); + + return 0; +} + +int +main(void) +{ + struct ll2_context *context = ll2_new_context("tst-write-read-user.db"); + char *error = NULL; + int64_t res_time; + char *res_tty = NULL; + char *res_rhost = NULL; + char *res_service = NULL; + + if (test_args (context, "user1", time (NULL), "test-tty", "localhost", "test") != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user2", 0, NULL, NULL, NULL) != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user3", time (NULL), NULL, NULL, NULL) != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user4", time (NULL), "test-tty", NULL, NULL) != 0) { + ll2_unref_context(context); + return 1; + } + if (test_args (context, "user5", time (NULL), NULL, "localhost", NULL) != 0) { + ll2_unref_context(context); + return 1; + } + + /* Checking errno if the db file does not exist */ + struct ll2_context *context_not_found = ll2_new_context("no_file"); + if (ll2_read_entry (context_not_found, "user", &res_time, &res_tty, &res_rhost, &res_service, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Couldn't read entries for all users\n"); + + if(errno) { + if (errno == ENOENT) + { + fprintf (stderr, "Returning the correct errno: %s\n", + strerror (errno)); + ll2_unref_context(context_not_found); + return 0; + } + fprintf (stderr, "errno: %s\n", + strerror (errno)); + } else { + fprintf (stderr, "errno: NULL\n"); + } + + ll2_unref_context(context_not_found); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + return 0; +} diff --git a/liblastlog2/src/tests/tst_y2038_ll2_read_all.c b/liblastlog2/src/tests/tst_y2038_ll2_read_all.c new file mode 100644 index 0000000..014ae9d --- /dev/null +++ b/liblastlog2/src/tests/tst_y2038_ll2_read_all.c @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Test case: + Create an entry with an 3*INT32_MAX timestamp, store that, + read that via ll2_read_all callback again and make sure the + timestamp is correct. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include "lastlog2P.h" + +#define BIG_TIME_VALUE ((int64_t)3*INT32_MAX) + +const char *user = "y2038"; +const char *on_tty = "pts/test"; +const char *rhost = NULL; +const char *service = "sshd"; + +static int +check_y2038 (const char *res_user, int64_t ll_time, const char *res_tty, + const char *res_rhost, const char *res_service, const char *error) +{ + + if (strcmp (user, res_user) != 0) { + fprintf (stderr, "write/read entry user mismatch: written: %s, got: %s\n", + user, res_user); + exit (1); + } + + if (ll_time != BIG_TIME_VALUE) { + fprintf (stderr, "write/read entry time mismatch: written: %lld, got: %lld\n", + (long long int)BIG_TIME_VALUE, (long long int)ll_time); + exit (1); + } + + if (strcmp (on_tty, res_tty) != 0) { + fprintf (stderr, "write/read entry tty mismatch: written: %s, got: %s\n", + on_tty, res_tty); + exit (1); + } + + if (rhost != NULL) { + fprintf (stderr, "write/read entry rhost mismatch: written: NULL, got: %s\n", + res_rhost); + exit (1); + } + + if (strcmp (service, res_service) != 0) { + fprintf (stderr, "write/read entry service mismatch: written: %s, got: %s\n", + service, res_service); + exit (1); + } + + if (error != NULL) { + fprintf (stderr, "got error: %s\n", + error); + exit (1); + } + + return 0; +} + +int +main(void) +{ + struct ll2_context *context = ll2_new_context("y2038-ll2_read_all.db"); + char *error = NULL; + + remove (context->lastlog2_path); + + printf ("Big time value is: %lld\n", (long long int)BIG_TIME_VALUE); + + if (ll2_write_entry (context, user, BIG_TIME_VALUE, on_tty, rhost, service, + &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } + else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_read_all (context, check_y2038, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Couldn't read entries for all users\n"); + ll2_unref_context(context); + return 1; + } + + /* Checking errno if the db file does not exist */ + remove (context->lastlog2_path); + + if (ll2_read_all (context, check_y2038, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Couldn't read entries for all users\n"); + + if(errno) { + if (errno == ENOENT) + { + fprintf (stderr, "Returning the correct errno: %s\n", + strerror (errno)); + ll2_unref_context(context); + return 0; + } + fprintf (stderr, "errno: %s\n", + strerror (errno)); + } else { + fprintf (stderr, "errno: NULL\n"); + } + + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + return 0; +} diff --git a/liblastlog2/src/tests/tst_y2038_sqlite3_time.c b/liblastlog2/src/tests/tst_y2038_sqlite3_time.c new file mode 100644 index 0000000..a296634 --- /dev/null +++ b/liblastlog2/src/tests/tst_y2038_sqlite3_time.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: BSD-2-Clause + + Copyright (c) 2023, Thorsten Kukuk <kukuk@suse.com> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Test case: + Create an entry with an INT64_MAX-1000 timestamp, store that, + read that again and make sure the timestamp is correct. +*/ + +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "lastlog2P.h" + +#define BIG_TIME_VALUE (INT64_MAX - 1000) + +int +main(void) +{ + const char *user = "y2038"; + struct ll2_context *context = ll2_new_context("y2038-sqlite3-time.db"); + int64_t ll_time = 0; + char *error = NULL; + + printf ("Big time value is: %lld\n", (long long int)BIG_TIME_VALUE); + + if (ll2_write_entry (context, user, BIG_TIME_VALUE, NULL, NULL, + NULL, &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "ll2_write_entry failed\n"); + ll2_unref_context(context); + return 1; + } + + if (ll2_read_entry (context, user, &ll_time, NULL, NULL, NULL, + &error) != 0) { + if (error) { + fprintf (stderr, "%s\n", error); + free (error); + } else + fprintf (stderr, "Unknown error reading database %s", context->lastlog2_path); + ll2_unref_context(context); + return 1; + } + + if (ll_time != BIG_TIME_VALUE) { + fprintf (stderr, "write/read entry time mismatch: written: %lld, got: %lld\n", + (long long int)BIG_TIME_VALUE, (long long int)ll_time); + ll2_unref_context(context); + return 1; + } + + ll2_unref_context(context); + return 0; +} diff --git a/libmount/Makemodule.am b/libmount/Makemodule.am index 8546c2f..02fb521 100644 --- a/libmount/Makemodule.am +++ b/libmount/Makemodule.am @@ -2,6 +2,7 @@ if BUILD_LIBMOUNT include libmount/src/Makemodule.am include libmount/python/Makemodule.am +include libmount/samples/Makemodule.am if ENABLE_GTK_DOC # Docs uses separate Makefiles diff --git a/libmount/docs/Makefile.in b/libmount/docs/Makefile.in index bf87c32..e74b624 100644 --- a/libmount/docs/Makefile.in +++ b/libmount/docs/Makefile.in @@ -117,7 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_vscript.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/tls.m4 \ - $(top_srcdir)/m4/ul.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ul.m4 $(top_srcdir)/m4/year2038.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -159,10 +160,12 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BISON = @BISON@ BSD_WARN_CFLAGS = @BSD_WARN_CFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPTSETUP_CFLAGS = @CRYPTSETUP_CFLAGS@ @@ -192,6 +195,7 @@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ +FLEX = @FLEX@ FUZZING_ENGINE_LDFLAGS = @FUZZING_ENGINE_LDFLAGS@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ @@ -219,6 +223,8 @@ LIBFDISK_VERSION = @LIBFDISK_VERSION@ LIBFDISK_VERSION_INFO = @LIBFDISK_VERSION_INFO@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ +LIBLASTLOG2_VERSION = @LIBLASTLOG2_VERSION@ +LIBLASTLOG2_VERSION_INFO = @LIBLASTLOG2_VERSION_INFO@ LIBMOUNT_MAJOR_VERSION = @LIBMOUNT_MAJOR_VERSION@ LIBMOUNT_MINOR_VERSION = @LIBMOUNT_MINOR_VERSION@ LIBMOUNT_PATCH_VERSION = @LIBMOUNT_PATCH_VERSION@ @@ -244,6 +250,7 @@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MATH_LIBS = @MATH_LIBS@ MKDIR_P = @MKDIR_P@ +MQ_LIBS = @MQ_LIBS@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ @@ -257,7 +264,6 @@ NCURSES_CFLAGS = @NCURSES_CFLAGS@ NCURSES_LIBS = @NCURSES_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ -NO_UNUSED_WARN_CFLAGS = @NO_UNUSED_WARN_CFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -297,6 +303,8 @@ SHELL = @SHELL@ SOCKET_LIBS = @SOCKET_LIBS@ SOLIB_CFLAGS = @SOLIB_CFLAGS@ SOLIB_LDFLAGS = @SOLIB_LDFLAGS@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ STRIP = @STRIP@ SUID_CFLAGS = @SUID_CFLAGS@ SUID_LDFLAGS = @SUID_LDFLAGS@ @@ -382,6 +390,7 @@ sysconfdir = @sysconfdir@ sysconfstaticdir = @sysconfstaticdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ +tmpfilesdir = @tmpfilesdir@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @@ -391,6 +400,7 @@ usrsbin_execdir = @usrsbin_execdir@ vendordir = @vendordir@ with_bashcompletiondir = @with_bashcompletiondir@ with_systemdsystemunitdir = @with_systemdsystemunitdir@ +with_tmpfilesdir = @with_tmpfilesdir@ # We require automake 1.10 at least. AUTOMAKE_OPTIONS = 1.10 diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt index 86a7622..0b4adb5 100644 --- a/libmount/docs/libmount-sections.txt +++ b/libmount/docs/libmount-sections.txt @@ -302,6 +302,8 @@ libmnt_lock mnt_free_lock mnt_lock_file mnt_new_lock +mnt_ref_lock +mnt_unref_lock mnt_unlock_file mnt_lock_block_signals </SECTION> @@ -454,5 +456,6 @@ mnt_monitor_get_fd mnt_monitor_close_fd mnt_monitor_next_change mnt_monitor_event_cleanup +mnt_monitor_veil_kernel mnt_monitor_wait </SECTION> diff --git a/libmount/docs/version.xml b/libmount/docs/version.xml index a69af57..4bdd32f 100644 --- a/libmount/docs/version.xml +++ b/libmount/docs/version.xml @@ -1 +1 @@ -2.39.3 +2.40 diff --git a/libmount/meson.build b/libmount/meson.build index 55de27b..d1262e7 100644 --- a/libmount/meson.build +++ b/libmount/meson.build @@ -83,7 +83,7 @@ lib_mount_static = static_library( lib__mount_deps = [ lib_selinux, - get_option('cryptsetup-dlopen').enabled() ? lib_dl : lib_cryptsetup, + cryptsetup_dlopen ? lib_dl : lib_cryptsetup, realtime_libs ] lib_mount = library( @@ -116,6 +116,7 @@ libmount_tests = [ 'context', 'lock', 'optstr', + 'optlist', 'tab', 'tab_diff', 'monitor', @@ -129,20 +130,22 @@ libmount_test_src_override = { 'debug': 'init', } -foreach libmount_test: libmount_tests - test_name = 'test_mount_' + libmount_test - exe = executable( - test_name, - 'src/' + libmount_test_src_override.get(libmount_test, libmount_test) + '.c', - include_directories : [dir_include, dir_libblkid], - link_with : [lib__mount, lib_common, lib_blkid_static], - dependencies : lib__mount_deps, - c_args : ['-DTEST_PROGRAM'], - ) - # the test-setup expects the helpers in the toplevel build-directory - link = meson.project_build_root() / test_name - run_command('ln', '-srf', exe.full_path(), link, - check : true) -endforeach +if program_tests + foreach libmount_test: libmount_tests + test_name = 'test_mount_' + libmount_test + exe = executable( + test_name, + 'src/' + libmount_test_src_override.get(libmount_test, libmount_test) + '.c', + include_directories : [dir_include, dir_libblkid], + link_with : [lib__mount, lib_common, lib_blkid_static], + dependencies : lib__mount_deps, + c_args : ['-DTEST_PROGRAM'], + ) + # the test-setup expects the helpers in the toplevel build-directory + link = meson.project_build_root() / test_name + run_command('ln', '-srf', exe.full_path(), link, + check : true) + endforeach +endif subdir('python') diff --git a/libmount/python/context.c b/libmount/python/context.c index 353a893..36cf488 100644 --- a/libmount/python/context.c +++ b/libmount/python/context.c @@ -1175,43 +1175,17 @@ static PyObject *Context_repr(ContextObjext *self) PyTypeObject ContextType = { PyVarObject_HEAD_INIT(NULL, 0) - "libmount.Context", /*tp_name*/ - sizeof(ContextObjext), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Context_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - NULL, /*tp_getattr*/ - NULL, /*tp_setattr*/ - NULL, /*tp_compare*/ - (reprfunc) Context_repr, - NULL, /*tp_as_number*/ - NULL, /*tp_as_sequence*/ - NULL, /*tp_as_mapping*/ - NULL, /*tp_hash */ - NULL, /*tp_call*/ - NULL, /*tp_str*/ - NULL, /*tp_getattro*/ - NULL, /*tp_setattro*/ - NULL, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - Context_HELP, /* tp_doc */ - NULL, /* tp_traverse */ - NULL, /* tp_clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - Context_methods, /* tp_methods */ - Context_members, /* tp_members */ - Context_getseters, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Context_init, /* tp_init */ - NULL, /* tp_alloc */ - Context_new, /* tp_new */ + .tp_name = "libmount.Context", + .tp_basicsize = sizeof(ContextObjext), + .tp_dealloc = (destructor)Context_dealloc, + .tp_repr = (reprfunc) Context_repr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Context_HELP, + .tp_methods = Context_methods, + .tp_members = Context_members, + .tp_getset = Context_getseters, + .tp_init = (initproc)Context_init, + .tp_new = Context_new, }; void Context_AddModuleObject(PyObject *mod) diff --git a/libmount/python/fs.c b/libmount/python/fs.c index e989124..ccb2460 100644 --- a/libmount/python/fs.c +++ b/libmount/python/fs.c @@ -832,43 +832,17 @@ static PyObject *Fs_copy_fs(FsObject *self, PyObject *args, PyObject *kwds) PyTypeObject FsType = { PyVarObject_HEAD_INIT(NULL, 0) - "libmount.Fs", /*tp_name*/ - sizeof(FsObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Fs_destructor, /*tp_dealloc*/ - 0, /*tp_print*/ - NULL, /*tp_getattr*/ - NULL, /*tp_setattr*/ - NULL, /*tp_compare*/ - (reprfunc)Fs_repr, /*tp_repr*/ - NULL, /*tp_as_number*/ - NULL, /*tp_as_sequence*/ - NULL, /*tp_as_mapping*/ - NULL, /*tp_hash */ - NULL, /*tp_call*/ - NULL, /*tp_str*/ - NULL, /*tp_getattro*/ - NULL, /*tp_setattro*/ - NULL, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - Fs_HELP, /* tp_doc */ - NULL, /* tp_traverse */ - NULL, /* tp_clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - Fs_methods, /* tp_methods */ - Fs_members, /* tp_members */ - Fs_getseters, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Fs_init, /* tp_init */ - NULL, /* tp_alloc */ - Fs_new, /* tp_new */ + .tp_name = "libmount.Fs", + .tp_basicsize = sizeof(FsObject), + .tp_dealloc = (destructor)Fs_destructor, + .tp_repr = (reprfunc)Fs_repr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Fs_HELP, + .tp_methods = Fs_methods, + .tp_members = Fs_members, + .tp_getset = Fs_getseters, + .tp_init = (initproc)Fs_init, + .tp_new = Fs_new, }; void FS_AddModuleObject(PyObject *mod) diff --git a/libmount/python/meson.build b/libmount/python/meson.build index c5feb7b..e1a79d1 100644 --- a/libmount/python/meson.build +++ b/libmount/python/meson.build @@ -17,23 +17,23 @@ if LINUX pylibmount_sources += 'context.c' endif -python.extension_module( - 'pylibmount', - pylibmount_sources, - include_directories : [dir_include, dir_libmount], - subdir : 'libmount', - link_with : lib_mount, - dependencies : python.dependency(), - c_args : [ - '-Wno-cast-function-type', +if build_python + python.extension_module( + 'pylibmount', + pylibmount_sources, + include_directories : [dir_include, dir_libmount], + subdir : 'libmount', + link_with : lib_mount, + dependencies : python.dependency(), + c_args : [ + '-Wno-cast-function-type', - # https://github.com/util-linux/util-linux/issues/2366 - python.language_version().version_compare('>=3.12') ? - [ '-Wno-error=redundant-decls' ] : [], - ], - install : true) + # https://github.com/util-linux/util-linux/issues/2366 + python.language_version().version_compare('>=3.12') ? + [ '-Wno-error=redundant-decls' ] : [], + ], + install : true) -if build_python python.install_sources( '__init__.py', subdir : 'libmount', diff --git a/libmount/python/tab.c b/libmount/python/tab.c index 000bc13..d33a1fe 100644 --- a/libmount/python/tab.c +++ b/libmount/python/tab.c @@ -731,43 +731,17 @@ static PyObject *Table_repr(TableObject *self) PyTypeObject TableType = { PyVarObject_HEAD_INIT(NULL, 0) - "libmount.Table", /*tp_name*/ - sizeof(TableObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)Table_destructor, /*tp_dealloc*/ - 0, /*tp_print*/ - NULL, /*tp_getattr*/ - NULL, /*tp_setattr*/ - NULL, /*tp_compare*/ - (reprfunc) Table_repr, /*tp_repr*/ - NULL, /*tp_as_number*/ - NULL, /*tp_as_sequence*/ - NULL, /*tp_as_mapping*/ - NULL, /*tp_hash */ - NULL, /*tp_call*/ - NULL, /*tp_str*/ - NULL, /*tp_getattro*/ - NULL, /*tp_setattro*/ - NULL, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - Table_HELP, /* tp_doc */ - NULL, /* tp_traverse */ - NULL, /* tp_clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - Table_methods, /* tp_methods */ - Table_members, /* tp_members */ - Table_getseters, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Table_init, /* tp_init */ - NULL, /* tp_alloc */ - Table_new, /* tp_new */ + .tp_name = "libmount.Table", + .tp_basicsize = sizeof(TableObject), + .tp_dealloc = (destructor)Table_destructor, + .tp_repr = (reprfunc) Table_repr, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = Table_HELP, + .tp_methods = Table_methods, + .tp_members = Table_members, + .tp_getset = Table_getseters, + .tp_init = (initproc)Table_init, + .tp_new = Table_new, }; void Table_AddModuleObject(PyObject *mod) diff --git a/libmount/samples/Makemodule.am b/libmount/samples/Makemodule.am new file mode 100644 index 0000000..e223ea6 --- /dev/null +++ b/libmount/samples/Makemodule.am @@ -0,0 +1,11 @@ + +check_PROGRAMS += \ + sample-mount-overwrite + +sample_mount_cflags = $(AM_CFLAGS) -I$(ul_libmount_incdir) +sample_mount_ldadd = libmount.la $(LDADD) + + +sample_mount_overwrite_SOURCES = libmount/samples/overwrite.c +sample_mount_overwrite_LDADD = $(sample_mount_ldadd) +sample_mount_overwrite_CFLAGS = $(sample_mount_cflags) diff --git a/libmount/samples/overwrite.c b/libmount/samples/overwrite.c new file mode 100644 index 0000000..ff11b71 --- /dev/null +++ b/libmount/samples/overwrite.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * + * This sample reads the mountpoint entry from /etc/fstab and mounts it to the + * different (on the command line specified) mountpoint. The mount options + * settings are read from fstab. + */ +#include <stdlib.h> + +#include "c.h" + +#include "libmount.h" + + +int main(int argc, char *argv[]) +{ + char *target, *fstab_target; + struct libmnt_table *tab; + struct libmnt_fs *fs; + struct libmnt_context *cxt; + int rc; + + if (argc != 3) + errx(EXIT_FAILURE, "usage: %s <mnt-from-fstab> <target>", + program_invocation_short_name); + + fstab_target = argv[1]; + target = argv[2]; + + printf("Mounting %s from fstab to %s\n", fstab_target, target); + + tab = mnt_new_table_from_file("/etc/fstab"); + if (!tab) + err(EXIT_FAILURE, "failed to parse fstab"); + + fs = mnt_table_find_target(tab, fstab_target, MNT_ITER_FORWARD); + if (!fs) + err(EXIT_FAILURE, "cannot found %s in fstab", argv[1]); + + cxt = mnt_new_context(); + if (!cxt) + err(EXIT_FAILURE, "cannot allocate context"); + + mnt_context_set_fs(cxt, fs); + mnt_context_set_target(cxt, target); + + rc = mnt_context_mount(cxt); + + printf("Done: rc=%d status=%d\n", rc, mnt_context_get_status(cxt)); + + mnt_free_context(cxt); + mnt_unref_table(tab); + return rc == 0 && mnt_context_get_status(cxt) == 1 ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index 367bc46..f8cedbd 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -101,7 +101,7 @@ check_PROGRAMS += test_mount_context test_mount_context_mount check_PROGRAMS += test_mount_monitor endif -libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) +libmount_tests_cflags = -DTEST_PROGRAM $(libmount_la_CFLAGS) libmount_tests_ldflags = -static libmount_tests_ldadd = libmount.la libblkid.la $(LDADD) $(REALTIME_LIBS) diff --git a/libmount/src/cache.c b/libmount/src/cache.c index 6286aeb..b795634 100644 --- a/libmount/src/cache.c +++ b/libmount/src/cache.c @@ -202,7 +202,7 @@ static int cache_add_entry(struct libmnt_cache *cache, char *key, if (cache->nents == cache->nallocs) { size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ; - e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry)); + e = reallocarray(cache->ents, sz, sizeof(struct mnt_cache_entry)); if (!e) return -ENOMEM; cache->ents = e; @@ -748,7 +748,9 @@ char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache) #ifdef TEST_PROGRAM -static int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[]) +static int test_resolve_path(struct libmnt_test *ts __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) { char line[BUFSIZ]; struct libmnt_cache *cache; @@ -771,7 +773,9 @@ static int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[]) +static int test_resolve_spec(struct libmnt_test *ts __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) { char line[BUFSIZ]; struct libmnt_cache *cache; @@ -794,7 +798,9 @@ static int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_read_tags(struct libmnt_test *ts, int argc, char *argv[]) +static int test_read_tags(struct libmnt_test *ts __attribute__((unused)), + int argc __attribute__((unused)), + char *argv[] __attribute__((unused))) { char line[BUFSIZ]; struct libmnt_cache *cache; diff --git a/libmount/src/context.c b/libmount/src/context.c index 0cd3201..952287a 100644 --- a/libmount/src/context.c +++ b/libmount/src/context.c @@ -107,7 +107,7 @@ void mnt_free_context(struct libmnt_context *cxt) mnt_unref_optlist(cxt->optlist_saved); mnt_unref_optlist(cxt->optlist); - mnt_free_lock(cxt->lock); + mnt_unref_lock(cxt->lock); mnt_free_update(cxt->update); mnt_context_set_target_ns(cxt, NULL); @@ -314,6 +314,8 @@ int mnt_context_reset_status(struct libmnt_context *cxt) if (!cxt) return -EINVAL; + reset_syscall_status(cxt); + cxt->syscall_status = 1; /* means not called yet */ cxt->helper_exec_status = 1; cxt->helper_status = 0; @@ -549,10 +551,10 @@ int mnt_context_enable_onlyonce(struct libmnt_context *cxt, int enable) } /** - * mnt_context_is_lazy: + * mnt_context_is_onlyonce: * @cxt: mount context * - * Returns: 1 if lazy umount is enabled or 0 + * Returns: 1 if only-once mount is enabled or 0 */ int mnt_context_is_onlyonce(struct libmnt_context *cxt) { @@ -2179,6 +2181,10 @@ int mnt_context_prepare_update(struct libmnt_context *cxt) rc = mnt_update_set_fs(cxt->update, flags, NULL, cxt->fs); + if (mnt_update_is_ready(cxt->update)) { + DBG(CXT, ul_debugobj(cxt, "update is ready")); + mnt_update_start(cxt->update); + } return rc < 0 ? rc : 0; } @@ -2207,9 +2213,9 @@ int mnt_context_update_tabs(struct libmnt_context *cxt) && mnt_context_get_helper_status(cxt) == 0 && mnt_context_utab_writable(cxt)) { - if (mnt_update_already_done(cxt->update, cxt->lock)) { + if (mnt_update_already_done(cxt->update)) { DBG(CXT, ul_debugobj(cxt, "don't update: error evaluate or already updated")); - goto end; + goto emit; } } else if (cxt->helper) { DBG(CXT, ul_debugobj(cxt, "don't update: external helper")); @@ -2225,8 +2231,13 @@ int mnt_context_update_tabs(struct libmnt_context *cxt) } rc = mnt_update_table(cxt->update, cxt->lock); +emit: + if (rc == 0 && !mnt_context_within_helper(cxt)) + mnt_update_emit_event(cxt->update); end: + mnt_update_end(cxt->update); + if (!mnt_context_switch_ns(cxt, ns_old)) return -MNT_ERR_NAMESPACE; return rc; @@ -2737,7 +2748,6 @@ int mnt_context_get_excode( return rc; } - /** * mnt_context_init_helper * @cxt: mount context @@ -2773,6 +2783,14 @@ int mnt_context_init_helper(struct libmnt_context *cxt, int action, return rc; } +/* + * libmount used in /sbin/[u]mount.<type> helper + */ +int mnt_context_within_helper(struct libmnt_context *cxt) +{ + return cxt && (cxt->flags & MNT_FL_HELPER); +} + /** * mnt_context_helper_setopt: * @cxt: context @@ -2850,7 +2868,7 @@ static int mnt_context_add_child(struct libmnt_context *cxt, pid_t pid) if (!cxt) return -EINVAL; - pids = realloc(cxt->children, sizeof(pid_t) * cxt->nchildren + 1); + pids = reallocarray(cxt->children, cxt->nchildren + 1, sizeof(pid_t)); if (!pids) return -ENOMEM; @@ -3166,7 +3184,8 @@ struct libmnt_ns *mnt_context_switch_target_ns(struct libmnt_context *cxt) #ifdef TEST_PROGRAM -static int test_search_helper(struct libmnt_test *ts, int argc, char *argv[]) +static int test_search_helper(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_context *cxt; const char *type; @@ -3200,7 +3219,8 @@ static void lock_fallback(void) mnt_unlock_file(lock); } -static int test_mount(struct libmnt_test *ts, int argc, char *argv[]) +static int test_mount(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { int idx = 1, rc = 0; struct libmnt_context *cxt; @@ -3250,7 +3270,8 @@ static int test_mount(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_umount(struct libmnt_test *ts, int argc, char *argv[]) +static int test_umount(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { int idx = 1, rc = 0; struct libmnt_context *cxt; @@ -3305,7 +3326,8 @@ err: return rc; } -static int test_flags(struct libmnt_test *ts, int argc, char *argv[]) +static int test_flags(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { int idx = 1, rc = 0; struct libmnt_context *cxt; @@ -3343,7 +3365,8 @@ static int test_flags(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_cxtsync(struct libmnt_test *ts, int argc, char *argv[]) +static int test_cxtsync(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_context *cxt; struct libmnt_fs *fs; @@ -3387,7 +3410,8 @@ static int test_cxtsync(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_mountall(struct libmnt_test *ts, int argc, char *argv[]) +static int test_mountall(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_context *cxt; struct libmnt_iter *itr; @@ -3405,10 +3429,8 @@ static int test_mountall(struct libmnt_test *ts, int argc, char *argv[]) mnt_context_set_options_pattern(cxt, argv[idx + 1]); idx += 2; } - if (argv[idx] && !strcmp(argv[idx], "-t")) { + if (argv[idx] && !strcmp(argv[idx], "-t")) mnt_context_set_fstype_pattern(cxt, argv[idx + 1]); - idx += 2; - } } while (mnt_context_next_mount(cxt, itr, &fs, &mntrc, &ignored) == 0) { diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c index 41986e7..478a9fd 100644 --- a/libmount/src/context_mount.c +++ b/libmount/src/context_mount.c @@ -1571,6 +1571,12 @@ int mnt_context_get_mount_excode( */ syserr = mnt_context_get_syscall_errno(cxt); + if (buf && cxt->syscall_errmsg) { + snprintf(buf, bufsz, _("%s system call failed: %s"), + cxt->syscall_name ? : "mount", + cxt->syscall_errmsg); + return MNT_EX_FAIL; + } switch(syserr) { case EPERM: @@ -1617,10 +1623,8 @@ int mnt_context_get_mount_excode( return MNT_EX_SUCCESS; if (buf) snprintf(buf, bufsz, _("special device %s does not exist"), src); - } else if (buf) { - errno = syserr; - snprintf(buf, bufsz, _("mount(2) system call failed: %m")); - } + } else + goto generic_error; break; case ENOTDIR: @@ -1633,10 +1637,8 @@ int mnt_context_get_mount_excode( if (buf) snprintf(buf, bufsz, _("special device %s does not exist " "(a path prefix is not a directory)"), src); - } else if (buf) { - errno = syserr; - snprintf(buf, bufsz, _("mount(2) system call failed: %m")); - } + } else + goto generic_error; break; case EINVAL: @@ -1717,10 +1719,8 @@ int mnt_context_get_mount_excode( snprintf(buf, bufsz, _("cannot remount %s read-write, is write-protected"), src); else if (mflags & MS_BIND) snprintf(buf, bufsz, _("bind %s failed"), src); - else { - errno = syserr; - snprintf(buf, bufsz, _("mount(2) system call failed: %m")); - } + else + goto generic_error; break; case ENOMEDIUM: @@ -1740,9 +1740,11 @@ int mnt_context_get_mount_excode( /* fallthrough */ default: + generic_error: if (buf) { errno = syserr; - snprintf(buf, bufsz, _("mount(2) system call failed: %m")); + snprintf(buf, bufsz, _("%s system call failed: %m"), + cxt->syscall_name ? : "mount"); } break; } @@ -1752,7 +1754,8 @@ int mnt_context_get_mount_excode( #ifdef TEST_PROGRAM -static int test_perms(struct libmnt_test *ts, int argc, char *argv[]) +static int test_perms(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_context *cxt; struct libmnt_optlist *ls; @@ -1795,7 +1798,8 @@ static int test_perms(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_fixopts(struct libmnt_test *ts, int argc, char *argv[]) +static int test_fixopts(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_context *cxt; struct libmnt_optlist *ls; diff --git a/libmount/src/context_umount.c b/libmount/src/context_umount.c index 26394d5..79b6237 100644 --- a/libmount/src/context_umount.c +++ b/libmount/src/context_umount.c @@ -270,7 +270,7 @@ static int lookup_umount_fs_by_statfs(struct libmnt_context *cxt, const char *tg */ if (mnt_context_is_restricted(cxt) || *tgt != '/' - || (cxt->flags & MNT_FL_HELPER) + || mnt_context_within_helper(cxt) || mnt_context_is_force(cxt) || mnt_context_is_lazy(cxt) || mnt_context_is_nocanonicalize(cxt) diff --git a/libmount/src/hook_loopdev.c b/libmount/src/hook_loopdev.c index 8c8f7f2..597b933 100644 --- a/libmount/src/hook_loopdev.c +++ b/libmount/src/hook_loopdev.c @@ -140,10 +140,6 @@ static int setup_loopdev(struct libmnt_context *cxt, DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file)); - ol = mnt_context_get_optlist(cxt); - if (!ol) - return -ENOMEM; - if (mnt_optlist_is_rdonly(ol)) { DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag")); lo_flags |= LO_FLAGS_READ_ONLY; @@ -356,15 +352,19 @@ success: */ mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux); - /* we have to keep the device open until mount(1), - * otherwise it will be auto-cleared by kernel + /* + * We have to keep the device open until mount(1), otherwise it + * will be auto-cleared by kernel. However we don't want to + * keep writeable fd as kernel wants to block all writers to + * the device being mounted (in the more hardened + * configurations). So grab read-only fd instead. */ - hd->loopdev_fd = loopcxt_get_fd(&lc); + hd->loopdev_fd = open(lc.device, O_RDONLY | O_CLOEXEC); if (hd->loopdev_fd < 0) { - DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD")); + DBG(LOOP, + ul_debugobj(cxt, "failed to reopen loopdev FD")); rc = -errno; - } else - loopcxt_set_fd(&lc, -1, 0); + } } done: loopcxt_deinit(&lc); diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c index dc3dfa7..f0cc381 100644 --- a/libmount/src/hook_mount.c +++ b/libmount/src/hook_mount.c @@ -65,6 +65,42 @@ static void close_sysapi_fds(struct libmnt_sysapi *api) api->fd_tree = api->fd_fs = -1; } +static void save_fd_messages(struct libmnt_context *cxt, int fd) +{ + uint8_t buf[BUFSIZ]; + int rc; + + free(cxt->syscall_errmsg); + cxt->syscall_errmsg = NULL; + + while ((rc = read(fd, buf, sizeof(buf))) != -1) { + if (rc > 0 && buf[rc - 1] == '\n') + buf[rc - 1] = '\0'; + DBG(CXT, ul_debug("message from kernel: \"%*s\"", rc, buf)); + + if (rc < 3 || strncmp((char *) buf, "e ", 2) != 0) + continue; + if (cxt->syscall_errmsg) + strappend(&cxt->syscall_errmsg, "; "); + + strappend(&cxt->syscall_errmsg, ((char *) buf) + 2); + } +} + +static void hookset_set_syscall_status(struct libmnt_context *cxt, + const char *name, int x) +{ + struct libmnt_sysapi *api; + + set_syscall_status(cxt, name, x); + + if (!x) { + api = get_sysapi(cxt); + if (api && api->fd_fs >= 0) + save_fd_messages(cxt, api->fd_fs); + } +} + /* * This hookset uses 'struct libmnt_sysapi' (mountP.h) as hookset data. */ @@ -122,26 +158,33 @@ static inline int fsconfig_set_value( const char *name, const char *value) { int rc; - char *p = NULL; + char *s = NULL; + /* "\," is a way to use comma in values, let's remove \ escape */ if (value && strstr(value, "\\,")) { - p = strdup(value); - if (!p) - return -EINVAL; + char *x, *p; - strrem(p, '\\'); - value = p; + s = strdup(value); + if (!s) + return -EINVAL; + for (x = p = s; *x; p++, x++) { + if (*x == '\\' && *(x + 1) == ',') + x++; + *p = *x; + } + *p = '\0'; + value = s; } DBG(HOOK, ul_debugobj(hs, " fsconfig(name=\"%s\" value=\"%s\")", name, value ? : "")); if (value) { rc = fsconfig(fd, FSCONFIG_SET_STRING, name, value, 0); - free(p); + free(s); } else rc = fsconfig(fd, FSCONFIG_SET_FLAG, name, NULL, 0); - set_syscall_status(cxt, "fsconfig", rc == 0); + hookset_set_syscall_status(cxt, "fsconfig", rc == 0); return rc; } @@ -181,6 +224,9 @@ static int configure_superblock(struct libmnt_context *cxt, /* Ignore VFS flags, userspace and external options */ continue; + if (!value && mnt_opt_is_sepnodata(opt)) + value = ""; /* force use the value as string */ + rc = fsconfig_set_value(cxt, hs, fd, name, value); if (rc != 0) goto done; @@ -206,7 +252,7 @@ static int open_fs_configuration_context(struct libmnt_context *cxt, DBG(HOOK, ul_debug(" fsopen(%s)", type)); api->fd_fs = fsopen(type, FSOPEN_CLOEXEC); - set_syscall_status(cxt, "fsopen", api->fd_fs >= 0); + hookset_set_syscall_status(cxt, "fsopen", api->fd_fs >= 0); if (api->fd_fs < 0) return -errno; api->is_new_fs = 1; @@ -245,7 +291,7 @@ static int open_mount_tree(struct libmnt_context *cxt, const char *path, unsigne oflg & OPEN_TREE_CLONE ? " clone" : "", oflg & AT_RECURSIVE ? " recursive" : "")); fd = open_tree(AT_FDCWD, path, oflg); - set_syscall_status(cxt, "open_tree", fd >= 0); + hookset_set_syscall_status(cxt, "open_tree", fd >= 0); return fd; } @@ -285,19 +331,19 @@ static int hook_create_mount(struct libmnt_context *cxt, DBG(HOOK, ul_debugobj(hs, "init FS")); rc = fsconfig(api->fd_fs, FSCONFIG_SET_STRING, "source", src, 0); - set_syscall_status(cxt, "fsconfig", rc == 0); + hookset_set_syscall_status(cxt, "fsconfig", rc == 0); if (!rc) rc = configure_superblock(cxt, hs, api->fd_fs, 0); if (!rc) { DBG(HOOK, ul_debugobj(hs, "create FS")); rc = fsconfig(api->fd_fs, FSCONFIG_CMD_CREATE, NULL, NULL, 0); - set_syscall_status(cxt, "fsconfig", rc == 0); + hookset_set_syscall_status(cxt, "fsconfig", rc == 0); } if (!rc) { api->fd_tree = fsmount(api->fd_fs, FSMOUNT_CLOEXEC, 0); - set_syscall_status(cxt, "fsmount", api->fd_tree >= 0); + hookset_set_syscall_status(cxt, "fsmount", api->fd_tree >= 0); if (api->fd_tree < 0) rc = -errno; } @@ -346,7 +392,7 @@ static int hook_reconfigure_mount(struct libmnt_context *cxt, if (api->fd_fs < 0) { api->fd_fs = fspick(api->fd_tree, "", FSPICK_EMPTY_PATH | FSPICK_NO_AUTOMOUNT); - set_syscall_status(cxt, "fspick", api->fd_fs >= 0); + hookset_set_syscall_status(cxt, "fspick", api->fd_fs >= 0); if (api->fd_fs < 0) return -errno; } @@ -355,7 +401,7 @@ static int hook_reconfigure_mount(struct libmnt_context *cxt, if (!rc) { DBG(HOOK, ul_debugobj(hs, "re-configurate FS")); rc = fsconfig(api->fd_fs, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0); - set_syscall_status(cxt, "fsconfig", rc == 0); + hookset_set_syscall_status(cxt, "fsconfig", rc == 0); } DBG(HOOK, ul_debugobj(hs, "reconf FS done [rc=%d]", rc)); @@ -393,7 +439,7 @@ static int set_vfsflags(struct libmnt_context *cxt, errno = 0; rc = mount_setattr(api->fd_tree, "", callflags, &attr, sizeof(attr)); - set_syscall_status(cxt, "mount_setattr", rc == 0); + hookset_set_syscall_status(cxt, "mount_setattr", rc == 0); if (rc && errno == EINVAL) return -MNT_ERR_APPLYFLAGS; @@ -485,7 +531,7 @@ static int hook_set_propagation(struct libmnt_context *cxt, (uint64_t) attr.propagation)); rc = mount_setattr(api->fd_tree, "", flgs, &attr, sizeof(attr)); - set_syscall_status(cxt, "mount_setattr", rc == 0); + hookset_set_syscall_status(cxt, "mount_setattr", rc == 0); if (rc && errno == EINVAL) return -MNT_ERR_APPLYFLAGS; @@ -527,7 +573,7 @@ static int hook_attach_target(struct libmnt_context *cxt, } rc = move_mount(api->fd_tree, "", AT_FDCWD, target, MOVE_MOUNT_F_EMPTY_PATH); - set_syscall_status(cxt, "move_mount", rc == 0); + hookset_set_syscall_status(cxt, "move_mount", rc == 0); return rc == 0 ? 0 : -errno; } @@ -613,7 +659,7 @@ static int init_sysapi(struct libmnt_context *cxt, else if (!fsopen_is_supported()) { errno = ENOSYS; rc = -errno; - set_syscall_status(cxt, "fsopen", rc == 0); + hookset_set_syscall_status(cxt, "fsopen", rc == 0); } if (rc < 0) goto fail; diff --git a/libmount/src/hook_veritydev.c b/libmount/src/hook_veritydev.c index f91778a..6a9e644 100644 --- a/libmount/src/hook_veritydev.c +++ b/libmount/src/hook_veritydev.c @@ -349,25 +349,8 @@ static int setup_veritydev( struct libmnt_context *cxt, backing_file = mnt_fs_get_srcpath(cxt->fs); if (!backing_file) return -EINVAL; - else { - /* To avoid clashes, prefix libmnt_ to all mapper devices */ - char *p, *path = strdup(backing_file); - if (!path) - return -ENOMEM; - - p = stripoff_last_component(path); - if (p) - mapper_device = calloc(strlen(p) + sizeof("libmnt_"), sizeof(char)); - if (mapper_device) { - strcat(mapper_device, "libmnt_"); - strcat(mapper_device, p); - } - free(path); - if (!mapper_device) - return -ENOMEM; - } - DBG(HOOK, ul_debugobj(hs, "verity: setup for %s [%s]", backing_file, mapper_device)); + DBG(HOOK, ul_debugobj(hs, "verity: setup for %s", backing_file)); /* verity.hashdevice= */ if (!rc && (opt = mnt_optlist_get_opt(ol, MNT_MS_HASH_DEVICE, cxt->map_userspace))) @@ -467,6 +450,13 @@ static int setup_veritydev( struct libmnt_context *cxt, rc = -EINVAL; } + /* To avoid clashes, use the roothash as the device name. This allows us to reuse already open devices, saving + * a lot of time and resources when there are duplicated mounts. If the roothash is the same, then the volumes + * are also guaranteed to be identical. This is what systemd also does, so we can deduplicate across the whole + * system. */ + if (asprintf(&mapper_device, "%s-verity", root_hash) < 0) + rc = -ENOMEM; + if (!rc) rc = verity_call( crypt_init_data_device(&crypt_dev, hash_device, backing_file) ); if (rc) @@ -506,7 +496,9 @@ static int setup_veritydev( struct libmnt_context *cxt, * If the mapper device already exists, and if libcryptsetup supports it, get the root * hash associated with the existing one and compare it with the parameter passed by * the user. If they match, then we can be sure the user intended to mount the exact - * same device, and simply reuse it and return success. + * same device, and simply reuse it and return success. Although we use the roothash + * as the device mapper name, and root privileges are required to open them, better be + * safe than sorry, so double check that the actual root hash used matches. * The kernel does the refcounting for us. * If libcryptsetup does not support getting the root hash out of an existing device, * then return an error and tell the user that the device is already in use. @@ -562,15 +554,10 @@ static int setup_veritydev( struct libmnt_context *cxt, } if (!rc) { - hsd->devname = calloc(strlen(mapper_device) - + sizeof(_PATH_DEV_MAPPER) + 2, sizeof(char)); - if (!hsd->devname) + if (asprintf(&hsd->devname, _PATH_DEV_MAPPER "/%s", mapper_device) == -1) rc = -ENOMEM; - else { - strcat(hsd->devname, _PATH_DEV_MAPPER "/"); - strcat(hsd->devname, mapper_device); + else rc = mnt_fs_set_source(cxt->fs, hsd->devname); - } } done: diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 06c2704..d893c26 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -444,6 +444,8 @@ extern const struct libmnt_optmap *mnt_get_builtin_optmap(int id); /* lock.c */ extern struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id) __ul_attribute__((warn_unused_result)); +extern void mnt_ref_lock(struct libmnt_lock *ml); +extern void mnt_unref_lock(struct libmnt_lock *ml); extern void mnt_free_lock(struct libmnt_lock *ml); extern void mnt_unlock_file(struct libmnt_lock *ml); @@ -697,6 +699,8 @@ extern int mnt_monitor_enable_kernel(struct libmnt_monitor *mn, int enable); extern int mnt_monitor_enable_userspace(struct libmnt_monitor *mn, int enable, const char *filename); +extern int mnt_monitor_veil_kernel(struct libmnt_monitor *mn, int enable); + extern int mnt_monitor_get_fd(struct libmnt_monitor *mn); extern int mnt_monitor_close_fd(struct libmnt_monitor *mn); extern int mnt_monitor_wait(struct libmnt_monitor *mn, int timeout); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index 715bb5c..2b6b12d 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -370,8 +370,14 @@ MOUNT_2_38 { MOUNT_2_39 { mnt_cache_set_sbprobe; mnt_context_enable_onlyonce; - mnt_context_is_lazy; + mnt_context_is_onlyonce; mnt_context_enable_noautofs; mnt_table_enable_noautofs; mnt_table_is_noautofs; } MOUNT_2_38; + +MOUNT_2_40 { + mnt_ref_lock; + mnt_unref_lock; + mnt_monitor_veil_kernel; +} MOUNT_2_39; diff --git a/libmount/src/lock.c b/libmount/src/lock.c index 4835406..8aca8a7 100644 --- a/libmount/src/lock.c +++ b/libmount/src/lock.c @@ -36,6 +36,7 @@ * lock handler */ struct libmnt_lock { + int refcount; /* reference counter */ char *lockfile; /* path to lock file (e.g. /etc/mtab~) */ int lockfile_fd; /* lock file descriptor */ @@ -73,6 +74,7 @@ struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id __attribute__((_ if (!ml) goto err; + ml->refcount = 1; ml->lockfile_fd = -1; ml->lockfile = lo; @@ -89,18 +91,57 @@ err: * mnt_free_lock: * @ml: struct libmnt_lock handler * - * Deallocates mnt_lock. + * Deallocates libmnt_lock. This function does not care about reference count. Don't + * use this function directly -- it's better to use mnt_unref_lock(). + * + * The reference counting is supported since util-linux v2.40. */ void mnt_free_lock(struct libmnt_lock *ml) { if (!ml) return; - DBG(LOCKS, ul_debugobj(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : "")); + + DBG(LOCKS, ul_debugobj(ml, "free%s [refcount=%d]", + ml->locked ? " !!! LOCKED !!!" : "", + ml->refcount)); free(ml->lockfile); free(ml); } /** + * mnt_ref_lock: + * @ml: lock pointer + * + * Increments reference counter. + * + * Since: 2.40 + */ +void mnt_ref_lock(struct libmnt_lock *ml) +{ + if (ml) { + ml->refcount++; + /*DBG(FS, ul_debugobj(fs, "ref=%d", ml->refcount));*/ + } +} + +/** + * mnt_unref_lock: + * @ml: lock pointer + * + * De-increments reference counter, on zero the @ml is automatically + * deallocated by mnt_free_lock). + */ +void mnt_unref_lock(struct libmnt_lock *ml) +{ + if (ml) { + ml->refcount--; + /*DBG(FS, ul_debugobj(fs, "unref=%d", ml->refcount));*/ + if (ml->refcount <= 0) + mnt_free_lock(ml); + } +} + +/** * mnt_lock_block_signals: * @ml: struct libmnt_lock handler * @enable: TRUE/FALSE @@ -146,7 +187,7 @@ static int lock_simplelock(struct libmnt_lock *ml) const char *lfile; int rc; struct stat sb; - const mode_t lock_mask = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; + const mode_t lock_mask = S_IRUSR|S_IWUSR; assert(ml); @@ -161,8 +202,7 @@ static int lock_simplelock(struct libmnt_lock *ml) sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask); } - ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, - S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); + ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, lock_mask); if (ml->lockfile_fd < 0) { rc = -errno; goto err; @@ -287,7 +327,7 @@ static void clean_lock(void) if (!lock) return; mnt_unlock_file(lock); - mnt_free_lock(lock); + mnt_unref_lock(lock); } static void __attribute__((__noreturn__)) sig_handler(int sig) @@ -295,7 +335,8 @@ static void __attribute__((__noreturn__)) sig_handler(int sig) errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig)); } -static int test_lock(struct libmnt_test *ts, int argc, char *argv[]) +static int test_lock(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { time_t synctime = 0; unsigned int usecs; @@ -367,7 +408,7 @@ static int test_lock(struct libmnt_test *ts, int argc, char *argv[]) increment_data(datafile, verbose, l); mnt_unlock_file(lock); - mnt_free_lock(lock); + mnt_unref_lock(lock); lock = NULL; /* The mount command usually finishes after a mtab update. We diff --git a/libmount/src/monitor.c b/libmount/src/monitor.c index f99751e..941f5d5 100644 --- a/libmount/src/monitor.c +++ b/libmount/src/monitor.c @@ -64,6 +64,8 @@ struct libmnt_monitor { int fd; /* public monitor file descriptor */ struct list_head ents; + + unsigned int kernel_veiled: 1; }; struct monitor_opers { @@ -226,18 +228,16 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd) assert(me->path); /* - * libmount uses rename(2) to atomically update utab, monitor - * rename changes is too tricky. It seems better to monitor utab - * lockfile close. + * libmount uses utab.event file to monitor and control utab updates */ - if (asprintf(&filename, "%s.lock", me->path) <= 0) { - rc = -errno; + if (asprintf(&filename, "%s.event", me->path) <= 0) { + rc = -ENOMEM; goto done; } - /* try lock file if already exists */ + /* try event file if already exists */ errno = 0; - wd = inotify_add_watch(me->fd, filename, IN_CLOSE_NOWRITE); + wd = inotify_add_watch(me->fd, filename, IN_CLOSE_WRITE); if (wd >= 0) { DBG(MONITOR, ul_debug(" added inotify watch for %s [fd=%d]", filename, wd)); rc = 0; @@ -256,7 +256,7 @@ static int userspace_add_watch(struct monitor_entry *me, int *final, int *fd) if (!*filename) break; - /* try directory where is the lock file */ + /* try directory where is the event file */ errno = 0; wd = inotify_add_watch(me->fd, filename, IN_CREATE|IN_ISDIR); if (wd >= 0) { @@ -339,10 +339,10 @@ static int userspace_event_verify(struct libmnt_monitor *mn, e = (const struct inotify_event *) p; DBG(MONITOR, ul_debugobj(mn, " inotify event 0x%x [%s]\n", e->mask, e->len ? e->name : "")); - if (e->mask & IN_CLOSE_NOWRITE) + if (e->mask & IN_CLOSE_WRITE) status = 1; else { - /* event on lock file */ + /* add watch for the event file */ userspace_add_watch(me, &status, &fd); if (fd != e->wd) { @@ -473,12 +473,28 @@ err: return rc; } +static int kernel_event_verify(struct libmnt_monitor *mn, + struct monitor_entry *me) +{ + int status = 1; + + if (!mn || !me || me->fd < 0) + return 0; + + if (mn->kernel_veiled && access(MNT_PATH_UTAB ".act", F_OK) == 0) { + status = 0; + DBG(MONITOR, ul_debugobj(mn, "kernel event veiled")); + } + return status; +} + /* * kernel monitor operations */ static const struct monitor_opers kernel_opers = { .op_get_fd = kernel_monitor_get_fd, .op_close_fd = kernel_monitor_close_fd, + .op_event_verify = kernel_event_verify }; /** @@ -547,6 +563,28 @@ err: return rc; } +/** + * mnt_monitor_veil_kernel: + * @mn: monitor instance + * @enable: 1 or 0 + * + * Force monitor to ignore kernel events if the same mount/umount operation + * will generate an userspace event later. The kernel-only mount operation will + * be not affected. + * + * Return: 0 on success and <0 on error. + * + * Since: 2.40 + */ +int mnt_monitor_veil_kernel(struct libmnt_monitor *mn, int enable) +{ + if (!mn) + return -EINVAL; + + mn->kernel_veiled = enable ? 1 : 0; + return 0; +} + /* * Add/Remove monitor entry to/from monitor epoll. */ @@ -854,6 +892,8 @@ static struct libmnt_monitor *create_test_monitor(int argc, char *argv[]) warn("failed to initialize kernel monitor"); goto err; } + } else if (strcmp(argv[i], "veil") == 0) { + mnt_monitor_veil_kernel(mn, 1); } } if (i == 1) { @@ -870,7 +910,8 @@ err: /* * create a monitor and add the monitor fd to epoll */ -static int __test_epoll(struct libmnt_test *ts, int argc, char *argv[], int cleanup) +static int __test_epoll(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[], int cleanup) { int fd, efd = -1, rc = -1; struct epoll_event ev; @@ -900,12 +941,14 @@ static int __test_epoll(struct libmnt_test *ts, int argc, char *argv[], int clea goto done; } - printf("waiting for changes...\n"); do { const char *filename = NULL; struct epoll_event events[1]; - int n = epoll_wait(efd, events, 1, -1); + int n; + + printf("waiting for changes...\n"); + n = epoll_wait(efd, events, 1, -1); if (n < 0) { rc = -errno; warn("polling error"); @@ -947,7 +990,8 @@ static int test_epoll_cleanup(struct libmnt_test *ts, int argc, char *argv[]) /* * create a monitor and wait for a change */ -static int test_wait(struct libmnt_test *ts, int argc, char *argv[]) +static int test_wait(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *filename; struct libmnt_monitor *mn = create_test_monitor(argc, argv); @@ -962,6 +1006,7 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[]) while (mnt_monitor_next_change(mn, &filename, NULL) == 0) printf(" %s: change detected\n", filename); + printf("waiting for changes...\n"); } mnt_unref_monitor(mn); return 0; @@ -970,9 +1015,9 @@ static int test_wait(struct libmnt_test *ts, int argc, char *argv[]) int main(int argc, char *argv[]) { struct libmnt_test tss[] = { - { "--epoll", test_epoll, "<userspace kernel ...> monitor in epoll" }, - { "--epoll-clean", test_epoll_cleanup, "<userspace kernel ...> monitor in epoll and clean events" }, - { "--wait", test_wait, "<userspace kernel ...> monitor wait function" }, + { "--epoll", test_epoll, "<userspace kernel veil ...> monitor in epoll" }, + { "--epoll-clean", test_epoll_cleanup, "<userspace kernel veil ...> monitor in epoll and clean events" }, + { "--wait", test_wait, "<userspace kernel veil ...> monitor wait function" }, { NULL } }; diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index 339e276..fcc40bf 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -432,6 +432,7 @@ struct libmnt_context int syscall_status; /* 1: not called yet, 0: success, <0: -errno */ const char *syscall_name; /* failed syscall name */ + char *syscall_errmsg; /* message from kernel */ struct libmnt_ns ns_orig; /* original namespace */ struct libmnt_ns ns_tgt; /* target namespace */ @@ -479,22 +480,27 @@ struct libmnt_context /* Flags usable with MS_BIND|MS_REMOUNT */ #define MNT_BIND_SETTABLE (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NOATIME|MS_NODIRATIME|MS_RELATIME|MS_RDONLY|MS_NOSYMFOLLOW) -#define set_syscall_status(_cxt, _name, _x) __extension__ ({ \ - if (!(_x)) { \ - DBG(CXT, ul_debug("syscall '%s' [%m]", _name)); \ - (_cxt)->syscall_status = -errno; \ - (_cxt)->syscall_name = (_name); \ - } else { \ - DBG(CXT, ul_debug("syscall '%s' [success]", _name)); \ - (_cxt)->syscall_status = 0; \ - } \ - }) - -#define reset_syscall_status(_cxt) __extension__ ({ \ - DBG(CXT, ul_debug("reset syscall status")); \ - (_cxt)->syscall_status = 0; \ - (_cxt)->syscall_name = NULL; \ - }) +static inline void set_syscall_status(struct libmnt_context *cxt, const char *name, int x) +{ + if (!x) { + DBG(CXT, ul_debug("syscall '%s' [%m]", name)); + cxt->syscall_status = -errno; + cxt->syscall_name = name; + } else { + DBG(CXT, ul_debug("syscall '%s' [success]", name)); + cxt->syscall_status = 0; + } +} + +static inline void reset_syscall_status(struct libmnt_context *cxt) +{ + DBG(CXT, ul_debug("reset syscall status")); + cxt->syscall_status = 0; + cxt->syscall_name = NULL; + + free(cxt->syscall_errmsg); + cxt->syscall_errmsg = NULL; +} /* optmap.c */ extern const struct libmnt_optmap *mnt_optmap_get_entry( @@ -506,6 +512,7 @@ extern const struct libmnt_optmap *mnt_optmap_get_entry( /* optstr.c */ extern int mnt_optstr_remove_option_at(char **optstr, char *begin, char *end); +extern int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing); extern int mnt_buffer_append_option(struct ul_buffer *buf, const char *name, size_t namesz, @@ -601,6 +608,7 @@ extern int mnt_opt_set_value(struct libmnt_opt *opt, const char *str); extern int mnt_opt_set_u64value(struct libmnt_opt *opt, uint64_t num); extern int mnt_opt_set_quoted_value(struct libmnt_opt *opt, const char *str); extern int mnt_opt_is_external(struct libmnt_opt *opt); +extern int mnt_opt_is_sepnodata(struct libmnt_opt *opt); /* fs.c */ extern int mnt_fs_follow_optlist(struct libmnt_fs *fs, struct libmnt_optlist *ol); @@ -617,6 +625,8 @@ extern struct libmnt_context *mnt_copy_context(struct libmnt_context *o); extern int mnt_context_utab_writable(struct libmnt_context *cxt); extern const char *mnt_context_get_writable_tabpath(struct libmnt_context *cxt); +extern int mnt_context_within_helper(struct libmnt_context *cxt); + extern int mnt_context_get_mountinfo(struct libmnt_context *cxt, struct libmnt_table **tb); extern int mnt_context_get_mountinfo_for_target(struct libmnt_context *cxt, struct libmnt_table **mountinfo, const char *tgt); @@ -658,9 +668,11 @@ extern int mnt_context_apply_fs(struct libmnt_context *cxt, struct libmnt_fs *fs extern struct libmnt_optlist *mnt_context_get_optlist(struct libmnt_context *cxt); /* tab_update.c */ +extern int mnt_update_emit_event(struct libmnt_update *upd); extern int mnt_update_set_filename(struct libmnt_update *upd, const char *filename); -extern int mnt_update_already_done(struct libmnt_update *upd, - struct libmnt_lock *lc); +extern int mnt_update_already_done(struct libmnt_update *upd); +extern int mnt_update_start(struct libmnt_update *upd); +extern int mnt_update_end(struct libmnt_update *upd); #if __linux__ /* btrfs.c */ diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c index 0702ada..476acfd 100644 --- a/libmount/src/optlist.c +++ b/libmount/src/optlist.c @@ -46,6 +46,7 @@ struct libmnt_opt { unsigned int external : 1, /* visible for external helpers only */ recursive : 1, /* recursive flag */ + sepnodata : 1, /* value separator, but without data ("name=") */ is_linux : 1, /* defined in ls->linux_map (VFS attr) */ quoted : 1; /* name="value" */ }; @@ -438,6 +439,10 @@ static struct libmnt_opt *optlist_new_opt(struct libmnt_optlist *ls, opt->value = strndup(value, valsz); if (!opt->value) goto fail; + + } else if (value) { + /* separator specified, but empty value ("name=") */ + opt->sepnodata = 1; } if (namesz) { opt->name = strndup(name, namesz); @@ -957,7 +962,8 @@ int mnt_optlist_strdup_optstr(struct libmnt_optlist *ls, char **optstr, continue; rc = mnt_buffer_append_option(&buf, opt->name, strlen(opt->name), - opt->value, + opt->value ? opt->value : + opt->sepnodata ? "" : NULL, opt->value ? strlen(opt->value) : 0, opt->quoted); if (rc) @@ -1043,6 +1049,7 @@ struct libmnt_optlist *mnt_copy_optlist(struct libmnt_optlist *ls) no->src = opt->src; no->external = opt->external; no->quoted = opt->quoted; + no->sepnodata = opt->sepnodata; } } @@ -1184,6 +1191,11 @@ int mnt_opt_is_external(struct libmnt_opt *opt) return opt && opt->external ? 1 : 0; } +int mnt_opt_is_sepnodata(struct libmnt_opt *opt) +{ + return opt->sepnodata; +} + #ifdef TEST_PROGRAM @@ -1241,7 +1253,8 @@ static inline unsigned long str2flg(const char *str) return (unsigned long) strtox64_or_err(str, "connt convert string to flags"); } -static int test_append_str(struct libmnt_test *ts, int argc, char *argv[]) +static int test_append_str(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; int rc; @@ -1257,7 +1270,8 @@ static int test_append_str(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[]) +static int test_prepend_str(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; int rc; @@ -1273,7 +1287,8 @@ static int test_prepend_str(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_set_str(struct libmnt_test *ts, int argc, char *argv[]) +static int test_set_str(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; int rc; @@ -1289,7 +1304,8 @@ static int test_set_str(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[]) +static int test_append_flg(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; int rc; @@ -1305,7 +1321,8 @@ static int test_append_flg(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[]) +static int test_set_flg(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; int rc; @@ -1321,7 +1338,8 @@ static int test_set_flg(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_get_str(struct libmnt_test *ts, int argc, char *argv[]) +static int test_get_str(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; const struct libmnt_optmap *map; @@ -1380,7 +1398,8 @@ done: return rc; } -static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[]) +static int test_get_flg(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_optlist *ol; unsigned long flags = 0; @@ -1397,6 +1416,36 @@ static int test_get_flg(struct libmnt_test *ts, int argc, char *argv[]) return rc; } +static int test_split(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) +{ + struct libmnt_optlist *ol; + int rc; + struct libmnt_iter itr; + struct libmnt_opt *opt; + const char *name, *value; + + if (argc != 2) + return -EINVAL; + rc = mk_optlist(&ol, argv[1]); + if (rc) + goto done; + + mnt_reset_iter(&itr, MNT_ITER_FORWARD); + + while (mnt_optlist_next_opt(ol, &itr, &opt) == 0) { + name = mnt_opt_get_name(opt); + value = mnt_opt_get_value(opt); + + printf("%s = %s\n", name, value ?: "(null)"); + } + +done: + mnt_unref_optlist(ol); + return rc; +} + + int main(int argc, char *argv[]) { struct libmnt_test tss[] = { @@ -1407,6 +1456,7 @@ int main(int argc, char *argv[]) { "--set-flg", test_set_flg, "<list> <flg> linux|user set to the list" }, { "--get-str", test_get_str, "<list> [linux|user] all options in string" }, { "--get-flg", test_get_flg, "<list> linux|user all options by flags" }, + { "--split", test_split, "<list> split options into key-value pairs"}, { NULL } }; diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c index e86b962..7ebc295 100644 --- a/libmount/src/optstr.c +++ b/libmount/src/optstr.c @@ -43,13 +43,16 @@ struct libmnt_optloc { * Locates the first option that matches @name. The @end is set to the * char behind the option (it means ',' or \0). * + * @ol is optional. + * * Returns negative number on parse error, 1 when not found and 0 on success. */ -static int mnt_optstr_locate_option(char *optstr, const char *name, +static int mnt_optstr_locate_option(char *optstr, + const char *name, size_t namesz, struct libmnt_optloc *ol) { char *n; - size_t namesz, nsz; + size_t nsz; int rc; if (!optstr) @@ -57,27 +60,30 @@ static int mnt_optstr_locate_option(char *optstr, const char *name, assert(name); - namesz = strlen(name); + if (!namesz) + namesz = strlen(name); if (!namesz) return 1; do { rc = ul_optstr_next(&optstr, &n, &nsz, - &ol->value, &ol->valsz); + ol ? &ol->value : NULL, + ol ? &ol->valsz : NULL); if (rc) break; if (namesz == nsz && strncmp(n, name, nsz) == 0) { - ol->begin = n; - ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr; - ol->namesz = nsz; + if (ol) { + ol->begin = n; + ol->end = *(optstr - 1) == ',' ? optstr - 1 : optstr; + ol->namesz = nsz; + } return 0; } } while(1); return rc; } - /** * mnt_optstr_next_option: * @optstr: option string, returns the position of the next option @@ -223,7 +229,7 @@ int mnt_optstr_get_option(const char *optstr, const char *name, if (!optstr || !name) return -EINVAL; - rc = mnt_optstr_locate_option((char *) optstr, name, &ol); + rc = mnt_optstr_locate_option((char *) optstr, name, 0, &ol); if (!rc) { if (value) *value = ol.value; @@ -255,7 +261,7 @@ int mnt_optstr_deduplicate_option(char **optstr, const char *name) do { struct libmnt_optloc ol = MNT_INIT_OPTLOC; - rc = mnt_optstr_locate_option(opt, name, &ol); + rc = mnt_optstr_locate_option(opt, name, 0, &ol); if (!rc) { if (begin) { /* remove the previous instance */ @@ -368,7 +374,7 @@ int mnt_optstr_set_option(char **optstr, const char *name, const char *value) return -EINVAL; if (*optstr) - rc = mnt_optstr_locate_option(*optstr, name, &ol); + rc = mnt_optstr_locate_option(*optstr, name, 0, &ol); if (rc < 0) return rc; /* parse error */ if (rc == 1) @@ -411,7 +417,7 @@ int mnt_optstr_remove_option(char **optstr, const char *name) if (!optstr || !name) return -EINVAL; - rc = mnt_optstr_locate_option(*optstr, name, &ol); + rc = mnt_optstr_locate_option(*optstr, name, 0, &ol); if (rc != 0) return rc; @@ -571,6 +577,51 @@ int mnt_optstr_get_options(const char *optstr, char **subset, return rc; } +/* + * @optstr: string with comma separated list of options + * @wanted: options expected in @optstr + * @missing: returns options from @wanted which missing in @optstr (optional) + * + * Retursn: <0 on error, 0 on missing options, 1 if nothing is missing + */ +int mnt_optstr_get_missing(const char *optstr, const char *wanted, char **missing) +{ + char *name, *val, *str = (char *) wanted; + size_t namesz = 0, valsz = 0; + struct ul_buffer buf = UL_INIT_BUFFER; + int rc = 0; + + if (!wanted) + return 1; + if (missing) { + /* caller wants data, prepare buffer */ + ul_buffer_set_chunksize(&buf, strlen(wanted) + 3); /* to call realloc() only once */ + *missing = NULL; + } + + while (!mnt_optstr_next_option(&str, &name, &namesz, &val, &valsz)) { + + rc = mnt_optstr_locate_option((char *) optstr, name, namesz, NULL); + if (rc == 1) { /* not found */ + if (!missing) + return 0; + rc = mnt_buffer_append_option(&buf, name, namesz, val, valsz, 0); + } + if (rc < 0) + break; + rc = 0; + } + + if (!rc && missing) { + if (ul_buffer_is_empty(&buf)) + rc = 1; + else + *missing = ul_buffer_get_data(&buf, NULL, NULL); + } else + ul_buffer_free_data(&buf); + + return rc; +} /** * mnt_optstr_get_flags: @@ -910,7 +961,8 @@ int mnt_match_options(const char *optstr, const char *pattern) } #ifdef TEST_PROGRAM -static int test_append(struct libmnt_test *ts, int argc, char *argv[]) +static int test_append(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *value = NULL, *name; char *optstr; @@ -933,7 +985,8 @@ static int test_append(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_prepend(struct libmnt_test *ts, int argc, char *argv[]) +static int test_prepend(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *value = NULL, *name; char *optstr; @@ -956,7 +1009,8 @@ static int test_prepend(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_split(struct libmnt_test *ts, int argc, char *argv[]) +static int test_split(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { char *optstr, *user = NULL, *fs = NULL, *vfs = NULL; int rc; @@ -982,7 +1036,8 @@ static int test_split(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_flags(struct libmnt_test *ts, int argc, char *argv[]) +static int test_flags(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { char *optstr; int rc; @@ -1010,7 +1065,8 @@ static int test_flags(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_apply(struct libmnt_test *ts, int argc, char *argv[]) +static int test_apply(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { char *optstr; int rc, map; @@ -1042,7 +1098,8 @@ static int test_apply(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_set(struct libmnt_test *ts, int argc, char *argv[]) +static int test_set(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *value = NULL, *name; char *optstr; @@ -1065,7 +1122,8 @@ static int test_set(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_get(struct libmnt_test *ts, int argc, char *argv[]) +static int test_get(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { char *optstr; const char *name; @@ -1094,7 +1152,32 @@ static int test_get(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_remove(struct libmnt_test *ts, int argc, char *argv[]) +static int test_missing(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) +{ + const char *optstr; + const char *wanted; + char *missing = NULL; + int rc; + + if (argc < 2) + return -EINVAL; + optstr = argv[1]; + wanted = argv[2]; + + rc = mnt_optstr_get_missing(optstr, wanted, &missing); + if (rc == 0) + printf("missing: %s\n", missing); + else if (rc == 1) { + printf("nothing\n"); + rc = 0; + } else + printf("parse error: %s\n", optstr); + return rc; +} + +static int test_remove(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *name; char *optstr; @@ -1114,7 +1197,8 @@ static int test_remove(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_dedup(struct libmnt_test *ts, int argc, char *argv[]) +static int test_dedup(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *name; char *optstr; @@ -1134,7 +1218,8 @@ static int test_dedup(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_match(struct libmnt_test *ts, int argc, char *argv[]) +static int test_match(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { char *optstr, *pattern; @@ -1156,6 +1241,7 @@ int main(int argc, char *argv[]) { "--prepend",test_prepend,"<optstr> <name> [<value>] prepend value to optstr" }, { "--set", test_set, "<optstr> <name> [<value>] (un)set value" }, { "--get", test_get, "<optstr> <name> search name in optstr" }, + { "--missing",test_missing,"<optstr> <wanted> what from wanted is missing" }, { "--remove", test_remove, "<optstr> <name> remove name in optstr" }, { "--dedup", test_dedup, "<optstr> <name> deduplicate name in optstr" }, { "--match", test_match, "<optstr> <pattern> compare optstr with pattern" }, diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 9725664..526edce 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -1925,7 +1925,8 @@ int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs) #ifdef TEST_PROGRAM #include "pathnames.h" -static int parser_errcb(struct libmnt_table *tb, const char *filename, int line) +static int parser_errcb(struct libmnt_table *tb __attribute__((unused)), + const char *filename, int line) { fprintf(stderr, "%s:%d: parse error\n", filename, line); @@ -1954,12 +1955,16 @@ err: return NULL; } -static int test_copy_fs(struct libmnt_test *ts, int argc, char *argv[]) +static int test_copy_fs(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; int rc = -1; + if (argc != 2) + return -1; + tb = create_table(argv[1], FALSE); if (!tb) return -1; @@ -1984,7 +1989,8 @@ done: return rc; } -static int test_parse(struct libmnt_test *ts, int argc, char *argv[]) +static int test_parse(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb = NULL; struct libmnt_iter *itr = NULL; @@ -2020,7 +2026,8 @@ done: return rc; } -static int test_find_idx(struct libmnt_test *ts, int argc, char *argv[]) +static int test_find_idx(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs = NULL; @@ -2065,7 +2072,8 @@ done: return rc; } -static int test_find(struct libmnt_test *ts, int argc, char *argv[], int dr) +static int test_find(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[], int dr) { struct libmnt_table *tb; struct libmnt_fs *fs = NULL; @@ -2107,23 +2115,29 @@ done: return rc; } -static int test_find_bw(struct libmnt_test *ts, int argc, char *argv[]) +static int test_find_bw(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { return test_find(ts, argc, argv, MNT_ITER_BACKWARD); } -static int test_find_fw(struct libmnt_test *ts, int argc, char *argv[]) +static int test_find_fw(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { return test_find(ts, argc, argv, MNT_ITER_FORWARD); } -static int test_find_pair(struct libmnt_test *ts, int argc, char *argv[]) +static int test_find_pair(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; struct libmnt_cache *mpc = NULL; int rc = -1; + if (argc != 4) + return -1; + tb = create_table(argv[1], FALSE); if (!tb) return -1; @@ -2144,13 +2158,17 @@ done: return rc; } -static int test_find_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) +static int test_find_mountpoint(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb; struct libmnt_fs *fs; struct libmnt_cache *mpc = NULL; int rc = -1; + if (argc != 2) + return -1; + tb = mnt_new_table_from_file(_PATH_PROC_MOUNTINFO); if (!tb) return -1; @@ -2171,13 +2189,17 @@ done: return rc; } -static int test_is_mounted(struct libmnt_test *ts, int argc, char *argv[]) +static int test_is_mounted(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb = NULL, *fstab = NULL; struct libmnt_fs *fs; struct libmnt_iter *itr = NULL; struct libmnt_cache *mpc = NULL; + if (argc != 2) + return -1; + tb = mnt_new_table_from_file("/proc/self/mountinfo"); if (!tb) { fprintf(stderr, "failed to parse mountinfo\n"); @@ -2227,7 +2249,8 @@ static int test_uniq_cmp(struct libmnt_table *tb __attribute__((__unused__)), return mnt_fs_streq_target(a, mnt_fs_get_target(b)) ? 0 : 1; } -static int test_uniq(struct libmnt_test *ts, int argc, char *argv[]) +static int test_uniq(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb; int rc = -1; diff --git a/libmount/src/tab_diff.c b/libmount/src/tab_diff.c index d5fbc4d..2765caf 100644 --- a/libmount/src/tab_diff.c +++ b/libmount/src/tab_diff.c @@ -309,7 +309,8 @@ done: #ifdef TEST_PROGRAM -static int test_diff(struct libmnt_test *ts, int argc, char *argv[]) +static int test_diff(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_table *tb_old, *tb_new; struct libmnt_tabdiff *diff; @@ -317,6 +318,9 @@ static int test_diff(struct libmnt_test *ts, int argc, char *argv[]) struct libmnt_fs *old, *new; int rc = -1, change; + if (argc != 3) + return -1; + tb_old = mnt_new_table_from_file(argv[1]); tb_new = mnt_new_table_from_file(argv[2]); diff = mnt_new_tabdiff(); diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c index f5e5d30..87512af 100644 --- a/libmount/src/tab_update.c +++ b/libmount/src/tab_update.c @@ -35,9 +35,15 @@ struct libmnt_update { struct libmnt_fs *fs; char *filename; unsigned long mountflags; - int ready; + + int act_fd; + char *act_filename; + + unsigned int ready : 1, + missing_options : 1; struct libmnt_table *mountinfo; + struct libmnt_lock *lock; }; static int set_fs_root(struct libmnt_update *upd, struct libmnt_fs *fs, unsigned long mountflags); @@ -56,6 +62,7 @@ struct libmnt_update *mnt_new_update(void) if (!upd) return NULL; + upd->act_fd = -1; DBG(UPDATE, ul_debugobj(upd, "allocate")); return upd; } @@ -73,10 +80,14 @@ void mnt_free_update(struct libmnt_update *upd) DBG(UPDATE, ul_debugobj(upd, "free")); + mnt_unref_lock(upd->lock); mnt_unref_fs(upd->fs); mnt_unref_table(upd->mountinfo); + if (upd->act_fd >= 0) + close(upd->act_fd); free(upd->target); free(upd->filename); + free(upd->act_filename); free(upd); } @@ -173,7 +184,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, mnt_unref_fs(upd->fs); free(upd->target); - upd->ready = FALSE; + upd->ready = 0; upd->fs = NULL; upd->target = NULL; upd->mountflags = 0; @@ -206,7 +217,7 @@ int mnt_update_set_fs(struct libmnt_update *upd, unsigned long mountflags, } DBG(UPDATE, ul_debugobj(upd, "ready")); - upd->ready = TRUE; + upd->ready = 1; return 0; } @@ -666,43 +677,42 @@ static int add_file_entry(struct libmnt_table *tb, struct libmnt_update *upd) return update_table(upd, tb); } -static int update_add_entry(struct libmnt_update *upd, struct libmnt_lock *lc) +static int update_add_entry(struct libmnt_update *upd) { struct libmnt_table *tb; int rc = 0; assert(upd); assert(upd->fs); + assert(upd->lock); DBG(UPDATE, ul_debugobj(upd, "%s: add entry", upd->filename)); - if (lc) - rc = mnt_lock_file(lc); + rc = mnt_lock_file(upd->lock); if (rc) return -MNT_ERR_LOCK; tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1); if (tb) rc = add_file_entry(tb, upd); - if (lc) - mnt_unlock_file(lc); + mnt_unlock_file(upd->lock); mnt_unref_table(tb); return rc; } -static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc) +static int update_remove_entry(struct libmnt_update *upd) { struct libmnt_table *tb; int rc = 0; assert(upd); assert(upd->target); + assert(upd->lock); DBG(UPDATE, ul_debugobj(upd, "%s: remove entry", upd->filename)); - if (lc) - rc = mnt_lock_file(lc); + rc = mnt_lock_file(upd->lock); if (rc) return -MNT_ERR_LOCK; @@ -714,23 +724,22 @@ static int update_remove_entry(struct libmnt_update *upd, struct libmnt_lock *lc rc = update_table(upd, tb); } } - if (lc) - mnt_unlock_file(lc); + mnt_unlock_file(upd->lock); mnt_unref_table(tb); return rc; } -static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *lc) +static int update_modify_target(struct libmnt_update *upd) { struct libmnt_table *tb = NULL; int rc = 0; assert(upd); + assert(upd->lock); DBG(UPDATE, ul_debugobj(upd, "%s: modify target", upd->filename)); - if (lc) - rc = mnt_lock_file(lc); + rc = mnt_lock_file(upd->lock); if (rc) return -MNT_ERR_LOCK; @@ -779,14 +788,13 @@ static int update_modify_target(struct libmnt_update *upd, struct libmnt_lock *l } done: - if (lc) - mnt_unlock_file(lc); - + mnt_unlock_file(upd->lock); mnt_unref_table(tb); return rc; } -static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock *lc) +/* replaces option in the table entry by new options from @udp */ +static int update_modify_options(struct libmnt_update *upd) { struct libmnt_table *tb = NULL; int rc = 0; @@ -794,13 +802,13 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock * assert(upd); assert(upd->fs); + assert(upd->lock); DBG(UPDATE, ul_debugobj(upd, "%s: modify options", upd->filename)); fs = upd->fs; - if (lc) - rc = mnt_lock_file(lc); + rc = mnt_lock_file(upd->lock); if (rc) return -MNT_ERR_LOCK; @@ -819,11 +827,77 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock * rc = add_file_entry(tb, upd); /* not found, add new */ } - if (lc) - mnt_unlock_file(lc); + mnt_unlock_file(upd->lock); + mnt_unref_table(tb); + return rc; +} + +/* add user options missing in the table entry by new options from @udp */ +static int update_add_options(struct libmnt_update *upd) +{ + struct libmnt_table *tb = NULL; + int rc = 0; + struct libmnt_fs *fs; + + assert(upd); + assert(upd->fs); + assert(upd->lock); + + if (!upd->fs->user_optstr) + return 0; + DBG(UPDATE, ul_debugobj(upd, "%s: add options", upd->filename)); + + fs = upd->fs; + + rc = mnt_lock_file(upd->lock); + if (rc) + return -MNT_ERR_LOCK; + + tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1); + if (tb) { + struct libmnt_fs *cur = mnt_table_find_target(tb, + mnt_fs_get_target(fs), + MNT_ITER_BACKWARD); + if (cur) { + char *u = NULL; + + rc = mnt_optstr_get_missing(cur->user_optstr, upd->fs->user_optstr, &u); + if (!rc && u) { + DBG(UPDATE, ul_debugobj(upd, " add missing: %s", u)); + rc = mnt_optstr_append_option(&cur->user_optstr, u, NULL); + } + if (!rc && u) + rc = update_table(upd, tb); + + if (rc == 1) /* nothing is missing */ + rc = 0; + } else + rc = add_file_entry(tb, upd); /* not found, add new */ + } + + mnt_unlock_file(upd->lock); mnt_unref_table(tb); return rc; + +} + +static int update_init_lock(struct libmnt_update *upd, struct libmnt_lock *lc) +{ + assert(upd); + + if (lc) { + mnt_unref_lock(upd->lock); + mnt_ref_lock(lc); + upd->lock = lc; + } else if (!upd->lock) { + upd->lock = mnt_new_lock(upd->filename, 0); + if (!upd->lock) + return -ENOMEM; + mnt_lock_block_signals(upd->lock, TRUE); + } + + return 0; } /** @@ -842,7 +916,6 @@ static int update_modify_options(struct libmnt_update *upd, struct libmnt_lock * */ int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc) { - struct libmnt_lock *lc0 = lc; int rc = -EINVAL; if (!upd || !upd->filename) @@ -854,33 +927,32 @@ int mnt_update_table(struct libmnt_update *upd, struct libmnt_lock *lc) if (upd->fs) { DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr)); } - if (!lc) { - lc = mnt_new_lock(upd->filename, 0); - if (lc) - mnt_lock_block_signals(lc, TRUE); - } + + rc = update_init_lock(upd, lc); + if (rc) + goto done; if (!upd->fs && upd->target) - rc = update_remove_entry(upd, lc); /* umount */ + rc = update_remove_entry(upd); /* umount */ else if (upd->mountflags & MS_MOVE) - rc = update_modify_target(upd, lc); /* move */ + rc = update_modify_target(upd); /* move */ else if (upd->mountflags & MS_REMOUNT) - rc = update_modify_options(upd, lc); /* remount */ + rc = update_modify_options(upd); /* remount */ + else if (upd->fs && upd->missing_options) + rc = update_add_options(upd); /* mount by externel helper */ else if (upd->fs) - rc = update_add_entry(upd, lc); /* mount */ + rc = update_add_entry(upd); /* mount */ - upd->ready = FALSE; + upd->ready = 1; +done: DBG(UPDATE, ul_debugobj(upd, "%s: update tab: done [rc=%d]", upd->filename, rc)); - if (lc != lc0) - mnt_free_lock(lc); return rc; } -int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc) +int mnt_update_already_done(struct libmnt_update *upd) { struct libmnt_table *tb = NULL; - struct libmnt_lock *lc0 = lc; int rc = 0; if (!upd || !upd->filename || (!upd->fs && !upd->target)) @@ -888,37 +960,32 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc) DBG(UPDATE, ul_debugobj(upd, "%s: checking for previous update", upd->filename)); - if (!lc) { - lc = mnt_new_lock(upd->filename, 0); - if (lc) - mnt_lock_block_signals(lc, TRUE); - } - if (lc) { - rc = mnt_lock_file(lc); - if (rc) { - rc = -MNT_ERR_LOCK; - goto done; - } - } - tb = __mnt_new_table_from_file(upd->filename, MNT_FMT_UTAB, 1); - if (lc) - mnt_unlock_file(lc); if (!tb) goto done; if (upd->fs) { /* mount */ + struct libmnt_fs *fs; const char *tgt = mnt_fs_get_target(upd->fs); const char *src = mnt_fs_get_bindsrc(upd->fs) ? mnt_fs_get_bindsrc(upd->fs) : mnt_fs_get_source(upd->fs); - if (mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD)) { + fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD); + if (fs) { DBG(UPDATE, ul_debugobj(upd, "%s: found %s %s", upd->filename, src, tgt)); + + /* Check if utab entry (probably writen by /sbin/mount.<type> + * helper) contains all options expected by this update */ + if (mnt_optstr_get_missing(fs->user_optstr, upd->fs->user_optstr, NULL) == 0) { + upd->missing_options = 1; + DBG(UPDATE, ul_debugobj(upd, " missing options detected")); + } + } else rc = 1; - } + } else if (upd->target) { /* umount */ if (!mnt_table_find_target(tb, upd->target, MNT_ITER_BACKWARD)) { @@ -930,13 +997,133 @@ int mnt_update_already_done(struct libmnt_update *upd, struct libmnt_lock *lc) mnt_unref_table(tb); done: - if (lc && lc != lc0) - mnt_free_lock(lc); DBG(UPDATE, ul_debugobj(upd, "%s: previous update check done [rc=%d]", upd->filename, rc)); return rc; } +int mnt_update_emit_event(struct libmnt_update *upd) +{ + char *filename; + int fd; + + if (!upd || !upd->filename) + return -EINVAL; + + if (asprintf(&filename, "%s.event", upd->filename) <= 0) + return -ENOMEM; + + DBG(UPDATE, ul_debugobj(upd, "emitting utab event")); + + fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC, + S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH); + free(filename); + if (fd < 0) + return -errno; + close(fd); + return 0; +} + +/* + * Let's use /run/mount/utab.act file to report to libmount monitor that + * libmount is working with utab. In this case, the monitor can ignore all + * events from kernel until entire mount (with all steps) is done. + * + * For example mount NFS with x-* options, means + * - create utab.act and mark it as used (by LOCK_SH) + * - exec /sbin/mount.nfs + * - call mount(2) (kernel event on /proc/self/mounts) + * - utab update (NFS stuff) + * - utab update (add x-* userspace options) + * - unlink utab.act (if not use anyone else) + * - release event by /run/mount/utab.event + * + * Note, this is used only when utab is in the game (x-* options). + */ +int mnt_update_start(struct libmnt_update *upd) +{ + int rc = 0; + mode_t oldmask; + + if (!upd || !upd->filename) + return -EINVAL; + + if (!upd->act_filename && + asprintf(&upd->act_filename, "%s.act", upd->filename) <= 0) + return -ENOMEM; + + /* Use exclusive lock to avoid some other proces will remove the the + * file before it's marked as used by LOCK_SH (below) */ + rc = update_init_lock(upd, NULL); + if (rc) + return rc; + + rc = mnt_lock_file(upd->lock); + if (rc) + return -MNT_ERR_LOCK; + + DBG(UPDATE, ul_debugobj(upd, "creating act file")); + + oldmask = umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH); + upd->act_fd = open(upd->act_filename, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR); + umask(oldmask); + + if (upd->act_fd < 0) { + rc = -errno; + goto fail; + } + + /* mark the file as used */ + rc = flock(upd->act_fd, LOCK_SH); + if (rc) { + rc = -errno; + goto fail; + } + + mnt_unlock_file(upd->lock); + return 0; +fail: + DBG(UPDATE, ul_debugobj(upd, "act file failed [rc=%d]", rc)); + mnt_unlock_file(upd->lock); + unlink(upd->act_filename); + if (upd->act_fd >= 0) + close(upd->act_fd); + upd->act_fd = -1; + return rc; +} + +int mnt_update_end(struct libmnt_update *upd) +{ + int rc; + + if (!upd || upd->act_fd < 0) + return -EINVAL; + + DBG(UPDATE, ul_debugobj(upd, "removing act file")); + + /* make sure nobody else will use the file */ + rc = mnt_lock_file(upd->lock); + if (rc) + return -MNT_ERR_LOCK; + + /* mark the file as unused */ + flock(upd->act_fd, LOCK_UN); + errno = 0; + + /* check if nobody else need the file (if yes, then the file is under LOCK_SH) )*/ + if (flock(upd->act_fd, LOCK_EX | LOCK_NB) != 0) { + if (errno == EWOULDBLOCK) + DBG(UPDATE, ul_debugobj(upd, "act file used, no unlink")); + } else { + DBG(UPDATE, ul_debugobj(upd, "unlinking act file")); + unlink(upd->act_filename); + } + + mnt_unlock_file(upd->lock); + close(upd->act_fd); + upd->act_fd = -1; + return 0; +} #ifdef TEST_PROGRAM @@ -970,7 +1157,8 @@ done: return rc; } -static int test_add(struct libmnt_test *ts, int argc, char *argv[]) +static int test_add(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); int rc; @@ -987,14 +1175,16 @@ static int test_add(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_remove(struct libmnt_test *ts, int argc, char *argv[]) +static int test_remove(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { if (argc < 2) return -1; return update(argv[1], NULL, 0); } -static int test_move(struct libmnt_test *ts, int argc, char *argv[]) +static int test_move(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); int rc; @@ -1010,7 +1200,8 @@ static int test_move(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_remount(struct libmnt_test *ts, int argc, char *argv[]) +static int test_remount(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); int rc; @@ -1025,7 +1216,8 @@ static int test_remount(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_replace(struct libmnt_test *ts, int argc, char *argv[]) +static int test_replace(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct libmnt_fs *fs = mnt_new_fs(); struct libmnt_table *tb = mnt_new_table(); diff --git a/libmount/src/utils.c b/libmount/src/utils.c index 3817b39..a2f8ea0 100644 --- a/libmount/src/utils.c +++ b/libmount/src/utils.c @@ -526,7 +526,7 @@ static int add_filesystem(char ***filesystems, char *name) if (n == 0 || !((n + 1) % MYCHUNK)) { size_t items = ((n + 1 + MYCHUNK) / MYCHUNK) * MYCHUNK; - char **x = realloc(*filesystems, items * sizeof(char *)); + char **x = reallocarray(*filesystems, items, sizeof(char *)); if (!x) goto err; @@ -1032,7 +1032,7 @@ int mnt_open_uniq_filename(const char *filename, char **name) rc = asprintf(&n, "%s.XXXXXX", filename); if (rc <= 0) - return -errno; + return -ENOMEM; /* This is for very old glibc and for compatibility with Posix, which says * nothing about mkstemp() mode. All sane glibc use secure mode (0600). @@ -1134,7 +1134,7 @@ char *mnt_get_kernel_cmdline_option(const char *name) int val = 0; char *p, *res = NULL, *mem = NULL; char buf[BUFSIZ]; /* see kernel include/asm-generic/setup.h: COMMAND_LINE_SIZE */ - const char *path = _PATH_PROC_CMDLINE; + const char *path; if (!name || !name[0]) return NULL; @@ -1143,6 +1143,8 @@ char *mnt_get_kernel_cmdline_option(const char *name) path = getenv("LIBMOUNT_KERNEL_CMDLINE"); if (!path) path = _PATH_PROC_CMDLINE; +#else + path = _PATH_PROC_CMDLINE; #endif f = fopen(path, "r" UL_CLOEXECSTR); if (!f) @@ -1287,8 +1289,12 @@ done: } #ifdef TEST_PROGRAM -static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[]) +static int test_match_fstype(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 3) + return -1; + char *type = argv[1]; char *pattern = argv[2]; @@ -1296,8 +1302,12 @@ static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_match_options(struct libmnt_test *ts, int argc, char *argv[]) +static int test_match_options(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 3) + return -1; + char *optstr = argv[1]; char *pattern = argv[2]; @@ -1305,8 +1315,12 @@ static int test_match_options(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_startswith(struct libmnt_test *ts, int argc, char *argv[]) +static int test_startswith(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 3) + return -1; + char *optstr = argv[1]; char *pattern = argv[2]; @@ -1314,8 +1328,12 @@ static int test_startswith(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_endswith(struct libmnt_test *ts, int argc, char *argv[]) +static int test_endswith(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 3) + return -1; + char *optstr = argv[1]; char *pattern = argv[2]; @@ -1323,8 +1341,12 @@ static int test_endswith(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) +static int test_mountpoint(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *path = canonicalize_path(argv[1]), *mnt = path ? mnt_get_mountpoint(path) : NULL; @@ -1334,13 +1356,17 @@ static int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[]) +static int test_filesystems(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { char **filesystems = NULL; int rc; - rc = mnt_get_filesystems(&filesystems, argc ? argv[1] : NULL); - if (!rc) { + if (argc != 1 && argc != 2) + return -1; + + rc = mnt_get_filesystems(&filesystems, argc == 2 ? argv[1] : NULL); + if (!rc && filesystems) { char **p; for (p = filesystems; *p; p++) printf("%s\n", *p); @@ -1349,8 +1375,12 @@ static int test_filesystems(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_chdir(struct libmnt_test *ts, int argc, char *argv[]) +static int test_chdir(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + int rc; char *path = canonicalize_path(argv[1]), *last = NULL; @@ -1368,8 +1398,12 @@ static int test_chdir(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[]) +static int test_kernel_cmdline(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *name = argv[1]; char *res; @@ -1387,7 +1421,8 @@ static int test_kernel_cmdline(struct libmnt_test *ts, int argc, char *argv[]) } -static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[]) +static int test_guess_root(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { int rc; char *real; @@ -1413,10 +1448,14 @@ static int test_guess_root(struct libmnt_test *ts, int argc, char *argv[]) return 0; } -static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[]) +static int test_mkdir(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { int rc; + if (argc != 2) + return -1; + rc = ul_mkdir_p(argv[1], S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); @@ -1425,11 +1464,15 @@ static int test_mkdir(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[]) +static int test_statfs_type(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { struct statfs vfs; int rc; + if (argc != 2) + return -1; + rc = statfs(argv[1], &vfs); if (rc) printf("%s: statfs failed: %m\n", argv[1]); @@ -1440,8 +1483,12 @@ static int test_statfs_type(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int tests_parse_uid(struct libmnt_test *ts, int argc, char *argv[]) +static int tests_parse_uid(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *str = argv[1]; uid_t uid = (uid_t) -1; int rc; @@ -1455,8 +1502,12 @@ static int tests_parse_uid(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int tests_parse_gid(struct libmnt_test *ts, int argc, char *argv[]) +static int tests_parse_gid(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *str = argv[1]; gid_t gid = (gid_t) -1; int rc; @@ -1470,8 +1521,12 @@ static int tests_parse_gid(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[]) +static int tests_parse_mode(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *str = argv[1]; mode_t mod = (mode_t) -1; int rc; @@ -1488,8 +1543,12 @@ static int tests_parse_mode(struct libmnt_test *ts, int argc, char *argv[]) return rc; } -static int tests_stat(struct libmnt_test *ts, int argc, char *argv[]) +static int tests_stat(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { + if (argc != 2) + return -1; + char *path = argv[1]; struct stat st; int rc; diff --git a/libmount/src/version.c b/libmount/src/version.c index 894c20c..b1e3432 100644 --- a/libmount/src/version.c +++ b/libmount/src/version.c @@ -121,7 +121,8 @@ int mnt_get_library_features(const char ***features) } #ifdef TEST_PROGRAM -static int test_version(struct libmnt_test *ts, int argc, char *argv[]) +static int test_version(struct libmnt_test *ts __attribute__((unused)), + int argc, char *argv[]) { const char *ver; const char **features; diff --git a/libsmartcols/Makemodule.am b/libsmartcols/Makemodule.am index 012848b..77e87b5 100644 --- a/libsmartcols/Makemodule.am +++ b/libsmartcols/Makemodule.am @@ -12,4 +12,7 @@ pkgconfig_DATA += libsmartcols/smartcols.pc PATHFILES += libsmartcols/smartcols.pc EXTRA_DIST += libsmartcols/COPYING +MANPAGES += libsmartcols/scols-filter.5 +dist_noinst_DATA += libsmartcols/scols-filter.5.adoc + endif # BUILD_LIBSMARTCOLS diff --git a/libsmartcols/docs/Makefile.in b/libsmartcols/docs/Makefile.in index de740f1..df70f76 100644 --- a/libsmartcols/docs/Makefile.in +++ b/libsmartcols/docs/Makefile.in @@ -117,7 +117,8 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_vscript.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/tls.m4 \ - $(top_srcdir)/m4/ul.m4 $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ul.m4 $(top_srcdir)/m4/year2038.m4 \ + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -159,10 +160,12 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BISON = @BISON@ BSD_WARN_CFLAGS = @BSD_WARN_CFLAGS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ +COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CRYPTSETUP_CFLAGS = @CRYPTSETUP_CFLAGS@ @@ -192,6 +195,7 @@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ FILECMD = @FILECMD@ +FLEX = @FLEX@ FUZZING_ENGINE_LDFLAGS = @FUZZING_ENGINE_LDFLAGS@ GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ GMSGFMT = @GMSGFMT@ @@ -219,6 +223,8 @@ LIBFDISK_VERSION = @LIBFDISK_VERSION@ LIBFDISK_VERSION_INFO = @LIBFDISK_VERSION_INFO@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ +LIBLASTLOG2_VERSION = @LIBLASTLOG2_VERSION@ +LIBLASTLOG2_VERSION_INFO = @LIBLASTLOG2_VERSION_INFO@ LIBMOUNT_MAJOR_VERSION = @LIBMOUNT_MAJOR_VERSION@ LIBMOUNT_MINOR_VERSION = @LIBMOUNT_MINOR_VERSION@ LIBMOUNT_PATCH_VERSION = @LIBMOUNT_PATCH_VERSION@ @@ -244,6 +250,7 @@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MATH_LIBS = @MATH_LIBS@ MKDIR_P = @MKDIR_P@ +MQ_LIBS = @MQ_LIBS@ MSGFMT = @MSGFMT@ MSGFMT_015 = @MSGFMT_015@ MSGMERGE = @MSGMERGE@ @@ -257,7 +264,6 @@ NCURSES_CFLAGS = @NCURSES_CFLAGS@ NCURSES_LIBS = @NCURSES_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ -NO_UNUSED_WARN_CFLAGS = @NO_UNUSED_WARN_CFLAGS@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ @@ -297,6 +303,8 @@ SHELL = @SHELL@ SOCKET_LIBS = @SOCKET_LIBS@ SOLIB_CFLAGS = @SOLIB_CFLAGS@ SOLIB_LDFLAGS = @SOLIB_LDFLAGS@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ STRIP = @STRIP@ SUID_CFLAGS = @SUID_CFLAGS@ SUID_LDFLAGS = @SUID_LDFLAGS@ @@ -382,6 +390,7 @@ sysconfdir = @sysconfdir@ sysconfstaticdir = @sysconfstaticdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ +tmpfilesdir = @tmpfilesdir@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @@ -391,6 +400,7 @@ usrsbin_execdir = @usrsbin_execdir@ vendordir = @vendordir@ with_bashcompletiondir = @with_bashcompletiondir@ with_systemdsystemunitdir = @with_systemdsystemunitdir@ +with_tmpfilesdir = @with_tmpfilesdir@ # We require automake 1.10 at least. AUTOMAKE_OPTIONS = 1.10 diff --git a/libsmartcols/docs/libsmartcols-docs.xml b/libsmartcols/docs/libsmartcols-docs.xml index 9ded584..bd55182 100644 --- a/libsmartcols/docs/libsmartcols-docs.xml +++ b/libsmartcols/docs/libsmartcols-docs.xml @@ -35,6 +35,7 @@ available from https://www.kernel.org/pub/linux/utils/util-linux/. <xi:include href="xml/cell.xml"/> <xi:include href="xml/symbols.xml"/> <xi:include href="xml/grouping.xml"/> + <xi:include href="xml/filter.xml"/> </part> <part> <title>Printing</title> @@ -82,4 +83,16 @@ available from https://www.kernel.org/pub/linux/utils/util-linux/. <title>Index of new symbols in 2.35</title> <xi:include href="xml/api-index-2.35.xml"><xi:fallback /></xi:include> </index> + <index role="2.38"> + <title>Index of new symbols in 2.38</title> + <xi:include href="xml/api-index-2.38.xml"><xi:fallback /></xi:include> + </index> + <index role="2.39"> + <title>Index of new symbols in 2.39</title> + <xi:include href="xml/api-index-2.39.xml"><xi:fallback /></xi:include> + </index> + <index role="2.40"> + <title>Index of new symbols in 2.40</title> + <xi:include href="xml/api-index-2.40.xml"><xi:fallback /></xi:include> + </index> </book> diff --git a/libsmartcols/docs/libsmartcols-sections.txt b/libsmartcols/docs/libsmartcols-sections.txt index fd6f2ba..5b343d0 100644 --- a/libsmartcols/docs/libsmartcols-sections.txt +++ b/libsmartcols/docs/libsmartcols-sections.txt @@ -5,9 +5,11 @@ scols_cell_copy_content scols_cell_get_alignment scols_cell_get_color scols_cell_get_data +scols_cell_get_datasiz scols_cell_get_flags scols_cell_get_userdata scols_cell_refer_data +scols_cell_refer_memory scols_cell_set_color scols_cell_set_data scols_cell_set_flags @@ -20,6 +22,7 @@ scols_reset_cell <FILE>column</FILE> libscols_column scols_column_get_color +scols_column_get_data_type scols_column_get_flags scols_column_get_header scols_column_get_json_type @@ -29,6 +32,8 @@ scols_column_get_safechars scols_column_get_table scols_column_get_whint scols_column_get_width +scols_column_get_wrap_data +scols_column_has_data_func scols_column_is_customwrap scols_column_is_hidden scols_column_is_noextremes @@ -39,6 +44,8 @@ scols_column_is_trunc scols_column_is_wrap scols_column_set_cmpfunc scols_column_set_color +scols_column_set_data_func +scols_column_set_data_type scols_column_set_flags scols_column_set_json_type scols_column_set_name @@ -50,8 +57,10 @@ scols_copy_column scols_new_column scols_ref_column scols_unref_column +scols_shellvar_name scols_wrapnl_chunksize scols_wrapnl_nextchunk +scols_wrapzero_nextchunk </SECTION> <SECTION> @@ -64,6 +73,30 @@ scols_reset_iter </SECTION> <SECTION> +<FILE>filter</FILE> +libscols_filter +libscols_counter +scols_counter_get_name +scols_counter_get_result +scols_counter_set_func +scols_counter_set_name +scols_counter_set_param +scols_dump_filter +scols_filter_assign_column +scols_filter_get_errmsg +scols_filter_new_counter +scols_filter_next_counter +scols_filter_next_holder +scols_filter_parse_string +scols_filter_set_filler_cb +scols_line_apply_filter +scols_line_is_filled +scols_new_filter +scols_ref_filter +scols_unref_filter +</SECTION> + +<SECTION> <FILE>line</FILE> libscols_line scols_copy_line @@ -146,6 +179,7 @@ scols_table_enable_shellvar scols_table_get_column scols_table_get_column_by_name scols_table_get_column_separator +scols_table_get_cursor scols_table_get_line scols_table_get_line_separator scols_table_get_name diff --git a/libsmartcols/docs/version.xml b/libsmartcols/docs/version.xml index a69af57..4bdd32f 100644 --- a/libsmartcols/docs/version.xml +++ b/libsmartcols/docs/version.xml @@ -1 +1 @@ -2.39.3 +2.40 diff --git a/libsmartcols/meson.build b/libsmartcols/meson.build index 122b1e8..bedd6da 100644 --- a/libsmartcols/meson.build +++ b/libsmartcols/meson.build @@ -11,6 +11,18 @@ configure_file( install_dir : join_paths(get_option('includedir'), 'libsmartcols'), ) +scols_bison = generator( + bison, + output : ['@BASENAME@.c', '@BASENAME@.h'], + arguments : ['@INPUT@', '--output=@OUTPUT0@', '--defines=@OUTPUT1@']) +scols_parser_c = scols_bison.process('src/filter-parser.y') + +scols_flex = generator( + flex, + output : ['@BASENAME@.c', '@BASENAME@.h'], + arguments : ['--outfile=@OUTPUT0@', '--header-file=@OUTPUT1@', '@INPUT@']) +scols_scanner_c = scols_flex.process('src/filter-scanner.l') + lib_smartcols_sources = ''' src/smartcolsP.h src/iter.c @@ -26,7 +38,12 @@ lib_smartcols_sources = ''' src/grouping.c src/walk.c src/init.c -'''.split() + src/filter.c + src/filter-param.c + src/filter-expr.c +'''.split() \ + + scols_parser_c + scols_scanner_c + libsmartcols_sym = 'src/libsmartcols.sym' libsmartcols_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libsmartcols_sym) @@ -55,3 +72,5 @@ if build_libsmartcols meson.override_dependency('smartcols', smartcols_dep) endif endif + +manadocs += ['libsmartcols/scols-filter.5.adoc'] diff --git a/libsmartcols/samples/Makemodule.am b/libsmartcols/samples/Makemodule.am index c0130b9..b192eba 100644 --- a/libsmartcols/samples/Makemodule.am +++ b/libsmartcols/samples/Makemodule.am @@ -4,13 +4,13 @@ check_PROGRAMS += \ sample-scols-title \ sample-scols-wrap \ sample-scols-continuous \ + sample-scols-continuous-json \ sample-scols-fromfile \ sample-scols-grouping-simple \ sample-scols-grouping-overlay \ sample-scols-maxout -sample_scols_cflags = $(AM_CFLAGS) $(NO_UNUSED_WARN_CFLAGS) \ - -I$(ul_libsmartcols_incdir) +sample_scols_cflags = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir) sample_scols_ldadd = libsmartcols.la $(LDADD) if HAVE_OPENAT @@ -36,6 +36,10 @@ sample_scols_continuous_SOURCES = libsmartcols/samples/continuous.c sample_scols_continuous_LDADD = $(sample_scols_ldadd) libcommon.la sample_scols_continuous_CFLAGS = $(sample_scols_cflags) +sample_scols_continuous_json_SOURCES = libsmartcols/samples/continuous-json.c +sample_scols_continuous_json_LDADD = $(sample_scols_ldadd) libcommon.la +sample_scols_continuous_json_CFLAGS = $(sample_scols_cflags) + sample_scols_maxout_SOURCES = libsmartcols/samples/maxout.c sample_scols_maxout_LDADD = $(sample_scols_ldadd) sample_scols_maxout_CFLAGS = $(sample_scols_cflags) diff --git a/libsmartcols/samples/continuous-json.c b/libsmartcols/samples/continuous-json.c new file mode 100644 index 0000000..ae1f405 --- /dev/null +++ b/libsmartcols/samples/continuous-json.c @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright (C) 2016 Karel Zak <kzak@redhat.com> + * Copyright (C) 2023 Thomas Weißschuh <thomas@t-8ch.de> + */ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "c.h" +#include "xalloc.h" + +#include "libsmartcols.h" + +/* add columns to the @tb */ +static void setup_columns(struct libscols_table *tb) +{ + scols_table_enable_maxout(tb, 1); + if (!scols_table_new_column(tb, "COUNT", 0.1, SCOLS_FL_RIGHT)) + goto fail; + if (!scols_table_new_column(tb, "TEXT", 0.9, 0)) + goto fail; + return; +fail: + scols_unref_table(tb); + err(EXIT_FAILURE, "failed to create output columns"); +} + +static struct libscols_line *add_line(struct libscols_table *tb, int i) +{ + char *p; + struct libscols_line *ln = scols_table_new_line(tb, NULL); + + if (!ln) + err(EXIT_FAILURE, "failed to create output line"); + + xasprintf(&p, "%d", i); + if (scols_line_refer_data(ln, 0, p)) + goto fail; + + xasprintf(&p, "text%d", i); + if (scols_line_refer_data(ln, 1, p)) + goto fail; + + return ln; +fail: + scols_unref_table(tb); + err(EXIT_FAILURE, "failed to create output line"); +} + +int main(void) +{ + struct libscols_table *tb; + size_t i; + + scols_init_debug(0); + + tb = scols_new_table(); + if (!tb) + err(EXIT_FAILURE, "failed to create output table"); + scols_table_enable_json(tb, 1); + + setup_columns(tb); + + for (i = 0; i < 10; i++) { + struct libscols_line *line; + + line = add_line(tb, i); + + /* print the line */ + scols_table_print_range(tb, line, NULL); + + fflush(scols_table_get_stream(tb)); + } + + scols_unref_table(tb); + return EXIT_SUCCESS; +} diff --git a/libsmartcols/samples/continuous.c b/libsmartcols/samples/continuous.c index 6bdba6e..432b027 100644 --- a/libsmartcols/samples/continuous.c +++ b/libsmartcols/samples/continuous.c @@ -65,7 +65,7 @@ fail: err(EXIT_FAILURE, "failed to create output line"); } -int main(int argc, char *argv[]) +int main(void) { struct libscols_table *tb; size_t i; @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) struct libscols_line *line; struct timeval now; int done = 0; - char *timecell = xmalloc( timecellsz ); + char *timecell = xcalloc(1, timecellsz ); line = add_line(tb, i); diff --git a/libsmartcols/samples/fromfile.c b/libsmartcols/samples/fromfile.c index 0fdc929..cf77cc4 100644 --- a/libsmartcols/samples/fromfile.c +++ b/libsmartcols/samples/fromfile.c @@ -18,136 +18,63 @@ #include "strutils.h" #include "xalloc.h" #include "optutils.h" +#include "mangle.h" +#include "path.h" #include "libsmartcols.h" -struct column_flag { - const char *name; - int mask; -}; - -static const struct column_flag flags[] = { - { "trunc", SCOLS_FL_TRUNC }, - { "tree", SCOLS_FL_TREE }, - { "right", SCOLS_FL_RIGHT }, - { "strictwidth",SCOLS_FL_STRICTWIDTH }, - { "noextremes", SCOLS_FL_NOEXTREMES }, - { "hidden", SCOLS_FL_HIDDEN }, - { "wrap", SCOLS_FL_WRAP }, - { "wrapnl", SCOLS_FL_WRAP }, - { "none", 0 } -}; - -static long name_to_flag(const char *name, size_t namesz) +static struct libscols_column *parse_column(const char *path) { - size_t i; + char buf[BUFSIZ]; + struct libscols_column *cl; - for (i = 0; i < ARRAY_SIZE(flags); i++) { - const char *cn = flags[i].name; + if (ul_path_read_buffer(NULL, buf, sizeof(buf), path) < 0) + err(EXIT_FAILURE, "failed to read column: %s", path); - if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) - return flags[i].mask; - } - warnx("unknown flag: %s", name); - return -1; -} - -static int parse_column_flags(char *str) -{ - unsigned long num_flags = 0; - - if (string_to_bitmask(str, &num_flags, name_to_flag)) - err(EXIT_FAILURE, "failed to parse column flags"); - - return num_flags; -} - -static struct libscols_column *parse_column(FILE *f) -{ - size_t len = 0; - char *line = NULL; - int nlines = 0; + cl = scols_new_column(); + if (!cl) + err(EXIT_FAILURE, "failed to allocate column"); - struct libscols_column *cl = NULL; + if (scols_column_set_properties(cl, buf) != 0) + err(EXIT_FAILURE, "failed to set column properties"); - while (getline(&line, &len, f) != -1) { - - char *p = strrchr(line, '\n'); - if (p) - *p = '\0'; - - switch (nlines) { - case 0: /* NAME */ - cl = scols_new_column(); - if (!cl) - goto fail; - if (scols_column_set_name(cl, line) != 0) - goto fail; - break; - - case 1: /* WIDTH-HINT */ - { - double whint = strtod_or_err(line, "failed to parse column whint"); - if (scols_column_set_whint(cl, whint)) - goto fail; - break; - } - case 2: /* FLAGS */ - { - int num_flags = parse_column_flags(line); - if (scols_column_set_flags(cl, num_flags)) - goto fail; - if (strcmp(line, "wrapnl") == 0) { - scols_column_set_wrapfunc(cl, - scols_wrapnl_chunksize, - scols_wrapnl_nextchunk, - NULL); - scols_column_set_safechars(cl, "\n"); - } - break; - } - case 3: /* COLOR */ - if (scols_column_set_color(cl, line)) - goto fail; - break; - default: - break; - } - - nlines++; - } - - free(line); return cl; -fail: - free(line); - scols_unref_column(cl); - return NULL; } static int parse_column_data(FILE *f, struct libscols_table *tb, int col) { size_t len = 0, nlines = 0; - int i; - char *str = NULL; + ssize_t i; + char *str = NULL, *p; while ((i = getline(&str, &len, f)) != -1) { - struct libscols_line *ln; - char *p = strrchr(str, '\n'); - if (p) - *p = '\0'; - - while ((p = strrchr(str, '\\')) && *(p + 1) == 'n') { - *p = '\n'; - memmove(p + 1, p + 2, i - (p + 2 - str)); - } + int rc = 0; ln = scols_table_get_line(tb, nlines++); if (!ln) break; - if (*str && scols_line_set_data(ln, col, str) != 0) + p = strrchr(str, '\n'); + if (p) + *p = '\0'; + if (!*str) + continue; + + /* convert \x?? to real bytes */ + if (strstr(str, "\\x")) { + struct libscols_cell *ce = scols_line_get_cell(ln, col); + size_t sz = i + 1; + char *buf = xcalloc(1, sz); + + sz = unhexmangle_to_buffer(str, buf, sz); + if (sz) + rc = scols_cell_refer_memory(ce, buf, sz); + else + free(buf); + } else + rc = scols_line_set_data(ln, col, str); + if (rc) err(EXIT_FAILURE, "failed to add output data"); } @@ -193,6 +120,83 @@ static void compose_tree(struct libscols_table *tb, int parent_col, int id_col) scols_free_iter(itr); } +static struct libscols_filter *init_filter( + struct libscols_table *tb, + const char *query, int dump) +{ + struct libscols_iter *itr; + struct libscols_filter *f = scols_new_filter(NULL); + const char *name = NULL; + int rc = 0; + + if (!f) + err(EXIT_FAILURE, "failed to allocate filter"); + if (scols_filter_parse_string(f, query) != 0) { + warnx("failed to parse filter: %s", scols_filter_get_errmsg(f)); + scols_unref_filter(f); + return NULL; + } + + itr = scols_new_iter(SCOLS_ITER_FORWARD); + if (!itr) + err(EXIT_FAILURE, "failed to allocate iterator"); + + while (scols_filter_next_holder(f, itr, &name, 0) == 0) { + struct libscols_column *col; + + col = scols_table_get_column_by_name(tb, name); + if (!col) { + warnx("unknown column '%s' in filter", name); + rc++; + continue; + } + scols_filter_assign_column(f, itr, name, col); + } + + scols_free_iter(itr); + if (dump && f) + scols_dump_filter(f, stdout); + if (rc) { + scols_unref_filter(f); + errx(EXIT_FAILURE, "failed to initialize filter"); + } + + return f; +} + +/* Note: This is a simple (naive) way to use the filter, employed here for + * testing functionality. + * + * A more effective approach to using the filter is demonstrated in lsblk.c, + * where data manipulation is divided into two steps. The initial step prepares + * only the data necessary for evaluating the filter, and the remaining data is + * gathered later, only if necessary. + */ +static void apply_filter(struct libscols_table *tb, struct libscols_filter *fltr) +{ + struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD); + struct libscols_line *ln; + + if (!itr) + err(EXIT_FAILURE, "failed to allocate iterator"); + + while (scols_table_next_line(tb, itr, &ln) == 0) { + int status = 0; + + if (scols_line_apply_filter(ln, fltr, &status) != 0) + err(EXIT_FAILURE, "failed to apply filter"); + if (status == 0) { + struct libscols_line *x = scols_line_get_parent(ln); + + if (x) + scols_line_remove_child(x, ln); + scols_table_remove_line(tb, ln); + ln = NULL; + } + } + + scols_free_iter(itr); +} static void __attribute__((__noreturn__)) usage(void) { @@ -211,6 +215,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(" -w, --width <num> hardcode terminal width\n", out); fputs(" -p, --tree-parent-column <n> parent column\n", out); fputs(" -i, --tree-id-column <n> id column\n", out); + fputs(" -Q, --filter <expr> filter\n", out); fputs(" -h, --help this help\n", out); fputs("\n", out); @@ -220,8 +225,11 @@ static void __attribute__((__noreturn__)) usage(void) int main(int argc, char *argv[]) { struct libscols_table *tb; - int c, n, nlines = 0; + int c, n, nlines = 0, rc; int parent_col = -1, id_col = -1; + int fltr_dump = 0; + const char *fltr_str = NULL; + struct libscols_filter *fltr = NULL; static const struct option longopts[] = { { "maxout", 0, NULL, 'm' }, @@ -235,6 +243,8 @@ int main(int argc, char *argv[]) { "raw", 0, NULL, 'r' }, { "export", 0, NULL, 'E' }, { "colsep", 1, NULL, 'C' }, + { "filter", 1, NULL, 'Q' }, + { "filter-dump", 0, NULL, 'd' }, { "help", 0, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; @@ -253,25 +263,23 @@ int main(int argc, char *argv[]) if (!tb) err(EXIT_FAILURE, "failed to create output table"); - while((c = getopt_long(argc, argv, "hCc:Ei:JMmn:p:rw:", longopts, NULL)) != -1) { + while((c = getopt_long(argc, argv, "hCc:dEi:JMmn:p:Q:rw:", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); switch(c) { case 'c': /* add column from file */ { - struct libscols_column *cl; - FILE *f = fopen(optarg, "r"); + struct libscols_column *cl = parse_column(optarg); - if (!f) - err(EXIT_FAILURE, "%s: open failed", optarg); - cl = parse_column(f); if (cl && scols_table_add_column(tb, cl)) err(EXIT_FAILURE, "%s: failed to add column", optarg); scols_unref_column(cl); - fclose(f); break; } + case 'd': + fltr_dump = 1; + break; case 'p': parent_col = strtou32_or_err(optarg, "failed to parse tree PARENT column"); break; @@ -300,6 +308,9 @@ int main(int argc, char *argv[]) case 'n': nlines = strtou32_or_err(optarg, "failed to parse number of lines"); break; + case 'Q': + fltr_str = optarg; + break; case 'w': scols_table_set_termforce(tb, SCOLS_TERMFORCE_ALWAYS); scols_table_set_termwidth(tb, strtou32_or_err(optarg, "failed to parse terminal width")); @@ -323,6 +334,14 @@ int main(int argc, char *argv[]) scols_unref_line(ln); } + if (fltr_str) { + fltr = init_filter(tb, fltr_str, fltr_dump); + if (!fltr) { + rc = EXIT_FAILURE; + goto done; + } + } + n = 0; while (optind < argc) { @@ -341,7 +360,13 @@ int main(int argc, char *argv[]) scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + if (fltr) + apply_filter(tb, fltr); + scols_print_table(tb); + rc = EXIT_SUCCESS; +done: + scols_unref_filter(fltr); scols_unref_table(tb); - return EXIT_SUCCESS; + return rc; } diff --git a/libsmartcols/samples/maxout.c b/libsmartcols/samples/maxout.c index 20c6424..a867697 100644 --- a/libsmartcols/samples/maxout.c +++ b/libsmartcols/samples/maxout.c @@ -19,7 +19,7 @@ enum { COL_LEFT, COL_FOO, COL_RIGHT }; -int main(int argc, char *argv[]) +int main(void) { struct libscols_table *tb; int rc = -1, nlines = 3; diff --git a/libsmartcols/samples/wrap.c b/libsmartcols/samples/wrap.c index 795bef7..8368304 100644 --- a/libsmartcols/samples/wrap.c +++ b/libsmartcols/samples/wrap.c @@ -92,7 +92,15 @@ int main(int argc, char *argv[]) if (!tb) err(EXIT_FAILURE, "failed to create output table"); - scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + if (argc > 1 && strcmp(argv[1], "--export") == 0) + scols_table_enable_export(tb, 1); + else if (argc > 1 && strcmp(argv[1], "--raw") == 0) + scols_table_enable_raw(tb, 1); + else if (argc > 1 && strcmp(argv[1], "--json") == 0) + scols_table_enable_json(tb, 1); + else + scols_table_enable_colors(tb, isatty(STDOUT_FILENO)); + setup_columns(tb); ln = add_line(tb, NULL, "A"); diff --git a/libsmartcols/scols-filter.5 b/libsmartcols/scols-filter.5 new file mode 100644 index 0000000..b7cb826 --- /dev/null +++ b/libsmartcols/scols-filter.5 @@ -0,0 +1,137 @@ +'\" t +.\" Title: scols-filter +.\" Author: [see the "AUTHOR(S)" section] +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-03-20 +.\" Manual: File formats and conventions +.\" Source: util-linux 2.40 +.\" Language: English +.\" +.TH "SCOLS\-FILTER" "5" "2024-03-20" "util\-linux 2.40" "File formats and conventions" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +scols-filter \- syntax for libsmartcols filter expressions +.SH "SYNTAX" +.sp +.if n .RS 4 +.nf +.fam C +expr: param + | ( expr ) + | expr && expr | expr AND expr + | expr || expr | expr OR expr + | !expr | NOT expr + | expr == expr | expr EQ expr + | expr != expr | expr NE expr + | expr >= expr | expr GE expr + | expr <= expr | expr LE expr + | expr > expr | expr GT expr + | expr < expr | expr LT expr + | expr =~ string + | expr !~ string + +param: integer + | float + | string + | boolean + | holder + +integer: [0\-9]* + +float: integer.integer + +boolean: "true" | "false" | "TRUE" | "FALSE" + +string: "[^\(rsn\(rs"]*" | \*(Aq[^\(rsn\(rs\*(Aq]*\*(Aq + +holder: [a\-zA\-Z][a\-zA\-Z_.%:/\(rs\-0\-9]* +.fam +.fi +.if n .RE +.SH "DESCRIPTION" +.sp +The filter expression can be used by application linked with libsmartcols to filter +output data. The application can use the filter before it gathers all data for the +output to reduce resources and improve performance. This makes scols filter more +effective than grep(1) on the complete output. For example +.sp +.if n .RS 4 +.nf +.fam C + lsblk \-\-output NAME,LABEL,FSTYPE \-\-filter \*(AqNAME=="sda1"\*(Aq +.fam +.fi +.if n .RE +.sp +helps lsblk(1) to not read LABELs for all block device from udevd or libblkid, +but read it only for device sda1. +.sp +The filter can be also used for columns which are not used in the output. +.SH "SYNTAX NOTES" +.sp +An expression consists of holders, params, and operators. +.sp +The currently supported \f(CRholder\fP type is column name only. The name has to be +used without quotes. Before evaluation, application map column names in the +given expression to the output table columns and assign column data type to the +holder. The default type is "string". +.sp +The \f(CRparam\fP is for representing a value directly. The currenly supported data +types are integer, float, string and boolean. +.sp +An operator works with one or two operand(s). An operator has an expectation +about the data type(s) of its operands. Giving an unexpected data type to an +operator causes a syntax error. The library can cast between data types, the +prefferred is always the type as specified by \f(CRparam\fP and in case of expression with +number and float the preferred is the float. +.sp +Operators taking two operands are \f(CRand\fP, \f(CRor\fP, \f(CReq\fP, \f(CRne\fP, \f(CRle\fP, \f(CRlt\fP, \f(CRge\fP, \f(CRgt\fP, \f(CR=~\fP, \f(CR!~\fP. +Alphabetically named operators have C\-language +flavored aliases: \f(CR&&\fP, \f(CR||\fP, \f(CR==\fP, \f(CR!=\fP, \f(CR<\fP, \f(CR\(lA\fP, \f(CR>=\fP, and \f(CR>\fP. +.sp +\f(CR!\fP is the only operator that takes one operand. If no operator is specified then +expression is true if param or holder are not empty. For example \f(CR\-\-filter NAME\fP will +return lines where column NAME is not empty. +.sp +\f(CR=~\fP and \f(CR!~\fP is for regular expression matching; if a string at the right side +matches (or not matches for \f(CR!~\fP a regular expression at the left side, the result +is true. The right side operand must be a string literal. +.sp +The precedences within operators is \f(CRor\fP, \f(CRand\fP, and \f(CReq\fP, \f(CRne\fP, \f(CRle\fP, \f(CRgt\fP, \f(CRge\fP, \f(CR=~\fP, \f(CR!~\fP, \f(CRnot\fP. +.SH "LIMITATIONS" +.sp +About \f(CRfloat\fP and \f(CRinteger\fP typed values, the filter engine supports only +non\-negative numbers. The \f(CRinteger\fP is unsigned 64\-bit number, and \f(CRfloat\fP is +long double. +.SH "AUTHORS" +.sp +.MTO "kzak\(atredhat.com" "Karel Zak" "" +.sp +Based on original implementation from \c +.MTO "yamato\(atredhat.com" "Masatake YAMATO" "." +.SH "REPORTING BUGS" +.sp +For bug reports, use the issue tracker at \c +.URL "https://github.com/util\-linux/util\-linux/issues" "" "." +.SH "AVAILABILITY" +.sp +The \fBlibsmartcols\fP library is part of the util\-linux package since version 2.25. It can be downloaded from \c +.URL "https://www.kernel.org/pub/linux/utils/util\-linux/" "Linux Kernel Archive" "."
\ No newline at end of file diff --git a/libsmartcols/scols-filter.5.adoc b/libsmartcols/scols-filter.5.adoc new file mode 100644 index 0000000..ec1d95c --- /dev/null +++ b/libsmartcols/scols-filter.5.adoc @@ -0,0 +1,121 @@ +//po4a: entry man manual +//// +Copyright (C) 2023 Karel Zak <kzak@redhat.com> + +This file may be copied under the terms of the GNU Public License. +//// += scols-filter(5) +:doctype: manpage +:man manual: File formats and conventions +:man source: util-linux {release-version} +:page-layout: base +:lib: libsmartcols +:firstversion: 2.25 +:colon: : + +== NAME + +scols-filter - syntax for libsmartcols filter expressions + +== SYNTAX +// Simplified https://en.wikipedia.org/wiki/Wirth_syntax_notation +// TRANSLATORS: don't translate the expressions syntax, please. +[verse] +---- +expr: param + | ( expr ) + | expr && expr | expr AND expr + | expr || expr | expr OR expr + | !expr | NOT expr + | expr == expr | expr EQ expr + | expr != expr | expr NE expr + | expr >= expr | expr GE expr + | expr <= expr | expr LE expr + | expr > expr | expr GT expr + | expr < expr | expr LT expr + | expr =~ string + | expr !~ string + +param: integer + | float + | string + | boolean + | holder + +integer: [0-9]* + +float: integer.integer + +boolean: "true" | "false" | "TRUE" | "FALSE" + +string: "[^\n\"]*" | '[^\n\']*' + +holder: [a-zA-Z][a-zA-Z_.%:/\-0-9]* +---- + +== DESCRIPTION + +The filter expression can be used by application linked with libsmartcols to filter +output data. The application can use the filter before it gathers all data for the +output to reduce resources and improve performance. This makes scols filter more +effective than grep(1) on the complete output. For example +.... + lsblk --output NAME,LABEL,FSTYPE --filter 'NAME=="sda1"' +.... +helps lsblk(1) to not read LABELs for all block device from udevd or libblkid, +but read it only for device sda1. + +The filter can be also used for columns which are not used in the output. + +== SYNTAX NOTES + +An expression consists of holders, params, and operators. + +The currently supported `holder` type is column name only. The name has to be +used without quotes. Before evaluation, application map column names in the +given expression to the output table columns and assign column data type to the +holder. The default type is "string". + +The `param` is for representing a value directly. The currenly supported data +types are integer, float, string and boolean. + +An operator works with one or two operand(s). An operator has an expectation +about the data type(s) of its operands. Giving an unexpected data type to an +operator causes a syntax error. The library can cast between data types, the +prefferred is always the type as specified by `param` and in case of expression with +number and float the preferred is the float. + +Operators taking two operands are `and`, `or`, `eq`, `ne`, `le`, `lt`, `ge`, `gt`, `=~`, `!~`. +Alphabetically named operators have C-language +flavored aliases: `&&`, `||`, `==`, `!=`, `<`, `<=`, `>=`, and `>`. + +`!` is the only operator that takes one operand. If no operator is specified then +expression is true if param or holder are not empty. For example `--filter NAME` will +return lines where column NAME is not empty. + +`=~` and `!~` is for regular expression matching; if a string at the right side +matches (or not matches for `!~` a regular expression at the left side, the result +is true. The right side operand must be a string literal. + +The precedences within operators is `or`, `and`, and `eq`, `ne`, `le`, `gt`, `ge`, `=~`, `!~`, `not`. + +== LIMITATIONS + +About `float` and `integer` typed values, the filter engine supports only +non-negative numbers. The `integer` is unsigned 64-bit number, and `float` is +long double. + + +== AUTHORS + +mailto:kzak@redhat.com[Karel Zak] + +Based on original implementation from mailto:yamato@redhat.com[Masatake YAMATO]. + +include::man-common/bugreports.adoc[] + +include::man-common/footer-lib.adoc[] + +ifdef::translation[] +include::man-common/translation.adoc[] +endif::[] diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am index 2bb19fd..377888e 100644 --- a/libsmartcols/src/Makemodule.am +++ b/libsmartcols/src/Makemodule.am @@ -1,5 +1,4 @@ - # smartcols.h is generated, so it's stored in builddir! smartcolsincdir = $(includedir)/libsmartcols nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h @@ -21,7 +20,68 @@ libsmartcols_la_SOURCES= \ libsmartcols/src/calculate.c \ libsmartcols/src/grouping.c \ libsmartcols/src/walk.c \ - libsmartcols/src/init.c + libsmartcols/src/init.c \ + \ + libsmartcols/src/filter-parser.c \ + libsmartcols/src/filter-scanner.c \ + libsmartcols/src/filter-parser.h \ + libsmartcols/src/filter-scanner.h \ + \ + libsmartcols/src/filter.c \ + libsmartcols/src/filter-param.c \ + libsmartcols/src/filter-expr.c + +BUILT_SOURCES += libsmartcols/src/filter-parser.c \ + libsmartcols/src/filter-parser.h \ + libsmartcols/src/filter-scanner.c \ + libsmartcols/src/filter-scanner.h + +EXTRA_DIST += libsmartcols/src/filter-parser.y \ + libsmartcols/src/filter-scanner.l + +## Generate filter parser (YACC) and tokenizer (LEX) .c and .h files. +# +# Note that we need advanced bison and flex features and configuration +# directives to generated thread safe parser (usable in stared library), so +# it's probably a bad idea to use "-y" (posix compatible) as default in +# autotools. We also need to generate .c and .h files in the same time to avoid +# duplicate stuff. +# +SCOLS_YACC_BASENAME = libsmartcols/src/filter-parser +SCOLS_YACC_STEMP = $(SCOLS_YACC_BASENAME).stamp + +SCOLS_LEX_BASENAME = libsmartcols/src/filter-scanner +SCOLS_LEX_STEMP = $(SCOLS_LEX_BASENAME).stamp + + +$(SCOLS_YACC_STEMP): $(SCOLS_YACC_BASENAME).y + @rm -f $(SCOLS_YACC_STEMP).tmp + @touch -f $(SCOLS_YACC_STEMP).tmp + $(AM_V_YACC) $(BISON) $< --output=${basename $@}.c --defines=${basename $@}.h + @mv -f $(SCOLS_YACC_STEMP).tmp $@ + +$(SCOLS_YACC_BASENAME).c $(SCOLS_YACC_BASENAME).h: $(SCOLS_YACC_STEMP) + @test -f $@ || rm -f $(SCOLS_YACC_STEMP) + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $(SCOLS_YACC_STEMP) + + +$(SCOLS_LEX_STEMP): $(SCOLS_LEX_BASENAME).l + @rm -f $(SCOLS_LEX_STEMP).tmp + @touch -f $(SCOLS_LEX_STEMP).tmp + $(AM_V_GEN) $(FLEX) --header-file=${basename $@}.h --outfile=${basename $@}.c $< + @mv -f $(SCOLS_LEX_STEMP).tmp $@ + +$(SCOLS_LEX_BASENAME).c $(SCOLS_LEX_BASENAME).h: $(SCOLS_LEX_STEMP) + @test -f $@ || rm -f $(SCOLS_LEX_STEMP) + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) $(SCOLS_LEX_STEMP) + +# Each part of the parser depends on the header files +$(SCOLS_LEX_BASENAME).c: $(SCOLS_YACC_BASENAME).h +$(SCOLS_YACC_BASENAME).c: $(SCOLS_LEX_BASENAME).h + +# Don't re-generate parser when use sources from tarball +EXTRA_DIST += $(SCOLS_YACC_STEMP) $(SCOLS_LEX_STEMP) + libsmartcols_la_LIBADD = $(LDADD) libcommon.la diff --git a/libsmartcols/src/calculate.c b/libsmartcols/src/calculate.c index ad0b15d..84198da 100644 --- a/libsmartcols/src/calculate.c +++ b/libsmartcols/src/calculate.c @@ -12,19 +12,19 @@ static void dbg_column(struct libscols_table *tb, struct libscols_column *cl) st = &cl->wstat; - DBG(COL, ul_debugobj(cl, "%15s seq=%zu, width=%zd, " - "hint=%d, max=%zu, min=%zu, " + DBG(COL, ul_debugobj(cl, "#%zu %12s: width=%zd " + "hint=%d max=%zu min=%zu " "0x04%x [%s%s%s]", - cl->header.data, cl->seqnum, cl->width, + cl->seqnum, cl->header.data, cl->width, cl->width_hint >= 1.0 ? (int) cl->width_hint : (int) (cl->width_hint * tb->termwidth), st->width_max, st->width_min, cl->flags, - cl->flags & SCOLS_FL_TRUNC ? "trunc" : "", - scols_column_is_right(cl) ? " right" : "", - scols_column_is_noextremes(cl) ? " noextrem" : "")); + cl->flags & SCOLS_FL_TRUNC ? "trunc " : "", + scols_column_is_right(cl) ? "right " : "", + scols_column_is_noextremes(cl) ? "noextrem " : "")); } static void dbg_columns(struct libscols_table *tb) @@ -42,41 +42,38 @@ static int count_cell_width(struct libscols_table *tb, struct libscols_column *cl, struct ul_buffer *buf) { - size_t len; - char *data; - int rc; + size_t len = 0; + int rc = 0; struct libscols_cell *ce; - struct libscols_wstat *st; + char *data; - rc = __cell_to_buffer(tb, ln, cl, buf); + ce = scols_line_get_cell(ln, cl->seqnum); + scols_table_set_cursor(tb, ln, cl, ce); + + rc = __cursor_to_buffer(tb, buf, 1); if (rc) - return rc; + goto done; data = ul_buffer_get_data(buf, NULL, NULL); - if (!data) - len = 0; - else if (scols_column_is_customwrap(cl)) - len = cl->wrap_chunksize(cl, data, cl->wrapfunc_data); - else if (scols_table_is_noencoding(tb)) - len = mbs_width(data); - else - len = mbs_safe_width(data); - - if (len == (size_t) -1) /* ignore broken multibyte strings */ - len = 0; - - ce = scols_line_get_cell(ln, cl->seqnum); - ce->width = len; + if (data) { + len = scols_table_is_noencoding(tb) ? + mbs_width(data) : + mbs_safe_width(data); - st = &cl->wstat; - st->width_max = max(len, st->width_max); + if (len == (size_t) -1) /* ignore broken multibyte strings */ + len = 0; + } if (scols_column_is_tree(cl)) { size_t treewidth = ul_buffer_get_safe_pointer_width(buf, SCOLS_BUFPTR_TREEEND); cl->width_treeart = max(cl->width_treeart, treewidth); } - return 0; + ce->width = len; + cl->wstat.width_max = max(len, cl->wstat.width_max); +done: + scols_table_reset_cursor(tb); + return rc; } static int walk_count_cell_width(struct libscols_table *tb, @@ -121,7 +118,7 @@ static void count_column_deviation(struct libscols_table *tb, struct libscols_co } if (n) - st->width_avg = sum / n; + st->width_avg = (double) sum / (double) n; /* count deviation */ if (n > 1) { @@ -136,7 +133,7 @@ static void count_column_deviation(struct libscols_table *tb, struct libscols_co st->width_sqr_sum += diff * diff; /* aka pow(x, 2) */ } - variance = st->width_sqr_sum / (n - 1); + variance = st->width_sqr_sum / (double) (n - 1); st->width_deviation = sqrtroot(variance); } @@ -175,8 +172,9 @@ static int count_column_width(struct libscols_table *tb, } /* set minimal width according to header width */ - data = scols_cell_get_data(&cl->header); - if (data) { + if (!scols_table_is_noheadings(tb) && + (data = scols_cell_get_data(&cl->header))) { + size_t len = scols_table_is_noencoding(tb) ? mbs_width(data) : mbs_safe_width(data); @@ -242,8 +240,8 @@ static int cmp_deviation(struct list_head *a, struct list_head *b, struct libscols_column *ca = list_entry(a, struct libscols_column, cl_columns); struct libscols_column *cb = list_entry(b, struct libscols_column, cl_columns); - double xa = ca->wstat.width_avg + (3*ca->wstat.width_deviation); - double xb = cb->wstat.width_avg + (3*cb->wstat.width_deviation); + double xa = ca->wstat.width_avg + (3.0 * ca->wstat.width_deviation); + double xb = cb->wstat.width_avg + (3.0 * cb->wstat.width_deviation); return cmp_numbers(xa, xb); } @@ -282,9 +280,15 @@ static void reduce_to_68(struct libscols_column *cl, size_t wanted) if (st->width_deviation < 1.0) return; - new = st->width_avg + st->width_deviation; + new = (size_t) (st->width_avg + st->width_deviation); + if (new < st->width_min) new = st->width_min; + else if (new > st->width_max) + new = st->width_max; + + if (new >= cl->width) + return; if (cl->width - new > wanted) cl->width -= wanted; @@ -509,6 +513,8 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) /* enlarge */ if (width < tb->termwidth) { + DBG(TAB, ul_debugobj(tb, " enlarge (extreme, avalable %zu)", + tb->termwidth - width)); if (ignore_extremes) { if (!sorted) { sort_columns(tb, cmp_deviation); @@ -523,14 +529,17 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) continue; if (cl->wstat.width_min == 0 && cl->width == 0) continue; + if (cl->width >= cl->wstat.width_max) + continue; add = tb->termwidth - width; - if (add && cl->wstat.width_max && - cl->width + add > cl->wstat.width_max) + if (add && cl->wstat.width_max + && cl->width + add > cl->wstat.width_max) add = cl->wstat.width_max - cl->width; + if (!add) continue; - DBG(TAB, ul_debugobj(tb, " add +%zd (extreme %s)", + DBG(COL, ul_debugobj(cl, " add +%zd (%s)", add, cl->header.data)); cl->width += add; width += add; @@ -541,7 +550,8 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) } if (width < tb->termwidth && scols_table_is_maxout(tb)) { - DBG(TAB, ul_debugobj(tb, " enlarge width (max-out)")); + DBG(TAB, ul_debugobj(tb, " enlarge (max-out, avalable %zu)", + tb->termwidth - width)); /* try enlarging all columns */ while (width < tb->termwidth) { @@ -549,7 +559,7 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) while (scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - DBG(TAB, ul_debugobj(tb, " enlarge (max-out %s)", + DBG(COL, ul_debugobj(cl, " add +1 (%s)", cl->header.data)); cl->width++; width++; @@ -559,9 +569,13 @@ int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf) } } else if (width < tb->termwidth) { /* enlarge the last column */ - DBG(TAB, ul_debugobj(tb, " enlarge width (last column)")); + DBG(TAB, ul_debugobj(tb, " enlarge (last column, avalable %zu)", + tb->termwidth - width)); if (!scols_column_is_right(last_cl)) { + DBG(COL, ul_debugobj(last_cl, " add +%zu (%s)", + tb->termwidth - width, + last_cl->header.data)); last_cl->width += tb->termwidth - width; width = tb->termwidth; } diff --git a/libsmartcols/src/cell.c b/libsmartcols/src/cell.c index 5b83123..df45667 100644 --- a/libsmartcols/src/cell.c +++ b/libsmartcols/src/cell.c @@ -56,24 +56,32 @@ int scols_reset_cell(struct libscols_cell *ce) * @ce: a pointer to a struct libscols_cell instance * @data: data (used for scols_print_table()) * - * Stores a copy of the @str in @ce, the old data are deallocated by free(). + * Stores a copy of the @data in @ce, the old data are deallocated by free(). * * Returns: 0, a negative value in case of an error. */ int scols_cell_set_data(struct libscols_cell *ce, const char *data) { - return strdup_to_struct_member(ce, data, data); + int rc; + + if (!ce) + return -EINVAL; + + ce->is_filled = 1; + rc = strdup_to_struct_member(ce, data, data); + ce->datasiz = ce->data && *ce->data ? strlen(ce->data) + 1: 0; + return rc; } /** * scols_cell_refer_data: * @ce: a pointer to a struct libscols_cell instance - * @data: data (used for scols_print_table()) + * @data: string (used for scols_print_table()) * - * Adds a reference to @str to @ce. The pointer is deallocated by - * scols_reset_cell() or scols_unref_line(). This function is mostly designed - * for situations when the data for the cell are already composed in allocated - * memory (e.g. asprintf()) to avoid extra unnecessary strdup(). + * Adds a reference to @data to @ce. The pointer is deallocated by + * scols_reset_cell() or scols_unref_line() by free(). This function is mostly + * designed for situations when the data for the cell are already composed in + * allocated memory (e.g. asprintf()) to avoid extra unnecessary strdup(). * * Returns: 0, a negative value in case of an error. */ @@ -83,10 +91,52 @@ int scols_cell_refer_data(struct libscols_cell *ce, char *data) return -EINVAL; free(ce->data); ce->data = data; + ce->datasiz = ce->data && *ce->data ? strlen(ce->data) + 1: 0; + ce->is_filled = 1; return 0; } /** + * scols_cell_refer_memory: + * @ce: a pointer to a struct libscols_cell instance + * @data: data + * @datasiz: size of the data + * + * Same like scols_cell_refer_data, but @data does not have to be zero terminated. + * The pointer is deallocated by scols_reset_cell() or scols_unref_line() by free(). + * + * The column (for the cell) has to define wrap function which converts the + * data to zero terminated string, otherwise library will work with the data as + * with string! + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_cell_refer_memory(struct libscols_cell *ce, char *data, size_t datasiz) +{ + if (!ce) + return -EINVAL; + free(ce->data); + ce->data = data; + ce->datasiz = datasiz; + return 0; +} + +/** + * scols_cell_get_datasiz: + * @ce: a pointer to a struct libscols_cell instance + * + * Returns: the current set data size. + * + * Since: 2.40 + */ +size_t scols_cell_get_datasiz(struct libscols_cell *ce) +{ + return ce ? ce->datasiz : 0; +} + +/** * scols_cell_get_data: * @ce: a pointer to a struct libscols_cell instance * @@ -120,7 +170,7 @@ int scols_cell_set_userdata(struct libscols_cell *ce, void *data) */ void *scols_cell_get_userdata(struct libscols_cell *ce) { - return ce->userdata; + return ce ? ce->userdata : NULL; } /** @@ -166,6 +216,9 @@ int scols_cmpstr_cells(struct libscols_cell *a, */ int scols_cell_set_color(struct libscols_cell *ce, const char *color) { + if (!ce) + return -EINVAL; + if (color && !color_is_sequence(color)) { char *seq = color_get_sequence(color); if (!seq) @@ -185,6 +238,9 @@ int scols_cell_set_color(struct libscols_cell *ce, const char *color) */ const char *scols_cell_get_color(const struct libscols_cell *ce) { + if (!ce) + return NULL; + return ce->color; } @@ -214,7 +270,7 @@ int scols_cell_set_flags(struct libscols_cell *ce, int flags) */ int scols_cell_get_flags(const struct libscols_cell *ce) { - return ce->flags; + return ce ? ce->flags : 0; } /** @@ -227,9 +283,11 @@ int scols_cell_get_flags(const struct libscols_cell *ce) */ int scols_cell_get_alignment(const struct libscols_cell *ce) { - if (ce->flags & SCOLS_CELL_FL_RIGHT) + int flags = scols_cell_get_flags(ce); + + if (flags & SCOLS_CELL_FL_RIGHT) return SCOLS_CELL_FL_RIGHT; - if (ce->flags & SCOLS_CELL_FL_CENTER) + if (flags & SCOLS_CELL_FL_CENTER) return SCOLS_CELL_FL_CENTER; return SCOLS_CELL_FL_LEFT; /* default */ @@ -240,7 +298,7 @@ int scols_cell_get_alignment(const struct libscols_cell *ce) * @dest: a pointer to a struct libscols_cell instance * @src: a pointer to an immutable struct libscols_cell instance * - * Copy the contents of @src into @dest. + * Copy the contents (data, usewrdata, colors) of @src into @dest. * * Returns: 0, a negative value in case of an error. */ @@ -248,8 +306,19 @@ int scols_cell_copy_content(struct libscols_cell *dest, const struct libscols_cell *src) { int rc; + char *data = NULL; + + if (!dest || !src) + return -EINVAL; + + if (src->datasiz) { + data = malloc(src->datasiz); + if (!data) + return -ENOMEM; + memcpy(data, src->data, src->datasiz); + } - rc = scols_cell_set_data(dest, scols_cell_get_data(src)); + rc = scols_cell_refer_memory(dest, data, src->datasiz); if (!rc) rc = scols_cell_set_color(dest, scols_cell_get_color(src)); if (!rc) diff --git a/libsmartcols/src/column.c b/libsmartcols/src/column.c index db4c357..5700bac 100644 --- a/libsmartcols/src/column.c +++ b/libsmartcols/src/column.c @@ -73,7 +73,7 @@ void scols_unref_column(struct libscols_column *cl) scols_reset_cell(&cl->header); free(cl->color); free(cl->safechars); - free(cl->pending_data_buf); + free(cl->wrap_data); free(cl->shellvar); free(cl); } @@ -211,6 +211,42 @@ int scols_column_get_json_type(const struct libscols_column *cl) /** + * scols_column_set_data_type: + * @cl: a pointer to a struct libscols_column instance + * @type: SCOLS_DATA_* + * + * The table always keep data in strings in form that is printed on output, but + * for some internal operations (like filters or counters) it needs to convert + * the strings to usable data format. This data format is possible to specify, + * by this function. If the format is not specified then filter and counters + * try to use SCOLS_JSON_* types, if also not define than defaults to string. + * + * If a simple string conversion is not possible then application (which want + * to use filters and counters) needs to define data function to do the + * conversion. See scols_column_set_data_func(). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_column_set_data_type(struct libscols_column *cl, int type) +{ + return cl->data_type = type; +} + +/** + * scols_column_get_data_type: + * @cl: a pointer to a struct libscols_column instance + * + * Returns: The current datatype setting of the column @cl. + * + * Since: 2.40 + */ +int scols_column_get_data_type(const struct libscols_column *cl) +{ + return cl->data_type; +} +/** * scols_column_get_table: * @cl: a pointer to a struct libscols_column instance * @@ -280,6 +316,59 @@ const char *scols_column_get_name(struct libscols_column *cl) } /** + * scols_shellvar_name: + * @name: raw (column) name + * @buf: buffer to returns normalized name + * @bufsz: size of the buffer + * + * Converts @name to a name compatible with shell. The buffer is reallocated if + * not large enough. + * + * Returns: 0 in case of conversion, 1 if conversion unnecessary, <0 on error. + * + * Since: 2.40 + */ +int scols_shellvar_name(const char *name, char **buf, size_t *bufsz) +{ + char *p; + const char *s; + size_t sz; + + if (!name || !*name || !buf || !bufsz) + return -EINVAL; + + /* size to convert "1FOO%" --> "_1FOO_PCT */ + sz = strlen(name) + 1 + 3; + if (sz + 1 > *bufsz) { + char *tmp; + + *bufsz = sz + 1; + tmp = realloc(*buf, *bufsz); + if (!tmp) + return -ENOMEM; + *buf = tmp; + } + memset(*buf, 0, *bufsz); + p = *buf; + + /* convert "1FOO" to "_1FOO" */ + if (!isalpha(*name)) + *p++ = '_'; + + /* replace all "bad" chars with "_" */ + for (s = name; *s; s++) + *p++ = !isalnum(*s) ? '_' : *s; + + if (!*s && *(s - 1) == '%') { + *p++ = 'P'; + *p++ = 'C'; + *p++ = 'T'; + } + + return strcmp(name, *buf) == 0; +} + +/** * scols_column_get_name_as_shellvar * @cl: a pointer to a struct libscols_column instance * @@ -291,32 +380,13 @@ const char *scols_column_get_name(struct libscols_column *cl) const char *scols_column_get_name_as_shellvar(struct libscols_column *cl) { if (!cl->shellvar) { - const char *s, *name = scols_column_get_name(cl); - char *p; - size_t sz; + const char *name = scols_column_get_name(cl); + size_t sz = 0; if (!name || !*name) return NULL; - - /* "1FOO%" --> "_1FOO_PCT */ - sz = strlen(name) + 1 + 3; - p = cl->shellvar = calloc(1, sz + 1); - if (!cl->shellvar) + if (scols_shellvar_name(name, &cl->shellvar, &sz) < 0) return NULL; - - /* convert "1FOO" to "_1FOO" */ - if (!isalpha(*name)) - *p++ = '_'; - - /* replace all "bad" chars with "_" */ - for (s = name; *s; s++) - *p++ = !isalnum(*s) ? '_' : *s; - - if (!*s && *(s - 1) == '%') { - *p++ = 'P'; - *p++ = 'C'; - *p++ = 'T'; - } } return cl->shellvar; } @@ -361,6 +431,7 @@ const char *scols_column_get_color(const struct libscols_column *cl) return cl->color; } + /** * scols_wrapnl_nextchunk: * @cl: a pointer to a struct libscols_column instance @@ -391,6 +462,37 @@ char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unu } /** + * scols_wrapzero_nextchunk: + * @cl: a pointer to a struct libscols_column instance + * @data: string + * @userdata: callback private data + * + * This is built-in function for scols_column_set_wrapfunc(). This function + * walk string separated by \0. + * + * For example for data "AAA\0BBB\0CCC" the next chunk is "BBB". + * + * Returns: next chunk + * + * Since: 2.40 + */ +char *scols_wrapzero_nextchunk(const struct libscols_column *cl, + char *data, + void *userdata __attribute__((unused))) +{ + char *start = NULL; + size_t sz = 0; + + if (!data) + return NULL; + scols_column_get_wrap_data(cl, &start, &sz, NULL, NULL); + if (!start || !sz) + return NULL; + return ul_next_string(data, start + sz); +} + + +/** * scols_wrapnl_chunksize: * @cl: a pointer to a struct libscols_column instance * @data: string @@ -402,6 +504,8 @@ char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unu * Note that the size has to be based on number of terminal cells rather than * bytes to support multu-byte output. * + * Deprecated since 2.40. + * * Returns: size of the largest chunk. * * Since: 2.29 @@ -459,7 +563,7 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, /** * scols_column_set_wrapfunc: * @cl: a pointer to a struct libscols_column instance - * @wrap_chunksize: function to return size of the largest chink of data + * @wrap_chunksize: function to return size of the largest chink of data (deprecated) * @wrap_nextchunk: function to return next zero terminated data * @userdata: optional stuff for callbacks * @@ -467,6 +571,13 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, * is to wrap by column size, but you can create functions to wrap for example * after \n or after words, etc. * + * Note that since 2.40 the @wrap_chunksize is unnecessary. The library calculates + * the size itself. + * + * The wrap functions do not work directly with cell data, but with buffer used + * by library to compose output data. The wrap_nextchunk() function can access + * additional details about wrap data by scols_column_get_wrap_data(). + * * Returns: 0, a negative value in case of an error. * * Since: 2.29 @@ -474,7 +585,7 @@ int scols_column_set_cmpfunc(struct libscols_column *cl, int scols_column_set_wrapfunc(struct libscols_column *cl, size_t (*wrap_chunksize)(const struct libscols_column *, const char *, - void *), + void *) __attribute__((__unused__)), char * (*wrap_nextchunk)(const struct libscols_column *, char *, void *), @@ -484,12 +595,88 @@ int scols_column_set_wrapfunc(struct libscols_column *cl, return -EINVAL; cl->wrap_nextchunk = wrap_nextchunk; - cl->wrap_chunksize = wrap_chunksize; cl->wrapfunc_data = userdata; return 0; } /** + * scols_column_get_wrap_data: + * @cl: column + * @data: return wrap data + * @datasiz: return wrap buffer size + * @cur: the current pozition in the buffer + * @next: the next pozition + * + * This function returns the current status of wrapping cell data (for multi-line cells). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_column_get_wrap_data(const struct libscols_column *cl, + char **data, size_t *datasiz, char **cur, char **next) +{ + if (!cl) + return -EINVAL; + if (data) + *data = cl->wrap_data; + if (datasiz) + *datasiz = cl->wrap_datasz; + if (cur) + *cur = cl->wrap_cur; + if (next) + *next = cl->wrap_next; + return 0; +} + +/* + * scols_column_set_data_func: + * @cl: a pointer to a struct libscols_column instance + * @datafunc: function to return data + * @userdata: optional stuff for callbacks + * + * The table always keep data in strings in form that is printed on output, but + * for some internal operations (like filters or counters) it needs to convert + * the strings to usable data format. If this converion is not possible then + * application can define datafunc() callback to provide data for filters and counters. + + * The callback needs to return the data as pointer to void, and the data type + * is defined by scols_column_set_data_type(). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_column_set_data_func(struct libscols_column *cl, + void *(*datafunc)(const struct libscols_column *, + struct libscols_cell *, + void *), + void *userdata) +{ + if (!cl) + return -EINVAL; + + cl->datafunc = datafunc; + cl->datafunc_data = userdata; + return 0; +} + +/** + * scols_column_has_data_func: + * @cl: a pointer to a struct libscols_column instance + * + * See scols_column_set_data_func() for more details. + * + * Returns: 1 if data function defined, or 0 + * + * Since: 2.40 + */ +int scols_column_has_data_func(struct libscols_column *cl) +{ + return cl && cl->datafunc != NULL ? 1 : 0; +} + +/** * scols_column_set_safechars: * @cl: a pointer to a struct libscols_column instance * @safe: safe characters (e.g. "\n\t") @@ -639,7 +826,6 @@ int scols_column_is_wrap(const struct libscols_column *cl) int scols_column_is_customwrap(const struct libscols_column *cl) { return (cl->flags & SCOLS_FL_WRAP) - && cl->wrap_chunksize && cl->wrap_nextchunk ? 1 : 0; } @@ -681,7 +867,7 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) flags |= SCOLS_FL_STRICTWIDTH; else if (strncmp(name, "noextremes", namesz) == 0) - flags |= SCOLS_FL_STRICTWIDTH; + flags |= SCOLS_FL_NOEXTREMES; else if (strncmp(name, "hidden", namesz) == 0) flags |= SCOLS_FL_HIDDEN; @@ -689,12 +875,29 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) else if (strncmp(name, "wrap", namesz) == 0) flags |= SCOLS_FL_WRAP; - else if (value && strncmp(name, "json", namesz) == 0) { + else if (strncmp(name, "wrapnl", namesz) == 0) { + flags |= SCOLS_FL_WRAP; + scols_column_set_wrapfunc(cl, + NULL, + scols_wrapnl_nextchunk, + NULL); + scols_column_set_safechars(cl, "\n"); + + } else if (strncmp(name, "wrapzero", namesz) == 0) { + flags |= SCOLS_FL_WRAP; + scols_column_set_wrapfunc(cl, + NULL, + scols_wrapzero_nextchunk, + NULL); + + } else if (value && strncmp(name, "json", namesz) == 0) { if (strncmp(value, "string", valuesz) == 0) rc = scols_column_set_json_type(cl, SCOLS_JSON_STRING); else if (strncmp(value, "number", valuesz) == 0) rc = scols_column_set_json_type(cl, SCOLS_JSON_NUMBER); + else if (strncmp(value, "float", valuesz) == 0) + rc = scols_column_set_json_type(cl, SCOLS_JSON_FLOAT); else if (strncmp(value, "array-string", valuesz) == 0) rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING); else if (strncmp(value, "array-number", valuesz) == 0) @@ -706,9 +909,8 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) char *end = NULL; double x = strtod(value, &end); - if (errno || str == end) + if (errno || value == end) return -EINVAL; - rc = scols_column_set_whint(cl, x); } else if (value && strncmp(name, "color", namesz) == 0) { @@ -735,3 +937,122 @@ int scols_column_set_properties(struct libscols_column *cl, const char *opts) return rc; } +void scols_column_reset_wrap(struct libscols_column *cl) +{ + if (!cl) + return; + + if (cl->wrap_data) + memset(cl->wrap_data, 0, cl->wrap_datamax); + cl->wrap_cell = NULL; + cl->wrap_datasz = 0; + cl->wrap_cur = NULL; + cl->wrap_next = NULL; +} + +static int scols_column_init_wrap( + struct libscols_column *cl, + struct libscols_cell *ce) +{ + const char *data = scols_cell_get_data(ce); + + if (!cl || !ce) + return -EINVAL; + + assert(cl->table->cur_column == cl); + assert(cl->table->cur_cell == ce); + + scols_column_reset_wrap(cl); + + cl->wrap_cell = ce; + if (data) { + void *tmp; + cl->wrap_datasz = scols_cell_get_datasiz(ce); + + if (cl->wrap_datasz > cl->wrap_datamax) { + cl->wrap_datamax = cl->wrap_datasz; + tmp = realloc(cl->wrap_data, cl->wrap_datamax); + if (!tmp) + return -ENOMEM; + cl->wrap_data = tmp; + } + memcpy(cl->wrap_data, data, cl->wrap_datasz); + cl->wrap_cur = cl->wrap_data; + cl->wrap_next = NULL; + } + + return 0; +} + +/* Returns the next chunk of cell data in multi-line cells */ +int scols_column_next_wrap( + struct libscols_column *cl, + struct libscols_cell *ce, + char **data) +{ + if (!cl || !data || (!cl->wrap_cell && !ce)) + return -EINVAL; + + *data = NULL; + + if (ce && cl->wrap_cell != ce) + scols_column_init_wrap(cl, ce); /* init */ + else { + cl->wrap_cur = cl->wrap_next; /* next step */ + cl->wrap_next = NULL; + } + + if (!cl->wrap_cur) + return 1; /* no more data */ + if (scols_column_is_customwrap(cl)) + cl->wrap_next = cl->wrap_nextchunk(cl, cl->wrap_cur, cl->wrapfunc_data); + + *data = cl->wrap_cur; + return 0; +} + +int scols_column_greatest_wrap( + struct libscols_column *cl, + struct libscols_cell *ce, + char **data) +{ + size_t maxsz = 0; + char *res = NULL;; + + if (!scols_column_is_customwrap(cl)) + return scols_column_next_wrap(cl, ce, data); + + while (scols_column_next_wrap(cl, ce, data) == 0) { + size_t sz = strlen(*data); + + maxsz = max(maxsz, sz); + if (maxsz == sz) + res = *data; + } + + *data = res; + return 0; +} + +/* Set the "next" chunk in multi-line cell to offset specified by @bytes. + * Don't use it for columns with custom wrapfunc(). + */ +int scols_column_move_wrap(struct libscols_column *cl, size_t bytes) +{ + size_t x; /* remaining bytes */ + + if (!cl->wrap_cur) + return -EINVAL; /* scols_column_init_wrap() not called */ + + x = cl->wrap_datasz - (cl->wrap_cur - cl->wrap_data); + if (bytes >= x) + cl->wrap_next = NULL; /* done */ + else + cl->wrap_next = cl->wrap_cur + bytes; + return 0; +} + +int scols_column_has_pending_wrap(struct libscols_column *cl) +{ + return cl && cl->wrap_next; +} diff --git a/libsmartcols/src/filter-expr.c b/libsmartcols/src/filter-expr.c new file mode 100644 index 0000000..7e559c4 --- /dev/null +++ b/libsmartcols/src/filter-expr.c @@ -0,0 +1,219 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "smartcolsP.h" + +struct filter_expr { + struct filter_node node; + enum filter_etype type; + + struct filter_node *left; + struct filter_node *right; +}; + +struct filter_node *filter_new_expr( + struct libscols_filter *fltr __attribute__((__unused__)), + enum filter_etype type, + struct filter_node *left, + struct filter_node *right) +{ + struct filter_expr *n = (struct filter_expr *) __filter_new_node( + F_NODE_EXPR, sizeof(struct filter_expr)); + + if (!n) + return NULL; + + n->type = type; + + switch (type) { + case F_EXPR_AND: + case F_EXPR_OR: + case F_EXPR_EQ: + case F_EXPR_NE: + case F_EXPR_LE: + case F_EXPR_LT: + case F_EXPR_GE: + case F_EXPR_GT: + case F_EXPR_REG: + case F_EXPR_NREG: + n->left = left; + n->right = right; + break; + case F_EXPR_NEG: + n->right = right; + break; + } + + return (struct filter_node *) n; +} + +void filter_free_expr(struct filter_expr *n) +{ + filter_unref_node(n->left); + filter_unref_node(n->right); + free(n); +} + +static const char *expr_type_as_string(struct filter_expr *n) +{ + switch (n->type) { + case F_EXPR_AND: + return "AND"; + case F_EXPR_OR: + return "OR"; + case F_EXPR_EQ: + return "EQ"; + case F_EXPR_NE: + return "NE"; + case F_EXPR_LE: + return "LE"; + case F_EXPR_LT: + return "LT"; + case F_EXPR_GE: + return "GE"; + case F_EXPR_GT: + return "GT"; + case F_EXPR_REG: + return "REG"; + case F_EXPR_NREG: + return "NREG"; + case F_EXPR_NEG: + return "NOT"; + } + return ""; +} + +void filter_dump_expr(struct ul_jsonwrt *json, struct filter_expr *n) +{ + ul_jsonwrt_object_open(json, "expr"); + ul_jsonwrt_value_s(json, "type", expr_type_as_string(n)); + + if (n->left) + filter_dump_node(json, n->left); + if (n->right) + filter_dump_node(json, n->right); + + ul_jsonwrt_object_close(json); +} + +static int cast_node(struct libscols_filter *fltr, + struct libscols_line *ln, + int type, + struct filter_node *n, + struct filter_param **result) +{ + struct filter_node *pr; + int status = 0, rc; + bool x; + + switch (n->type) { + case F_NODE_EXPR: + /* convert expression to a boolean param */ + rc = filter_eval_expr(fltr, ln, (struct filter_expr *) n, &status); + if (rc) + return rc; + x = status != 0 ? true : false; + pr = filter_new_param(NULL, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + if (!pr) + return -ENOMEM; + rc = filter_cast_param(fltr, ln, type, (struct filter_param *) pr, result); + filter_unref_node(pr); + break; + case F_NODE_PARAM: + rc = filter_cast_param(fltr, ln, type, (struct filter_param *) n, result); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int node_get_datatype(struct filter_node *n) +{ + switch (n->type) { + case F_NODE_EXPR: + return SCOLS_DATA_BOOLEAN; + case F_NODE_PARAM: + return filter_param_get_datatype((struct filter_param *) n); + } + return SCOLS_DATA_NONE; +} + +static int guess_expr_datatype(struct filter_expr *n) +{ + int type; + int l = node_get_datatype(n->left), + r = node_get_datatype(n->right); + + if (l == r) + type = l; + else { + bool l_holder, r_holder; + + /* for expression like "FOO > 5.5" preffer type defined by a real param + * rather than by holder (FOO) */ + l_holder = is_filter_holder_node(n->left); + r_holder = is_filter_holder_node(n->right); + + if (l_holder && !r_holder) + type = r; + else if (r_holder && !l_holder) + type = l; + else + type = l; + + /* Always prefer float before number */ + if (type == SCOLS_DATA_U64 + && (r == SCOLS_DATA_FLOAT || l == SCOLS_DATA_FLOAT)) + type = SCOLS_DATA_FLOAT; + } + + DBG(FPARAM, ul_debugobj(n, " expr datatype: %d", type)); + return type; +} + +int filter_eval_expr(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_expr *n, int *status) +{ + int rc = 0; + struct filter_param *l = NULL, *r = NULL; + enum filter_etype oper = n->type; + int type; + + /* logical operators */ + switch (oper) { + case F_EXPR_AND: + rc = filter_eval_node(fltr, ln, n->left, status); + if (rc == 0 && *status) + rc = filter_eval_node(fltr, ln, n->right, status); + return rc; + case F_EXPR_OR: + rc = filter_eval_node(fltr, ln, n->left, status); + if (rc == 0 && !*status) + rc = filter_eval_node(fltr, ln, n->right, status); + return rc; + case F_EXPR_NEG: + rc = filter_eval_node(fltr, ln, n->right, status); + if (rc == 0) + *status = !*status; + return rc; + default: + break; + } + + type = guess_expr_datatype(n); + + /* compare data */ + rc = cast_node(fltr, ln, type, n->left, &l); + if (!rc) + rc = cast_node(fltr, ln, type, n->right, &r); + if (!rc) + rc = filter_compare_params(fltr, oper, l, r, status); + + filter_unref_node((struct filter_node *) l); + filter_unref_node((struct filter_node *) r); + return rc; +} diff --git a/libsmartcols/src/filter-param.c b/libsmartcols/src/filter-param.c new file mode 100644 index 0000000..f7d243d --- /dev/null +++ b/libsmartcols/src/filter-param.c @@ -0,0 +1,889 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <regex.h> + +#include "rpmatch.h" +#include "smartcolsP.h" + +struct filter_param { + struct filter_node node; + int type; + enum filter_holder holder; + + union { + char *str; + unsigned long long num; + long double fnum; + bool boolean; + } val; + + struct list_head pr_params; + struct libscols_column *col; + char *holder_name; + regex_t *re; + + unsigned int fetched :1, /* holder requested */ + empty : 1; +}; + +static int cast_param(int type, struct filter_param *n); + +static inline const char *datatype2str(int type) +{ + static const char *types[] = { + [SCOLS_DATA_NONE] = "none", + [SCOLS_DATA_STRING] = "string", + [SCOLS_DATA_U64] = "u64", + [SCOLS_DATA_FLOAT] = "float", + [SCOLS_DATA_BOOLEAN] = "boolean" + }; + return types[type]; +} + +static char *rem_quotation(const char *p, int c) +{ + size_t len = strlen(p); + + if (*(p + (len - 1)) == c) + len -= 2; + return strndup(p + 1, len); +} + +static int param_set_data(struct filter_param *n, int type, const void *data) +{ + const char *p; + + /*DBG(FPARAM, ul_debugobj(n, " set %s data", datatype2str(type)));*/ + + switch (type) { + case SCOLS_DATA_STRING: + p = data; + if (p && (*p == '"' || *p == '\'')) + n->val.str = rem_quotation(p, *p); + else if (data) + n->val.str = strdup((char *) data); + if (data && !n->val.str) + return -ENOMEM; + if (data) { + rtrim_whitespace((unsigned char *) n->val.str); + ltrim_whitespace((unsigned char *) n->val.str); + } + break; + case SCOLS_DATA_U64: + n->val.num = data ? *((unsigned long long *) data) : 0; + break; + case SCOLS_DATA_FLOAT: + n->val.fnum = data ? *((long double *) data) : 0; + break; + case SCOLS_DATA_BOOLEAN: + n->val.boolean = data ? (*((bool *) data) == 0 ? 0 : 1) : 0; + break; + default: + return 0; + } + + n->type = type; + n->empty = data == NULL; + return 0; +} + +struct filter_node *filter_new_param( + struct libscols_filter *fltr, + int type, + enum filter_holder holder, + void *data) +{ + struct filter_param *n = (struct filter_param *) __filter_new_node( + F_NODE_PARAM, + sizeof(struct filter_param)); + if (!n) + return NULL; + + n->type = type; + n->holder = holder; + INIT_LIST_HEAD(&n->pr_params); + + if (param_set_data(n, type, data) != 0) { + filter_free_param(n); + return NULL; + } + + if (holder == F_HOLDER_COLUMN) { + n->holder_name = strdup((char *) data); + DBG(FLTR, ul_debugobj(fltr, "new %s holder", n->holder_name)); + } + + if (fltr) + list_add_tail(&n->pr_params, &fltr->params); + + return (struct filter_node *) n; +} + +int filter_compile_param(struct libscols_filter *fltr, struct filter_param *n) +{ + int rc; + + if (n->re) + return 0; + if (!n->val.str) + return -EINVAL; + + n->re = calloc(1, sizeof(regex_t)); + if (!n->re) + return -ENOMEM; + + rc = regcomp(n->re, n->val.str, REG_NOSUB | REG_EXTENDED); + if (rc) { + size_t size = regerror(rc, n->re, NULL, 0); + + fltr->errmsg = malloc(size + 1); + if (!fltr->errmsg) + return -ENOMEM; + regerror(rc, n->re, fltr->errmsg, size); + return -EINVAL; + } + return 0; +} + +static struct filter_param *copy_param(struct filter_param *n) +{ + void *data = NULL; + + switch (n->type) { + case SCOLS_DATA_STRING: + data = n->val.str; + break; + case SCOLS_DATA_U64: + data = &n->val.num; + break; + case SCOLS_DATA_FLOAT: + data = &n->val.fnum; + break; + case SCOLS_DATA_BOOLEAN: + data = &n->val.boolean; + break; + } + + DBG(FPARAM, ul_debugobj(n, "copying")); + return (struct filter_param *) filter_new_param(NULL, n->type, F_HOLDER_NONE, data); +} + +static void param_reset_data(struct filter_param *n) +{ + if (n->type == SCOLS_DATA_STRING) + free(n->val.str); + + memset(&n->val, 0, sizeof(n->val)); + n->fetched = 0; + n->empty = 1; + + if (n->re) { + regfree(n->re); + free(n->re); + n->re = NULL; + } +} + +void filter_free_param(struct filter_param *n) +{ + param_reset_data(n); + + free(n->holder_name); + list_del_init(&n->pr_params); + scols_unref_column(n->col); + free(n); +} + +int filter_param_get_datatype(struct filter_param *n) +{ + return n ? n->type : SCOLS_DATA_NONE; +} + +int is_filter_holder_node(struct filter_node *n) +{ + return n && filter_node_get_type(n) == F_NODE_PARAM + && ((struct filter_param *)(n))->holder; +} + +void filter_dump_param(struct ul_jsonwrt *json, struct filter_param *n) +{ + ul_jsonwrt_object_open(json, "param"); + + if (n->empty) { + ul_jsonwrt_value_boolean(json, "empty", true); + ul_jsonwrt_value_s(json, "type", datatype2str(n->type)); + } else { + switch (n->type) { + case SCOLS_DATA_STRING: + ul_jsonwrt_value_s(json, "string", n->val.str); + break; + case SCOLS_DATA_U64: + ul_jsonwrt_value_u64(json, "number", n->val.num); + break; + case SCOLS_DATA_FLOAT: + ul_jsonwrt_value_double(json, "float", n->val.fnum); + break; + case SCOLS_DATA_BOOLEAN: + ul_jsonwrt_value_boolean(json, "bool", n->val.boolean); + break; + default: + break; + } + } + + if (n->holder == F_HOLDER_COLUMN) + ul_jsonwrt_value_s(json, "column", n->holder_name); + + ul_jsonwrt_object_close(json); +} + +int filter_param_reset_holder(struct filter_param *n) +{ + if (!n->holder || !n->col) + return 0; + + param_reset_data(n); + + if (n->type != SCOLS_DATA_NONE) + return 0; /* already set */ + + if (scols_column_get_data_type(n->col)) + /* use by application defined type */ + n->type = scols_column_get_data_type(n->col); + else { + /* use by JSON defined type, default to string if not specified */ + switch (n->col->json_type) { + case SCOLS_JSON_NUMBER: + n->type = SCOLS_DATA_U64; + break; + case SCOLS_JSON_BOOLEAN: + n->type = SCOLS_DATA_BOOLEAN; + break; + case SCOLS_JSON_FLOAT: + n->type = SCOLS_DATA_FLOAT; + break; + case SCOLS_JSON_STRING: + default: + n->type = SCOLS_DATA_STRING; + break; + } + } + + DBG(FPARAM, ul_debugobj(n, "holder %s type: %s", n->holder_name, datatype2str(n->type))); + return 0; +} + +static int fetch_holder_data(struct libscols_filter *fltr __attribute__((__unused__)), + struct filter_param *n, struct libscols_line *ln) +{ + const char *data = NULL; + struct libscols_column *cl = n->col; + int type = n->type; + int rc = 0; + + if (n->fetched || n->holder != F_HOLDER_COLUMN) + return 0; + if (!cl) { + DBG(FPARAM, ul_debugobj(n, "no column for %s holder", n->holder_name)); + return -EINVAL; + } + DBG(FPARAM, ul_debugobj(n, "fetching %s data", n->holder_name)); + + if (fltr->filler_cb && !scols_line_is_filled(ln, cl->seqnum)) { + DBG(FPARAM, ul_debugobj(n, " by callback")); + rc = fltr->filler_cb(fltr, ln, cl->seqnum, fltr->filler_data); + if (rc) + return rc; + } + + n->fetched = 1; + + if (scols_column_has_data_func(cl)) { + struct libscols_cell *ce = scols_line_get_column_cell(ln, cl); + + DBG(FPARAM, ul_debugobj(n, " using datafunc()")); + if (ce) + data = cl->datafunc(n->col, ce, cl->datafunc_data); + if (data) + rc = param_set_data(n, scols_column_get_data_type(cl), data); + } else { + DBG(FPARAM, ul_debugobj(n, " using as string")); + data = scols_line_get_column_data(ln, n->col); + rc = param_set_data(n, SCOLS_DATA_STRING, data); + } + + /* cast to the wanted type */ + if (rc == 0 && type != SCOLS_DATA_NONE) + rc = cast_param(type, n); + return rc; +} + +int filter_eval_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct filter_param *n, + int *status) +{ + int rc = 0; + + DBG(FLTR, ul_debugobj(fltr, "eval param")); + + rc = fetch_holder_data(fltr, n, ln); + if (n->empty || rc) { + *status = 0; + goto done; + } + + switch (n->type) { + case SCOLS_DATA_STRING: + *status = n->val.str != NULL && *n->val.str != '\0'; + break; + case SCOLS_DATA_U64: + *status = n->val.num != 0; + break; + case SCOLS_DATA_FLOAT: + *status = n->val.fnum != 0.0; + break; + case SCOLS_DATA_BOOLEAN: + *status = n->val.boolean != false; + break; + default: + rc = -EINVAL; + break; + } +done: + if (rc) + DBG(FLTR, ul_debugobj(fltr, "failed eval param [rc=%d]", rc)); + return rc; +} + +int filter_count_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct libscols_counter *ct) +{ + unsigned long long num = 0; + + if (ct->func == SCOLS_COUNTER_COUNT) { + ct->result++; + return 0; + } + + if (ct->param) { + int rc; + + ct->param->type = SCOLS_DATA_U64; + rc = fetch_holder_data(fltr, ct->param, ln); + if (rc) + return rc; + + if (ct->param->empty) + return -EINVAL; + + num = ct->param->val.num; + } + + switch (ct->func) { + case SCOLS_COUNTER_MAX: + if (!ct->has_result) + ct->result = num; + else if (num > ct->result) + ct->result = num; + break; + case SCOLS_COUNTER_MIN: + if (!ct->has_result) + ct->result = num; + else if (num < ct->result) + ct->result = num; + break; + case SCOLS_COUNTER_SUM: + ct->result += num; + break; + default: + return -EINVAL; + } + + ct->has_result = 1; + DBG(FLTR, ul_debugobj(fltr, "counted '%s' [result: %llu]", ct->name, ct->result)); + return 0; +} + +static int xstrcmp(char *a, char *b) +{ + if (!a && !b) + return 0; + if (!a && b) + return -1; + if (a && !b) + return 1; + return strcmp(a, b); +} + +static int string_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + switch (oper) { + case F_EXPR_EQ: + *status = xstrcmp(l->val.str, r->val.str) == 0; + break; + case F_EXPR_NE: + *status = xstrcmp(l->val.str, r->val.str) != 0; + break; + case F_EXPR_LE: + *status = xstrcmp(l->val.str, r->val.str) <= 0; + break; + case F_EXPR_LT: + *status = xstrcmp(l->val.str, r->val.str) < 0; + break; + case F_EXPR_GE: + *status = xstrcmp(l->val.str, r->val.str) >= 0; + break; + case F_EXPR_GT: + *status = xstrcmp(l->val.str, r->val.str) > 0; + break; + case F_EXPR_REG: + if (!r->re) + return -EINVAL; + *status = regexec(r->re, l->val.str ? : "", 0, NULL, 0) == 0; + break; + case F_EXPR_NREG: + if (!r->re) + return -EINVAL; + *status = regexec(r->re, l->val.str ? : "", 0, NULL, 0) != 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int u64_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + if (l->empty || r->empty) { + *status = 0; + return 0; + } + + switch (oper) { + case F_EXPR_EQ: + *status = l->val.num == r->val.num; + break; + case F_EXPR_NE: + *status = l->val.num != r->val.num; + break; + case F_EXPR_LE: + *status = l->val.num <= r->val.num; + break; + case F_EXPR_LT: + *status = l->val.num < r->val.num; + break; + case F_EXPR_GE: + *status = l->val.num >= r->val.num; + break; + case F_EXPR_GT: + *status = l->val.num > r->val.num; + break; + default: + return -EINVAL; + } + return 0; +} + +static int float_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + if (l->empty || r->empty) { + *status = 0; + return 0; + } + + switch (oper) { + case F_EXPR_EQ: + *status = l->val.fnum == r->val.fnum; + break; + case F_EXPR_NE: + *status = l->val.fnum != r->val.fnum; + break; + case F_EXPR_LE: + *status = l->val.fnum <= r->val.fnum; + break; + case F_EXPR_LT: + *status = l->val.fnum < r->val.fnum; + break; + case F_EXPR_GE: + *status = l->val.fnum >= r->val.fnum; + break; + case F_EXPR_GT: + *status = l->val.fnum > r->val.fnum; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bool_opers(enum filter_etype oper, struct filter_param *l, + struct filter_param *r, int *status) +{ + if (l->empty || r->empty) { + *status = 0; + return 0; + } + + switch (oper) { + case F_EXPR_EQ: + *status = l->val.boolean == r->val.boolean; + break; + case F_EXPR_NE: + *status = l->val.boolean != r->val.boolean; + break; + case F_EXPR_LE: + *status = l->val.boolean <= r->val.boolean; + break; + case F_EXPR_LT: + *status = l->val.boolean < r->val.boolean; + break; + case F_EXPR_GE: + *status = l->val.boolean >= r->val.boolean; + break; + case F_EXPR_GT: + *status = l->val.boolean > r->val.boolean; + break; + default: + return -EINVAL; + } + return 0; +} + +/* call filter_cast_param() to be sure that param data are ready (fetched from + * holder, etc.) */ +int filter_compare_params(struct libscols_filter *fltr __attribute__((__unused__)), + enum filter_etype oper, + struct filter_param *l, + struct filter_param *r, + int *status) +{ + int rc; + + if (!l || !r || l->type != r->type) + return -EINVAL; + + *status = 0; + + switch (l->type) { + case SCOLS_DATA_STRING: + rc = string_opers(oper, l, r, status); + break; + case SCOLS_DATA_U64: + rc = u64_opers(oper, l, r, status); + break; + case SCOLS_DATA_FLOAT: + rc = float_opers(oper, l, r, status); + break; + case SCOLS_DATA_BOOLEAN: + rc = bool_opers(oper, l, r, status); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int string_cast(int type, struct filter_param *n) +{ + char *str = n->val.str; + + if (type == SCOLS_DATA_STRING) + return 0; + + n->val.str = NULL; + + switch (type) { + case SCOLS_DATA_U64: + { + uint64_t num = 0; + if (str) { + int rc = ul_strtou64(str, &num, 10); + if (rc) + return rc; + } + n->val.num = num; + break; + } + case SCOLS_DATA_FLOAT: + { + long double num = 0; + if (str) { + int rc = ul_strtold(str, &num); + if (rc) + return rc; + } + n->val.fnum = num; + break; + } + case SCOLS_DATA_BOOLEAN: + { + bool x = str && *str + && (strcasecmp(str, "1") == 0 + || strcasecmp(str, "true") == 0 + || rpmatch(str) == RPMATCH_YES); + n->val.boolean = x; + break; + } + default: + return -EINVAL; + } + + free(str); + return 0; +} + +static int u64_cast(int type, struct filter_param *n) +{ + unsigned long long num = n->val.num; + + switch (type) { + case SCOLS_DATA_STRING: + n->val.str = NULL; + if (asprintf(&n->val.str, "%llu", num) <= 0) + return -ENOMEM; + break; + case SCOLS_DATA_U64: + break; + case SCOLS_DATA_FLOAT: + n->val.fnum = num; + break; + case SCOLS_DATA_BOOLEAN: + n->val.boolean = num > 0 ? true : false; + break; + default: + return -EINVAL; + } + return 0; +} + +static int float_cast(int type, struct filter_param *n) +{ + long double fnum = n->val.fnum; + + switch (type) { + case SCOLS_DATA_STRING: + n->val.str = NULL; + if (asprintf(&n->val.str, "%Lg", fnum) <= 0) + return -ENOMEM; + break; + case SCOLS_DATA_U64: + n->val.num = fnum; + break; + case SCOLS_DATA_FLOAT: + break;; + case SCOLS_DATA_BOOLEAN: + n->val.boolean = fnum > 0.0 ? true : false; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bool_cast(int type, struct filter_param *n) +{ + bool x = n->val.boolean; + + switch (type) { + case SCOLS_DATA_STRING: + n->val.str = NULL; + if (asprintf(&n->val.str, "%s", x ? "true" : "false") <= 0) + return -ENOMEM; + break; + case SCOLS_DATA_U64: + n->val.num = x ? 1 : 0; + break; + case SCOLS_DATA_FLOAT: + n->val.fnum = x ? 1.0 : 0.0; + break; + case SCOLS_DATA_BOOLEAN: + break;; + default: + return -EINVAL; + } + return 0; +} + +static int cast_param(int type, struct filter_param *n) +{ + int rc; + int orgtype = n->type; + + if (type == orgtype) + return 0; + + if (orgtype == SCOLS_DATA_STRING) + DBG(FPARAM, ul_debugobj(n, " casting \"%s\" to %s", n->val.str, datatype2str(type))); + else + DBG(FPARAM, ul_debugobj(n, " casting %s to %s", datatype2str(orgtype), datatype2str(type))); + + switch (orgtype) { + case SCOLS_DATA_STRING: + rc = string_cast(type, n); + break; + case SCOLS_DATA_U64: + rc = u64_cast(type, n); + break; + case SCOLS_DATA_FLOAT: + rc = float_cast(type, n); + break; + case SCOLS_DATA_BOOLEAN: + rc = bool_cast(type, n); + break; + default: + rc = -EINVAL; + break; + } + + if (rc == 0) + n->type = type; + + if (rc) + DBG(FPARAM, ul_debugobj(n, "cast done [rc=%d]", rc)); + return rc; +} + +int filter_cast_param(struct libscols_filter *fltr, + struct libscols_line *ln, + int type, + struct filter_param *n, + struct filter_param **result) +{ + int rc; + int orgtype = n->type; + + DBG(FPARAM, ul_debugobj(n, "casting param to %s", datatype2str(type))); + rc = fetch_holder_data(fltr, n, ln); + if (rc) + return rc; + + if (type == orgtype) { + filter_ref_node((struct filter_node *) n); /* caller wants to call filter_unref_node() for the result */ + *result = n; + return 0; + } + + *result = copy_param(n); + if (!*result) + return -ENOMEM; + rc = cast_param(type, *result); + + DBG(FPARAM, ul_debugobj(n, "cast done [rc=%d]", rc)); + return rc; +} + +int filter_next_param(struct libscols_filter *fltr, + struct libscols_iter *itr, struct filter_param **prm) +{ + int rc = 1; + + if (!fltr || !itr || !prm) + return -EINVAL; + *prm = NULL; + + if (!itr->head) + SCOLS_ITER_INIT(itr, &fltr->params); + if (itr->p != itr->head) { + SCOLS_ITER_ITERATE(itr, *prm, struct filter_param, pr_params); + rc = 0; + } + + return rc; +} + +/** + * scols_filter_assign_column: + * @fltr: pointer to filter + * @itr: iterator + * @name: holder name + * @col: column + * + * Assign @col to filter parametr. The parametr is addressed by @itr or by + * @name. See scols_filter_next_holder(). + * + * Returns: 0, a negative value in case of an error. + * + * Since: 2.40 + */ +int scols_filter_assign_column(struct libscols_filter *fltr, + struct libscols_iter *itr, + const char *name, struct libscols_column *col) +{ + struct filter_param *n = NULL; + + if (itr && itr->p) { + struct list_head *p = IS_ITER_FORWARD(itr) ? + itr->p->prev : itr->p->next; + n = list_entry(p, struct filter_param, pr_params); + } else if (name) { + struct libscols_iter xitr; + struct filter_param *x = NULL; + + scols_reset_iter(&xitr, SCOLS_ITER_FORWARD); + while (filter_next_param(fltr, &xitr, &x) == 0) { + if (x->col + || x->holder != F_HOLDER_COLUMN + || strcmp(name, x->holder_name) != 0) + continue; + n = x; + break; + } + } + + if (n) { + if (n->col) + scols_unref_column(n->col); + + DBG(FPARAM, ul_debugobj(n, "assing %s to column %s", name, + scols_column_get_name(col))); + n->col = col; + scols_ref_column(col); + } + + return n ? 0 : -EINVAL; +} + +/** + * scols_filter_next_holder: + * @fltr: filter instance + * @itr: a pointer to a struct libscols_iter instance + * @name: returns the next column name + * @type: 0 (not implemented yet) + * + * Finds the next holder used in the expression and and returns a name via + * @name. The currently supported holder type is only column name. + * + * Returns: 0, a negative value in case of an error, and 1 at the end. + * + * Since: 2.40 + */ +int scols_filter_next_holder(struct libscols_filter *fltr, + struct libscols_iter *itr, + const char **name, + int type) +{ + struct filter_param *prm = NULL; + int rc = 0; + + *name = NULL; + if (!type) + type = F_HOLDER_COLUMN; /* default */ + + do { + rc = filter_next_param(fltr, itr, &prm); + if (rc == 0 && (int) prm->holder == type) { + *name = prm->holder_name; + } + } while (rc == 0 && !*name); + + return rc; +} diff --git a/libsmartcols/src/filter-parser.c b/libsmartcols/src/filter-parser.c new file mode 100644 index 0000000..cf9ed9b --- /dev/null +++ b/libsmartcols/src/filter-parser.c @@ -0,0 +1,1803 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + 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, either version 3 of the License, or + (at your option) any later version. + + 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, see <https://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 2 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 1 "libsmartcols/src/filter-parser.y" + +#ifdef __clang__ +/* clang detects yynerrs as unused. + * Will be fixed in future versions of bison. + */ +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#endif + +#include <stdio.h> + +#include "smartcolsP.h" +#include "filter-parser.h" +#include "filter-scanner.h" + +void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); + + +#line 89 "libsmartcols/src/filter-parser.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "filter-parser.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_T_NUMBER = 3, /* T_NUMBER */ + YYSYMBOL_T_STRING = 4, /* T_STRING */ + YYSYMBOL_T_HOLDER = 5, /* T_HOLDER */ + YYSYMBOL_T_FLOAT = 6, /* T_FLOAT */ + YYSYMBOL_T_OR = 7, /* T_OR */ + YYSYMBOL_T_AND = 8, /* T_AND */ + YYSYMBOL_T_EQ = 9, /* T_EQ */ + YYSYMBOL_T_NE = 10, /* T_NE */ + YYSYMBOL_T_LT = 11, /* T_LT */ + YYSYMBOL_T_LE = 12, /* T_LE */ + YYSYMBOL_T_GT = 13, /* T_GT */ + YYSYMBOL_T_GE = 14, /* T_GE */ + YYSYMBOL_T_REG = 15, /* T_REG */ + YYSYMBOL_T_NREG = 16, /* T_NREG */ + YYSYMBOL_T_TRUE = 17, /* T_TRUE */ + YYSYMBOL_T_FALSE = 18, /* T_FALSE */ + YYSYMBOL_T_NEG = 19, /* T_NEG */ + YYSYMBOL_20_ = 20, /* '(' */ + YYSYMBOL_21_ = 21, /* ')' */ + YYSYMBOL_YYACCEPT = 22, /* $accept */ + YYSYMBOL_filter = 23, /* filter */ + YYSYMBOL_expr = 24, /* expr */ + YYSYMBOL_param = 25 /* param */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>. */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 14 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 54 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 22 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 4 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 21 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 36 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 274 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 20, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int8 yyrline[] = +{ + 0, 72, 72, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 88, 94, 102, 103, 104, 105, + 106, 110 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "T_NUMBER", "T_STRING", + "T_HOLDER", "T_FLOAT", "T_OR", "T_AND", "T_EQ", "T_NE", "T_LT", "T_LE", + "T_GT", "T_GE", "T_REG", "T_NREG", "T_TRUE", "T_FALSE", "T_NEG", "'('", + "')'", "$accept", "filter", "expr", "param", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-8) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 1, -8, -8, -8, -8, -8, -8, 1, 1, 2, + 30, -8, -8, 15, -8, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, -8, 38, 38, -8, -8, + -8, -8, -8, -8, -8, -8 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 16, 19, 18, 17, 20, 21, 0, 0, 0, + 2, 3, 7, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 6, 5, 8, 9, + 11, 10, 13, 12, 14, 15 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -8, -8, -7, -8 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 9, 10, 11 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 12, 13, 14, 0, 1, 2, 3, 4, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 5, 6, + 7, 8, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 0, 0, 0, 0, 25, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 17, 18, 19, + 20, 21, 22, 23, 24 +}; + +static const yytype_int8 yycheck[] = +{ + 7, 8, 0, -1, 3, 4, 5, 6, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 17, 18, + 19, 20, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, -1, -1, -1, -1, 21, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 9, 10, 11, + 12, 13, 14, 15, 16 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 4, 5, 6, 17, 18, 19, 20, 23, + 24, 25, 24, 24, 0, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 21, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr1[] = +{ + 0, 22, 23, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, + 25, 25 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 3, 3, 3, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, + 1, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (scanner, fltr, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, scanner, fltr); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, void *scanner, struct libscols_filter *fltr) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (scanner); + YY_USE (fltr); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, void *scanner, struct libscols_filter *fltr) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, scanner, fltr); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, void *scanner, struct libscols_filter *fltr) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], scanner, fltr); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, scanner, fltr); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, void *scanner, struct libscols_filter *fltr) +{ + YY_USE (yyvaluep); + YY_USE (scanner); + YY_USE (fltr); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yykind) + { + case YYSYMBOL_expr: /* expr */ +#line 59 "libsmartcols/src/filter-parser.y" + { + /* This destruct is called on error. The root node will be deallocated + * by scols_unref_filter(). + */ + if (fltr->root != ((*yyvaluep).param)) + filter_unref_node(((*yyvaluep).param)); + } +#line 1133 "libsmartcols/src/filter-parser.c" + break; + + case YYSYMBOL_param: /* param */ +#line 59 "libsmartcols/src/filter-parser.y" + { + /* This destruct is called on error. The root node will be deallocated + * by scols_unref_filter(). + */ + if (fltr->root != ((*yyvaluep).param)) + filter_unref_node(((*yyvaluep).param)); + } +#line 1145 "libsmartcols/src/filter-parser.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void *scanner, struct libscols_filter *fltr) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, scanner); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* filter: expr */ +#line 72 "libsmartcols/src/filter-parser.y" + { fltr->root = (yyvsp[0].param); } +#line 1424 "libsmartcols/src/filter-parser.c" + break; + + case 3: /* expr: param */ +#line 76 "libsmartcols/src/filter-parser.y" + { (yyval.param) = (yyvsp[0].param); } +#line 1430 "libsmartcols/src/filter-parser.c" + break; + + case 4: /* expr: '(' expr ')' */ +#line 77 "libsmartcols/src/filter-parser.y" + { (yyval.param) = (yyvsp[-1].param); } +#line 1436 "libsmartcols/src/filter-parser.c" + break; + + case 5: /* expr: expr T_AND expr */ +#line 78 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_AND, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1442 "libsmartcols/src/filter-parser.c" + break; + + case 6: /* expr: expr T_OR expr */ +#line 79 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_OR, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1448 "libsmartcols/src/filter-parser.c" + break; + + case 7: /* expr: T_NEG expr */ +#line 80 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_NEG, NULL, (yyvsp[0].param)); } +#line 1454 "libsmartcols/src/filter-parser.c" + break; + + case 8: /* expr: expr T_EQ expr */ +#line 81 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_EQ, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1460 "libsmartcols/src/filter-parser.c" + break; + + case 9: /* expr: expr T_NE expr */ +#line 82 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_NE, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1466 "libsmartcols/src/filter-parser.c" + break; + + case 10: /* expr: expr T_LE expr */ +#line 83 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_LE, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1472 "libsmartcols/src/filter-parser.c" + break; + + case 11: /* expr: expr T_LT expr */ +#line 84 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_LT, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1478 "libsmartcols/src/filter-parser.c" + break; + + case 12: /* expr: expr T_GE expr */ +#line 85 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_GE, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1484 "libsmartcols/src/filter-parser.c" + break; + + case 13: /* expr: expr T_GT expr */ +#line 86 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_expr(fltr, F_EXPR_GT, (yyvsp[-2].param), (yyvsp[0].param)); } +#line 1490 "libsmartcols/src/filter-parser.c" + break; + + case 14: /* expr: expr T_REG expr */ +#line 88 "libsmartcols/src/filter-parser.y" + { + if (filter_compile_param(fltr, (struct filter_param *) (yyvsp[0].param)) != 0) + YYERROR; + (yyval.param) = filter_new_expr(fltr, F_EXPR_REG, (yyvsp[-2].param), (yyvsp[0].param)); + } +#line 1500 "libsmartcols/src/filter-parser.c" + break; + + case 15: /* expr: expr T_NREG expr */ +#line 94 "libsmartcols/src/filter-parser.y" + { + if (filter_compile_param(fltr, (struct filter_param *) (yyvsp[0].param)) != 0) + YYERROR; + (yyval.param) = filter_new_expr(fltr, F_EXPR_NREG, (yyvsp[-2].param), (yyvsp[0].param)); + } +#line 1510 "libsmartcols/src/filter-parser.c" + break; + + case 16: /* param: T_NUMBER */ +#line 102 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_U64, 0, (void *) (&(yyvsp[0].param_number))); } +#line 1516 "libsmartcols/src/filter-parser.c" + break; + + case 17: /* param: T_FLOAT */ +#line 103 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_FLOAT, 0, (void *) (&(yyvsp[0].param_float))); } +#line 1522 "libsmartcols/src/filter-parser.c" + break; + + case 18: /* param: T_HOLDER */ +#line 104 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_NONE, F_HOLDER_COLUMN, (void *) (yyvsp[0].param_name)); } +#line 1528 "libsmartcols/src/filter-parser.c" + break; + + case 19: /* param: T_STRING */ +#line 105 "libsmartcols/src/filter-parser.y" + { (yyval.param) = filter_new_param(fltr, SCOLS_DATA_STRING, 0, (void *) (yyvsp[0].param_string)); } +#line 1534 "libsmartcols/src/filter-parser.c" + break; + + case 20: /* param: T_TRUE */ +#line 106 "libsmartcols/src/filter-parser.y" + { + bool x = true; + (yyval.param) = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } +#line 1543 "libsmartcols/src/filter-parser.c" + break; + + case 21: /* param: T_FALSE */ +#line 110 "libsmartcols/src/filter-parser.y" + { + bool x = false; + (yyval.param) = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } +#line 1552 "libsmartcols/src/filter-parser.c" + break; + + +#line 1556 "libsmartcols/src/filter-parser.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (scanner, fltr, yymsgp); + if (yysyntax_error_status == YYENOMEM) + YYNOMEM; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, scanner, fltr); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, scanner, fltr); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (scanner, fltr, YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, scanner, fltr); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, scanner, fltr); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 118 "libsmartcols/src/filter-parser.y" + + +void yyerror (yyscan_t *locp __attribute__((__unused__)), + struct libscols_filter *fltr, + char const *msg) +{ + if (msg && fltr) { + char *p; + + if (fltr->errmsg) + free(fltr->errmsg); + + fltr->errmsg = strdup(msg); + if (!fltr->errmsg) + return; + + p = strstr(fltr->errmsg, "T_"); + if (p) { + size_t sz = strlen(fltr->errmsg); + memmove(p, p + 2, sz - 1 - (p - fltr->errmsg)); + } + } + errno = EINVAL; +} diff --git a/libsmartcols/src/filter-parser.h b/libsmartcols/src/filter-parser.h new file mode 100644 index 0000000..45666f1 --- /dev/null +++ b/libsmartcols/src/filter-parser.h @@ -0,0 +1,110 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + 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, either version 3 of the License, or + (at your option) any later version. + + 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, see <https://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_LIBSMARTCOLS_SRC_FILTER_PARSER_H_INCLUDED +# define YY_YY_LIBSMARTCOLS_SRC_FILTER_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif +/* "%code requires" blocks. */ +#line 27 "libsmartcols/src/filter-parser.y" + + +#line 52 "libsmartcols/src/filter-parser.h" + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + T_NUMBER = 258, /* T_NUMBER */ + T_STRING = 259, /* T_STRING */ + T_HOLDER = 260, /* T_HOLDER */ + T_FLOAT = 261, /* T_FLOAT */ + T_OR = 262, /* T_OR */ + T_AND = 263, /* T_AND */ + T_EQ = 264, /* T_EQ */ + T_NE = 265, /* T_NE */ + T_LT = 266, /* T_LT */ + T_LE = 267, /* T_LE */ + T_GT = 268, /* T_GT */ + T_GE = 269, /* T_GE */ + T_REG = 270, /* T_REG */ + T_NREG = 271, /* T_NREG */ + T_TRUE = 272, /* T_TRUE */ + T_FALSE = 273, /* T_FALSE */ + T_NEG = 274 /* T_NEG */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 40 "libsmartcols/src/filter-parser.y" + + unsigned long long param_number; + const char* param_string; + const char* param_name; + long double param_float; + struct filter_node *param; + struct filter_node *expr; + +#line 97 "libsmartcols/src/filter-parser.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + + +int yyparse (void *scanner, struct libscols_filter *fltr); + + +#endif /* !YY_YY_LIBSMARTCOLS_SRC_FILTER_PARSER_H_INCLUDED */ diff --git a/tests/expected/mount/special b/libsmartcols/src/filter-parser.stamp index e69de29..e69de29 100644 --- a/tests/expected/mount/special +++ b/libsmartcols/src/filter-parser.stamp diff --git a/libsmartcols/src/filter-parser.y b/libsmartcols/src/filter-parser.y new file mode 100644 index 0000000..ce245f3 --- /dev/null +++ b/libsmartcols/src/filter-parser.y @@ -0,0 +1,141 @@ +%{ +#ifdef __clang__ +/* clang detects yynerrs as unused. + * Will be fixed in future versions of bison. + */ +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#endif + +#include <stdio.h> + +#include "smartcolsP.h" +#include "filter-parser.h" +#include "filter-scanner.h" + +void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); + +%} + +%define api.pure full + +%lex-param {void *scanner} +%parse-param {void *scanner}{struct libscols_filter *fltr} + +%define parse.error verbose + +%code requires +{ +} + +/* Elegant way, but not compatible with biron -y (autotools): +%define api.value.type union +%token <unsigned long long> param_number +%token <const char*> param_string +%token <const char*> param_name +%token <long double> param_float +%type <struct filter_node*> param +%type <struct filter_node*> expr +*/ + +%union { + unsigned long long param_number; + const char* param_string; + const char* param_name; + long double param_float; + struct filter_node *param; + struct filter_node *expr; +} +%token <param_number> T_NUMBER +%token <param_string> T_STRING +%token <param_name> T_HOLDER +%token <param_float> T_FLOAT +%type <param> param expr + +%token T_OR T_AND T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG +%left T_OR T_AND +%left T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG + + +%destructor { + /* This destruct is called on error. The root node will be deallocated + * by scols_unref_filter(). + */ + if (fltr->root != $$) + filter_unref_node($$); + } <param> + +%% + +%start filter; + +filter: + expr { fltr->root = $1; } +; + +expr: + param { $$ = $1; } + | '(' expr ')' { $$ = $2; } + | expr T_AND expr { $$ = filter_new_expr(fltr, F_EXPR_AND, $1, $3); } + | expr T_OR expr { $$ = filter_new_expr(fltr, F_EXPR_OR, $1, $3); } + | T_NEG expr { $$ = filter_new_expr(fltr, F_EXPR_NEG, NULL, $2); } + | expr T_EQ expr { $$ = filter_new_expr(fltr, F_EXPR_EQ, $1, $3); } + | expr T_NE expr { $$ = filter_new_expr(fltr, F_EXPR_NE, $1, $3); } + | expr T_LE expr { $$ = filter_new_expr(fltr, F_EXPR_LE, $1, $3); } + | expr T_LT expr { $$ = filter_new_expr(fltr, F_EXPR_LT, $1, $3); } + | expr T_GE expr { $$ = filter_new_expr(fltr, F_EXPR_GE, $1, $3); } + | expr T_GT expr { $$ = filter_new_expr(fltr, F_EXPR_GT, $1, $3); } + + | expr T_REG expr { + if (filter_compile_param(fltr, (struct filter_param *) $3) != 0) + YYERROR; + $$ = filter_new_expr(fltr, F_EXPR_REG, $1, $3); + } + + | expr T_NREG expr { + if (filter_compile_param(fltr, (struct filter_param *) $3) != 0) + YYERROR; + $$ = filter_new_expr(fltr, F_EXPR_NREG, $1, $3); + } +; + +param: + T_NUMBER { $$ = filter_new_param(fltr, SCOLS_DATA_U64, 0, (void *) (&$1)); } + | T_FLOAT { $$ = filter_new_param(fltr, SCOLS_DATA_FLOAT, 0, (void *) (&$1)); } + | T_HOLDER { $$ = filter_new_param(fltr, SCOLS_DATA_NONE, F_HOLDER_COLUMN, (void *) $1); } + | T_STRING { $$ = filter_new_param(fltr, SCOLS_DATA_STRING, 0, (void *) $1); } + | T_TRUE { + bool x = true; + $$ = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } + | T_FALSE { + bool x = false; + $$ = filter_new_param(fltr, SCOLS_DATA_BOOLEAN, 0, (void *) &x); + } + +; + + +%% + +void yyerror (yyscan_t *locp __attribute__((__unused__)), + struct libscols_filter *fltr, + char const *msg) +{ + if (msg && fltr) { + char *p; + + if (fltr->errmsg) + free(fltr->errmsg); + + fltr->errmsg = strdup(msg); + if (!fltr->errmsg) + return; + + p = strstr(fltr->errmsg, "T_"); + if (p) { + size_t sz = strlen(fltr->errmsg); + memmove(p, p + 2, sz - 1 - (p - fltr->errmsg)); + } + } + errno = EINVAL; +} diff --git a/libsmartcols/src/filter-scanner.c b/libsmartcols/src/filter-scanner.c new file mode 100644 index 0000000..fdebb46 --- /dev/null +++ b/libsmartcols/src/filter-scanner.c @@ -0,0 +1,2096 @@ +#line 1 "libsmartcols/src/filter-scanner.c" + +#line 3 "libsmartcols/src/filter-scanner.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yyget_lval +#define yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval yyget_lval +#endif + +#ifdef yyset_lval +#define yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval yyset_lval +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +#define yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 23 +#define YY_END_OF_BUFFER 24 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[78] = + { 0, + 0, 0, 24, 23, 1, 2, 8, 23, 23, 5, + 3, 4, 20, 12, 23, 14, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 23, 1, 2, 10, 16, 0, 22, + 6, 0, 0, 20, 11, 9, 15, 13, 21, 21, + 9, 21, 13, 14, 11, 12, 10, 21, 7, 21, + 21, 21, 21, 21, 7, 19, 6, 21, 8, 21, + 21, 21, 21, 18, 21, 17, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 6, 7, 8, 9, + 10, 1, 1, 1, 6, 11, 6, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 6, 1, 13, + 14, 15, 1, 1, 16, 17, 17, 18, 19, 20, + 21, 17, 17, 17, 17, 22, 17, 23, 24, 17, + 25, 26, 27, 28, 29, 17, 17, 17, 17, 17, + 1, 1, 1, 1, 6, 1, 30, 17, 17, 31, + + 32, 33, 34, 17, 17, 17, 17, 35, 17, 36, + 37, 17, 38, 39, 40, 41, 42, 17, 17, 17, + 17, 17, 1, 43, 1, 44, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[45] = + { 0, + 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, + 3, 3, 1, 1, 1, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 1 + } ; + +static const flex_int16_t yy_base[81] = + { 0, + 0, 0, 108, 109, 105, 103, 31, 100, 97, 95, + 109, 109, 35, 88, 34, 87, 77, 0, 74, 82, + 30, 31, 32, 71, 70, 59, 56, 63, 20, 21, + 23, 53, 52, 47, 87, 85, 109, 109, 82, 109, + 109, 78, 73, 52, 109, 109, 109, 109, 0, 65, + 0, 58, 0, 0, 0, 0, 0, 49, 0, 47, + 43, 38, 31, 29, 109, 58, 0, 42, 0, 49, + 27, 34, 46, 0, 25, 0, 109, 78, 81, 51 + } ; + +static const flex_int16_t yy_def[81] = + { 0, + 77, 1, 77, 77, 77, 77, 77, 78, 77, 79, + 77, 77, 77, 77, 77, 77, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 77, 77, 77, 77, 77, 78, 77, + 77, 79, 77, 77, 77, 77, 77, 77, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 77, 77, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 0, 77, 77, 77 + } ; + +static const flex_int16_t yy_nxt[154] = + { 0, + 4, 5, 6, 7, 8, 4, 9, 10, 11, 12, + 4, 13, 14, 15, 16, 17, 18, 18, 19, 20, + 21, 22, 23, 24, 18, 18, 18, 25, 18, 26, + 18, 27, 28, 29, 30, 31, 32, 18, 18, 18, + 33, 18, 34, 4, 37, 43, 44, 46, 53, 55, + 57, 53, 55, 49, 57, 58, 76, 54, 56, 63, + 54, 56, 43, 44, 76, 74, 75, 74, 73, 66, + 72, 69, 71, 67, 38, 70, 69, 47, 39, 68, + 39, 42, 67, 42, 66, 40, 40, 36, 35, 65, + 64, 59, 62, 51, 61, 60, 59, 52, 51, 50, + + 48, 45, 40, 41, 40, 36, 35, 77, 3, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77 + } ; + +static const flex_int16_t yy_chk[154] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7, 13, 13, 15, 21, 22, + 23, 29, 30, 80, 31, 23, 75, 21, 22, 31, + 29, 30, 44, 44, 73, 72, 71, 70, 68, 66, + 64, 63, 62, 61, 7, 60, 58, 15, 78, 52, + 78, 79, 50, 79, 43, 42, 39, 36, 35, 34, + 33, 32, 28, 27, 26, 25, 24, 20, 19, 17, + + 16, 14, 10, 9, 8, 6, 5, 3, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "libsmartcols/src/filter-scanner.l" +#line 2 "libsmartcols/src/filter-scanner.l" +#include "smartcolsP.h" +#include "filter-parser.h" /* define tokens (T_*) */ +#line 491 "libsmartcols/src/filter-scanner.c" +#define YY_NO_INPUT 1 +#line 493 "libsmartcols/src/filter-scanner.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +#line 14 "libsmartcols/src/filter-scanner.l" + + +#line 768 "libsmartcols/src/filter-scanner.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 78 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 109 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 16 "libsmartcols/src/filter-scanner.l" +; /* ignore */ + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 17 "libsmartcols/src/filter-scanner.l" +; /* ignore */ + YY_BREAK +case 3: +YY_RULE_SETUP +#line 19 "libsmartcols/src/filter-scanner.l" +return '('; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 20 "libsmartcols/src/filter-scanner.l" +return ')'; + YY_BREAK +case 5: +YY_RULE_SETUP +#line 21 "libsmartcols/src/filter-scanner.l" +return '\''; + YY_BREAK +case 6: +YY_RULE_SETUP +#line 23 "libsmartcols/src/filter-scanner.l" +return T_AND; + YY_BREAK +case 7: +YY_RULE_SETUP +#line 24 "libsmartcols/src/filter-scanner.l" +return T_OR; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 25 "libsmartcols/src/filter-scanner.l" +return T_NEG; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 27 "libsmartcols/src/filter-scanner.l" +return T_EQ; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 28 "libsmartcols/src/filter-scanner.l" +return T_NE; + YY_BREAK +case 11: +YY_RULE_SETUP +#line 30 "libsmartcols/src/filter-scanner.l" +return T_LE; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 31 "libsmartcols/src/filter-scanner.l" +return T_LT; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 33 "libsmartcols/src/filter-scanner.l" +return T_GE; + YY_BREAK +case 14: +YY_RULE_SETUP +#line 34 "libsmartcols/src/filter-scanner.l" +return T_GT; + YY_BREAK +case 15: +YY_RULE_SETUP +#line 36 "libsmartcols/src/filter-scanner.l" +return T_REG; + YY_BREAK +case 16: +YY_RULE_SETUP +#line 37 "libsmartcols/src/filter-scanner.l" +return T_NREG; + YY_BREAK +case 17: +YY_RULE_SETUP +#line 39 "libsmartcols/src/filter-scanner.l" +return T_FALSE; + YY_BREAK +case 18: +YY_RULE_SETUP +#line 40 "libsmartcols/src/filter-scanner.l" +return T_TRUE; + YY_BREAK +case 19: +YY_RULE_SETUP +#line 42 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_float = strtold(yytext, NULL); + return T_FLOAT; +} + YY_BREAK +case 20: +YY_RULE_SETUP +#line 47 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10); + return T_NUMBER; +} + YY_BREAK +case 21: +YY_RULE_SETUP +#line 52 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_name = yytext; + return T_HOLDER; +} + YY_BREAK +case 22: +YY_RULE_SETUP +#line 57 "libsmartcols/src/filter-scanner.l" +{ + yylval->param_string = yytext; + return T_STRING; +} + YY_BREAK +case 23: +YY_RULE_SETUP +#line 63 "libsmartcols/src/filter-scanner.l" +ECHO; + YY_BREAK +#line 953 "libsmartcols/src/filter-scanner.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 78 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 78 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 77); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 63 "libsmartcols/src/filter-scanner.l" diff --git a/libsmartcols/src/filter-scanner.h b/libsmartcols/src/filter-scanner.h new file mode 100644 index 0000000..e56f09b --- /dev/null +++ b/libsmartcols/src/filter-scanner.h @@ -0,0 +1,507 @@ +#ifndef yyHEADER_H +#define yyHEADER_H 1 +#define yyIN_HEADER 1 + +#line 5 "libsmartcols/src/filter-scanner.h" + +#line 7 "libsmartcols/src/filter-scanner.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yyget_lval +#define yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval yyget_lval +#endif + +#ifdef yyset_lval +#define yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval yyset_lval +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#ifndef yy_create_buffer_ALREADY_DEFINED +#undef yy_create_buffer +#endif +#ifndef yy_delete_buffer_ALREADY_DEFINED +#undef yy_delete_buffer +#endif +#ifndef yy_scan_buffer_ALREADY_DEFINED +#undef yy_scan_buffer +#endif +#ifndef yy_scan_string_ALREADY_DEFINED +#undef yy_scan_string +#endif +#ifndef yy_scan_bytes_ALREADY_DEFINED +#undef yy_scan_bytes +#endif +#ifndef yy_init_buffer_ALREADY_DEFINED +#undef yy_init_buffer +#endif +#ifndef yy_flush_buffer_ALREADY_DEFINED +#undef yy_flush_buffer +#endif +#ifndef yy_load_buffer_state_ALREADY_DEFINED +#undef yy_load_buffer_state +#endif +#ifndef yy_switch_to_buffer_ALREADY_DEFINED +#undef yy_switch_to_buffer +#endif +#ifndef yypush_buffer_state_ALREADY_DEFINED +#undef yypush_buffer_state +#endif +#ifndef yypop_buffer_state_ALREADY_DEFINED +#undef yypop_buffer_state +#endif +#ifndef yyensure_buffer_stack_ALREADY_DEFINED +#undef yyensure_buffer_stack +#endif +#ifndef yylex_ALREADY_DEFINED +#undef yylex +#endif +#ifndef yyrestart_ALREADY_DEFINED +#undef yyrestart +#endif +#ifndef yylex_init_ALREADY_DEFINED +#undef yylex_init +#endif +#ifndef yylex_init_extra_ALREADY_DEFINED +#undef yylex_init_extra +#endif +#ifndef yylex_destroy_ALREADY_DEFINED +#undef yylex_destroy +#endif +#ifndef yyget_debug_ALREADY_DEFINED +#undef yyget_debug +#endif +#ifndef yyset_debug_ALREADY_DEFINED +#undef yyset_debug +#endif +#ifndef yyget_extra_ALREADY_DEFINED +#undef yyget_extra +#endif +#ifndef yyset_extra_ALREADY_DEFINED +#undef yyset_extra +#endif +#ifndef yyget_in_ALREADY_DEFINED +#undef yyget_in +#endif +#ifndef yyset_in_ALREADY_DEFINED +#undef yyset_in +#endif +#ifndef yyget_out_ALREADY_DEFINED +#undef yyget_out +#endif +#ifndef yyset_out_ALREADY_DEFINED +#undef yyset_out +#endif +#ifndef yyget_leng_ALREADY_DEFINED +#undef yyget_leng +#endif +#ifndef yyget_text_ALREADY_DEFINED +#undef yyget_text +#endif +#ifndef yyget_lineno_ALREADY_DEFINED +#undef yyget_lineno +#endif +#ifndef yyset_lineno_ALREADY_DEFINED +#undef yyset_lineno +#endif +#ifndef yyget_column_ALREADY_DEFINED +#undef yyget_column +#endif +#ifndef yyset_column_ALREADY_DEFINED +#undef yyset_column +#endif +#ifndef yywrap_ALREADY_DEFINED +#undef yywrap +#endif +#ifndef yyget_lval_ALREADY_DEFINED +#undef yyget_lval +#endif +#ifndef yyset_lval_ALREADY_DEFINED +#undef yyset_lval +#endif +#ifndef yyget_lloc_ALREADY_DEFINED +#undef yyget_lloc +#endif +#ifndef yyset_lloc_ALREADY_DEFINED +#undef yyset_lloc +#endif +#ifndef yyalloc_ALREADY_DEFINED +#undef yyalloc +#endif +#ifndef yyrealloc_ALREADY_DEFINED +#undef yyrealloc +#endif +#ifndef yyfree_ALREADY_DEFINED +#undef yyfree +#endif +#ifndef yytext_ALREADY_DEFINED +#undef yytext +#endif +#ifndef yyleng_ALREADY_DEFINED +#undef yyleng +#endif +#ifndef yyin_ALREADY_DEFINED +#undef yyin +#endif +#ifndef yyout_ALREADY_DEFINED +#undef yyout +#endif +#ifndef yy_flex_debug_ALREADY_DEFINED +#undef yy_flex_debug +#endif +#ifndef yylineno_ALREADY_DEFINED +#undef yylineno +#endif +#ifndef yytables_fload_ALREADY_DEFINED +#undef yytables_fload +#endif +#ifndef yytables_destroy_ALREADY_DEFINED +#undef yytables_destroy +#endif +#ifndef yyTABLES_NAME_ALREADY_DEFINED +#undef yyTABLES_NAME +#endif + +#line 63 "libsmartcols/src/filter-scanner.l" + +#line 505 "libsmartcols/src/filter-scanner.h" +#undef yyIN_HEADER +#endif /* yyHEADER_H */ diff --git a/libsmartcols/src/filter-scanner.l b/libsmartcols/src/filter-scanner.l new file mode 100644 index 0000000..501b603 --- /dev/null +++ b/libsmartcols/src/filter-scanner.l @@ -0,0 +1,62 @@ +%{ +#include "smartcolsP.h" +#include "filter-parser.h" /* define tokens (T_*) */ +%} + +%option reentrant bison-bridge noyywrap noinput nounput + +id [a-zA-Z][a-zA-Z_.%:/\-0-9]* +int [0-9]+ +blank [ \t] +str_qu \"[^\"\n]*\" +str_ap \'[^\'\n]*\' + +%% + +{blank}+ ; /* ignore */ +[\n]+ ; /* ignore */ + +"(" return '('; +")" return ')'; +"'" return '\''; + +and|AND|"&&" return T_AND; +or|OR|"||" return T_OR; +"!"|not|NOT return T_NEG; + +eq|EQ|"==" return T_EQ; +ne|NE|"!=" return T_NE; + +le|LE|"<=" return T_LE; +lt|LT|"<" return T_LT; + +ge|GE|">=" return T_GE; +gt|GT|">" return T_GT; + +"=~" return T_REG; +"!~" return T_NREG; + +false|FALSE return T_FALSE; +true|TRUE return T_TRUE; + +{int}+\.{int}+ { + yylval->param_float = strtold(yytext, NULL); + return T_FLOAT; +} + +{int}+ { + yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10); + return T_NUMBER; +} + +{id} { + yylval->param_name = yytext; + return T_HOLDER; +} + +{str_ap}|{str_qu} { + yylval->param_string = yytext; + return T_STRING; +} + + diff --git a/libsmartcols/src/filter-scanner.stamp b/libsmartcols/src/filter-scanner.stamp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libsmartcols/src/filter-scanner.stamp diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c new file mode 100644 index 0000000..dccf05c --- /dev/null +++ b/libsmartcols/src/filter.c @@ -0,0 +1,520 @@ +/* + * filter.c - functions for lines filtering + * + * Copyright (C) 2023 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +/** + * SECTION: filter + * @title: Filters and counters + * @short_description: defines lines filter and counter + * + * An API to define and use filter and counters. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "smartcolsP.h" + +#include "filter-parser.h" +#include "filter-scanner.h" + +/** + * scols_new_filter: + * @str: filter expression or NULL + * + * Allocated and optionally parses a new filter. + * + * Returns: new filter instance or NULL in case of error. + * + * Since: 2.40 + */ +struct libscols_filter *scols_new_filter(const char *str) +{ + struct libscols_filter *fltr = calloc(1, sizeof(*fltr)); + + if (!fltr) + return NULL; + + DBG(FLTR, ul_debugobj(fltr, "alloc")); + fltr->refcount = 1; + INIT_LIST_HEAD(&fltr->params); + INIT_LIST_HEAD(&fltr->counters); + + if (str && scols_filter_parse_string(fltr, str) != 0) { + scols_unref_filter(fltr); + return NULL; + } + + return fltr; +} + +/** + * scols_ref_filter: + * @fltr: filter instance + * + * Increment filter reference counter. + * + * Since: 2.40 + */ +void scols_ref_filter(struct libscols_filter *fltr) +{ + if (fltr) + fltr->refcount++; +} + +static void reset_filter(struct libscols_filter *fltr) +{ + if (!fltr) + return; + filter_unref_node(fltr->root); + fltr->root = NULL; + + if (fltr->src) + fclose(fltr->src); + fltr->src = NULL; + + free(fltr->errmsg); + fltr->errmsg = NULL; +} + +static void remove_counters(struct libscols_filter *fltr) +{ + if (!fltr) + return; + + DBG(FLTR, ul_debugobj(fltr, "remove all counters")); + while (!list_empty(&fltr->counters)) { + struct libscols_counter *ct = list_entry(fltr->counters.next, + struct libscols_counter, counters); + + filter_unref_node((struct filter_node *) ct->param); + list_del_init(&ct->counters); + free(ct->name); + free(ct); + } +} + +/** + * scols_unref_filter: + * @fltr: filter instance + * + * Deincrements reference counter, unallocates the filter for the last + * reference. + * + * Since: 2.40 + */ +void scols_unref_filter(struct libscols_filter *fltr) +{ + if (fltr && --fltr->refcount <= 0) { + DBG(FLTR, ul_debugobj(fltr, "dealloc")); + reset_filter(fltr); + remove_counters(fltr); + free(fltr); + } +} + +/* This is generic allocater for a new node, always use the node type specific + * functions (e.g. filter_new_param() */ +struct filter_node *__filter_new_node(enum filter_ntype type, size_t sz) +{ + struct filter_node *n = calloc(1, sz); + + if (!n) + return NULL; + + n->type = type; + n->refcount = 1; + return n; +} + +void filter_unref_node(struct filter_node *n) +{ + if (!n || --n->refcount > 0) + return; + + switch (n->type) { + case F_NODE_EXPR: + filter_free_expr((struct filter_expr *) n); + break; + case F_NODE_PARAM: + filter_free_param((struct filter_param *) n); + break; + } +} + +void filter_ref_node(struct filter_node *n) +{ + if (n) + n->refcount++; +} + +void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n) +{ + if (!n) + return; + + switch (n->type) { + case F_NODE_EXPR: + filter_dump_expr(json, (struct filter_expr *) n); + break; + case F_NODE_PARAM: + filter_dump_param(json, (struct filter_param *) n); + break; + } +} + +/** + * scols_filter_parse_string: + * @fltr: filter instance + * @str: string with filter expression + * + * Parses filter, see scols_filter_get_errmsg() for errors. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_filter_parse_string(struct libscols_filter *fltr, const char *str) +{ + yyscan_t sc; + int rc; + + reset_filter(fltr); + + if (!str || !*str) + return 0; /* empty filter is not error */ + + fltr->src = fmemopen((void *) str, strlen(str), "r"); + if (!fltr->src) + return -errno; + + yylex_init(&sc); + yyset_in(fltr->src, sc); + + rc = yyparse(sc, fltr); + yylex_destroy(sc); + + fclose(fltr->src); + fltr->src = NULL; + + ON_DBG(FLTR, scols_dump_filter(fltr, stderr)); + + return rc; +} + +/** + * scols_dump_filter: + * @fltr: filter instance + * @out: output stream + * + * Dumps internal filter nodes in JSON format. This function is mostly designed + * for debugging purpose. The fileds in the output are subject to change. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_dump_filter(struct libscols_filter *fltr, FILE *out) +{ + struct ul_jsonwrt json; + + if (!fltr || !out) + return -EINVAL; + + ul_jsonwrt_init(&json, out, 0); + ul_jsonwrt_root_open(&json); + + filter_dump_node(&json, fltr->root); + ul_jsonwrt_root_close(&json); + return 0; +} + +/** + * scols_filter_get_errmsg: + * @fltr: filter instance + * + * Returns: string with parse-error message of NULL (if no error) + * + * Since: 2.40 + */ +const char *scols_filter_get_errmsg(struct libscols_filter *fltr) +{ + return fltr ? fltr->errmsg : NULL; +} + +int filter_eval_node(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_node *n, int *status) +{ + switch (n->type) { + case F_NODE_PARAM: + return filter_eval_param(fltr, ln, (struct filter_param *) n, status); + case F_NODE_EXPR: + return filter_eval_expr(fltr, ln, (struct filter_expr *) n, status); + default: + break; + } + return -EINVAL; +} + +/** + * scols_line_apply_filter: + * @ln: apply filter to the line + * @fltr: filter instance + * @status: return 1 or 0 as result of the expression + * + * Applies filter (and also counters assisiated with the filter). + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_line_apply_filter(struct libscols_line *ln, + struct libscols_filter *fltr, int *status) +{ + int rc, res = 0; + struct libscols_iter itr; + struct filter_param *prm = NULL; + + if (!ln || !fltr) + return -EINVAL; + + /* reset column data and types stored in the filter */ + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (filter_next_param(fltr, &itr, &prm) == 0) { + filter_param_reset_holder(prm); + } + + if (fltr->root) + rc = filter_eval_node(fltr, ln, fltr->root, &res); + else + rc = 0, res = 1; /* empty filter matches all lines */ + + if (rc == 0) { + struct libscols_counter *ct = NULL; + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_filter_next_counter(fltr, &itr, &ct) == 0) { + if ((ct->neg && res == 0) || res == 1) + filter_count_param(fltr, ln, ct); + } + } + + if (status) + *status = res; + DBG(FLTR, ul_debugobj(fltr, "filter done [rc=%d, status=%d]", rc, res)); + return rc; +} + +/** + * scols_filter_set_filler_cb: + * @fltr: filter instance + * @cb: application defined callback + * @userdata: pointer to private callback data + * + * The application can apply filter for empty lines to avoid filling the table + * with unnecessary data (for example if the line will be later removed from + * the table due to filter). + * + * This callback is used by filter to ask application to fill to the line data + * which are necessary to evaluate the filter expression. The callback + * arguments are filter, column number and userdata. + * + * <informalexample> + * <programlisting> + * ln = scols_table_new_line(tab, NULL); + * + * scols_filter_set_filler_cb(filter, my_filler, NULL); + * + * scols_line_apply_filter(line, filter, &status); + * if (status == 0) + * scols_table_remove_line(tab, line); + * else for (i = 0; i < ncolumns; i++) { + * if (scols_line_is_filled(line, i)) + * continue; + * my_filler(NULL, ln, i, NULL); + * } + * </programlisting> + * </informalexample> + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_filter_set_filler_cb(struct libscols_filter *fltr, + int (*cb)(struct libscols_filter *, + struct libscols_line *, size_t, void *), + void *userdata) +{ + if (!fltr) + return -EINVAL; + fltr->filler_cb = cb; + fltr->filler_data = userdata; + + return 0; +} + +/** + * scols_filter_new_counter: + * @fltr: filter instance + * + * Alocates a new counter instance into the filter. + * + * Returns: new counter or NULL in case of an error. + * + * Since: 2.40 + */ +struct libscols_counter *scols_filter_new_counter(struct libscols_filter *fltr) +{ + struct libscols_counter *ct; + + if (!fltr) + return NULL; + + ct = calloc(1, sizeof(*ct)); + if (!ct) + return NULL; + + DBG(FLTR, ul_debugobj(fltr, "alloc counter")); + + ct->filter = fltr; /* don't use ref.counting here */ + INIT_LIST_HEAD(&ct->counters); + list_add_tail(&ct->counters, &fltr->counters); + + + return ct; +} + +/** + * scols_counter_set_name: + * @ct: counter instance + * @name: something for humans + * + * The name is not use by library, it's just description usable for application + * when prints results from countes. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_name(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + return strdup_to_struct_member(ct, name, name); +} + +/** + * scols_counter_set_param: + * @ct: counter instance + * @name: holder (column) name + * + * Assigns a counter to the column. The name is used in the same way as names + * in the filter expression. This is usable for counter that calcuate with data + * from table cells (e.g. max, sum, etc.) + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_param(struct libscols_counter *ct, const char *name) +{ + if (!ct) + return -EINVAL; + + if (ct->param) { + filter_unref_node((struct filter_node *) ct->param); + ct->param = NULL; + } + if (name) { + ct->param = (struct filter_param *) + filter_new_param(ct->filter, SCOLS_DATA_U64, + F_HOLDER_COLUMN, (void *) name); + if (!ct->param) + return -ENOMEM; + } + return 0; +} + +/** + * scols_counter_set_func: + * @ct: counter instance + * @func: SCOLS_COUNTER_{COUNT,MAX,MIN,SUM} + * + * Defines function to calculate data. + * + * Returns: 0, a negative number in case of an error. + * + * Since: 2.40 + */ +int scols_counter_set_func(struct libscols_counter *ct, int func) +{ + if (!ct || func < 0 || func >= __SCOLS_NCOUNTES) + return -EINVAL; + + ct->func = func; + return 0; +} + +/** + * scols_counter_get_result: + * @ct: counter instance + * + * Returns: result from the counter + * + * Since: 2.40 + */ +unsigned long long scols_counter_get_result(struct libscols_counter *ct) +{ + return ct ? ct->result : 0; +} + +/** + * scols_counter_get_name: + * @ct: counter instance + * + * Returns: name of the counter. + * + * Since: 2.40 + */ +const char *scols_counter_get_name(struct libscols_counter *ct) +{ + return ct ? ct->name : NULL;; +} + +/** + * scols_filter_next_counter: + * @fltr: filter instance + * @itr: a pointer to a struct libscols_iter instance + * @ct: returns the next counter + * + * Finds the next counter and returns a pointer to it via @ct. + * + * Returns: 0, a negative value in case of an error, and 1 at the end. + * + * Since: 2.40 + */ +int scols_filter_next_counter(struct libscols_filter *fltr, + struct libscols_iter *itr, struct libscols_counter **ct) +{ + int rc = 1; + + if (!fltr || !itr || !ct) + return -EINVAL; + *ct = NULL; + + if (!itr->head) + SCOLS_ITER_INIT(itr, &fltr->counters); + if (itr->p != itr->head) { + SCOLS_ITER_ITERATE(itr, *ct, struct libscols_counter, counters); + rc = 0; + } + + return rc; +} diff --git a/libsmartcols/src/grouping.c b/libsmartcols/src/grouping.c index 0b27cb2..0f6fe78 100644 --- a/libsmartcols/src/grouping.c +++ b/libsmartcols/src/grouping.c @@ -278,7 +278,7 @@ static struct libscols_group **grpset_locate_freespace(struct libscols_table *tb DBG(TAB, ul_debugobj(tb, " realocate grpset [sz: old=%zu, new=%zu, new_chunks=%d]", tb->grpset_size, tb->grpset_size + wanted, chunks)); - tmp = realloc(tb->grpset, (tb->grpset_size + wanted) * sizeof(struct libscols_group *)); + tmp = reallocarray(tb->grpset, tb->grpset_size + wanted, sizeof(struct libscols_group *)); if (!tmp) return NULL; diff --git a/libsmartcols/src/init.c b/libsmartcols/src/init.c index dfd7510..5d51691 100644 --- a/libsmartcols/src/init.c +++ b/libsmartcols/src/init.c @@ -28,6 +28,8 @@ UL_DEBUG_DEFINE_MASKNAMES(libsmartcols) = { "group", SCOLS_DEBUG_GROUP, "lines grouping utils" }, { "line", SCOLS_DEBUG_LINE, "table line utils" }, { "tab", SCOLS_DEBUG_TAB, "table utils" }, + { "filter", SCOLS_DEBUG_FLTR, "lines filter" }, + { "fparam", SCOLS_DEBUG_FPARAM, "filter params" }, { NULL, 0, NULL } }; diff --git a/libsmartcols/src/libsmartcols.h.in b/libsmartcols/src/libsmartcols.h.in index f5820e9..c97651f 100644 --- a/libsmartcols/src/libsmartcols.h.in +++ b/libsmartcols/src/libsmartcols.h.in @@ -67,6 +67,29 @@ struct libscols_table; */ struct libscols_column; +/** + * libscols_filter: + * + * A filter - defines the filtering + */ +struct libscols_filter; + +/** + * libscols_counter: + * + * A filter counter + */ +struct libscols_counter; + +enum { + SCOLS_COUNTER_COUNT = 0, + SCOLS_COUNTER_MAX, + SCOLS_COUNTER_MIN, + SCOLS_COUNTER_SUM, + + __SCOLS_NCOUNTES +}; + /* iter.c */ enum { @@ -97,6 +120,18 @@ enum { SCOLS_JSON_ARRAY_STRING = 3, /* e.g. for multi-line (SCOLS_FL_WRAP) cells */ SCOLS_JSON_ARRAY_NUMBER = 4, SCOLS_JSON_BOOLEAN_OPTIONAL = 5, + SCOLS_JSON_FLOAT = 6 +}; + +/* + * Types used by filters and counters + */ +enum { + SCOLS_DATA_NONE = 0, /* default */ + SCOLS_DATA_U64, /* uint64_t */ + SCOLS_DATA_BOOLEAN, /* 0 or 1 */ + SCOLS_DATA_FLOAT, /* long double */ + SCOLS_DATA_STRING }; /* @@ -146,7 +181,11 @@ extern int scols_cell_copy_content(struct libscols_cell *dest, const struct libscols_cell *src); extern int scols_cell_set_data(struct libscols_cell *ce, const char *data); extern int scols_cell_refer_data(struct libscols_cell *ce, char *data); +extern int scols_cell_refer_memory(struct libscols_cell *ce, char *data, size_t datasiz); + extern const char *scols_cell_get_data(const struct libscols_cell *ce); +extern size_t scols_cell_get_datasiz(struct libscols_cell *ce); + extern int scols_cell_set_color(struct libscols_cell *ce, const char *color); extern const char *scols_cell_get_color(const struct libscols_cell *ce); @@ -159,6 +198,7 @@ extern int scols_cell_set_userdata(struct libscols_cell *ce, void *data); extern int scols_cmpstr_cells(struct libscols_cell *a, struct libscols_cell *b, void *data); + /* column.c */ extern int scols_column_is_tree(const struct libscols_column *cl); extern int scols_column_is_trunc(const struct libscols_column *cl); @@ -177,6 +217,9 @@ extern const char *scols_column_get_safechars(const struct libscols_column *cl); extern int scols_column_set_json_type(struct libscols_column *cl, int type); extern int scols_column_get_json_type(const struct libscols_column *cl); +extern int scols_column_set_data_type(struct libscols_column *cl, int type); +extern int scols_column_get_data_type(const struct libscols_column *cl); + extern int scols_column_set_flags(struct libscols_column *cl, int flags); extern int scols_column_get_flags(const struct libscols_column *cl); extern struct libscols_column *scols_new_column(void); @@ -193,6 +236,7 @@ extern struct libscols_table *scols_column_get_table(const struct libscols_colum extern int scols_column_set_name(struct libscols_column *cl, const char *name); extern const char *scols_column_get_name(struct libscols_column *cl); extern const char *scols_column_get_name_as_shellvar(struct libscols_column *cl); +extern int scols_shellvar_name(const char *name, char **buf, size_t *bufsz); extern int scols_column_set_properties(struct libscols_column *cl, const char *opts); @@ -208,9 +252,21 @@ extern int scols_column_set_wrapfunc(struct libscols_column *cl, char *, void *), void *userdata); +extern int scols_column_set_data_func(struct libscols_column *cl, + void *(*datafunc)(const struct libscols_column *, + struct libscols_cell *, + void *), + void *userdata); +extern int scols_column_has_data_func(struct libscols_column *cl); + extern char *scols_wrapnl_nextchunk(const struct libscols_column *cl, char *data, void *userdata); extern size_t scols_wrapnl_chunksize(const struct libscols_column *cl, const char *data, void *userdata); +extern char *scols_wrapzero_nextchunk(const struct libscols_column *cl, char *data, void *userdata); + +extern int scols_column_get_wrap_data(const struct libscols_column *cl, + char **data, size_t *datasiz, char **cur, char **next); + /* line.c */ extern struct libscols_line *scols_new_line(void); extern void scols_ref_line(struct libscols_line *ln); @@ -235,6 +291,7 @@ extern struct libscols_cell *scols_line_get_column_cell( struct libscols_column *cl); extern int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data); extern int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data); +extern int scols_line_is_filled(struct libscols_line *ln, size_t n); extern int scols_line_set_column_data(struct libscols_line *ln, struct libscols_column *cl, const char *data); extern const char *scols_line_get_column_data(struct libscols_line *ln, struct libscols_column *cl); extern int scols_line_refer_column_data(struct libscols_line *ln, struct libscols_column *cl, char *data); @@ -310,6 +367,12 @@ extern int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce extern int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl); extern int scols_sort_table_by_tree(struct libscols_table *tb); + +extern int scols_table_get_cursor(struct libscols_table *tb, + struct libscols_line **ln, + struct libscols_column **cl, + struct libscols_cell **ce); + /* * */ @@ -342,6 +405,38 @@ extern int scols_table_print_range_to_string( struct libscols_table *tb, int scols_line_link_group(struct libscols_line *ln, struct libscols_line *member, int id); int scols_table_group_lines(struct libscols_table *tb, struct libscols_line *ln, struct libscols_line *member, int id); + +/* filter.c */ +extern int scols_filter_parse_string(struct libscols_filter *fltr, const char *str); +extern struct libscols_filter *scols_new_filter(const char *str); +extern void scols_ref_filter(struct libscols_filter *fltr); +extern void scols_unref_filter(struct libscols_filter *fltr); +extern int scols_dump_filter(struct libscols_filter *fltr, FILE *out); +extern const char *scols_filter_get_errmsg(struct libscols_filter *fltr); + +extern int scols_line_apply_filter(struct libscols_line *ln, + struct libscols_filter *fltr, int *status); + +extern int scols_filter_next_holder(struct libscols_filter *fltr, + struct libscols_iter *itr, const char **name, int type); +extern int scols_filter_assign_column(struct libscols_filter *fltr, + struct libscols_iter *itr, + const char *name, struct libscols_column *col); +extern int scols_filter_set_filler_cb(struct libscols_filter *fltr, + int (*cb)(struct libscols_filter *, + struct libscols_line *, size_t, void *), + void *userdata); + +extern struct libscols_counter *scols_filter_new_counter(struct libscols_filter *fltr); +extern int scols_counter_set_name(struct libscols_counter *ct, const char *name); +extern int scols_counter_set_param(struct libscols_counter *ct, const char *name); +extern int scols_counter_set_func(struct libscols_counter *ct, int func); + +extern unsigned long long scols_counter_get_result(struct libscols_counter *ct); +extern const char *scols_counter_get_name(struct libscols_counter *ct); +extern int scols_filter_next_counter(struct libscols_filter *fltr, + struct libscols_iter *itr, struct libscols_counter **ct); + #ifdef __cplusplus } #endif diff --git a/libsmartcols/src/libsmartcols.sym b/libsmartcols/src/libsmartcols.sym index 4499908..41c7455 100644 --- a/libsmartcols/src/libsmartcols.sym +++ b/libsmartcols/src/libsmartcols.sym @@ -210,8 +210,37 @@ SMARTCOLS_2.38 { scols_table_enable_shellvar; } SMARTCOLS_2.35; - SMARTCOLS_2.39 { scols_column_set_properties; scols_table_get_column_by_name; } SMARTCOLS_2.38; + +SMARTCOLS_2.40 { + scols_table_get_cursor; + scols_cell_refer_memory; + scols_cell_get_datasiz; + scols_wrapzero_nextchunk; + scols_column_get_wrap_data; + scols_dump_filter; + scols_filter_parse_string; + scols_new_filter; + scols_unref_filter; + scols_filter_get_errmsg; + scols_filter_next_holder; + scols_filter_assign_column; + scols_line_apply_filter; + scols_filter_set_filler_cb; + scols_line_is_filled; + scols_filter_new_counter; + scols_counter_set_name; + scols_counter_set_param; + scols_counter_set_func; + scols_counter_get_result; + scols_counter_get_name; + scols_filter_next_counter; + scols_shellvar_name; + scols_column_set_data_func; + scols_column_has_data_func; + scols_column_set_data_type; + scols_column_get_data_type; +} SMARTCOLS_2.39; diff --git a/libsmartcols/src/line.c b/libsmartcols/src/line.c index cab99c5..2289db0 100644 --- a/libsmartcols/src/line.c +++ b/libsmartcols/src/line.c @@ -136,7 +136,7 @@ int scols_line_alloc_cells(struct libscols_line *ln, size_t n) DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n)); - ce = realloc(ln->cells, n * sizeof(struct libscols_cell)); + ce = reallocarray(ln->cells, n, sizeof(struct libscols_cell)); if (!ce) return -errno; @@ -505,6 +505,20 @@ int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data) } /** + * scols_line_is_filled: + * @ln: a pointer to a struct libscols_line instance + * @n: number of the cell + * + * Returns: 0 or 1 if cell was already filled (note that NULL is also valid filler) + */ +int scols_line_is_filled(struct libscols_line *ln, size_t n) +{ + struct libscols_cell *ce = scols_line_get_cell(ln, n); + + return ce ? ce->is_filled : 0; +} + +/** * scols_line_refer_column_data: * @ln: a pointer to a struct libscols_line instance * @cl: column, whose data is to be set diff --git a/libsmartcols/src/print.c b/libsmartcols/src/print.c index 6a7e6da..88ab5a2 100644 --- a/libsmartcols/src/print.c +++ b/libsmartcols/src/print.c @@ -232,21 +232,6 @@ static int groups_ascii_art_to_buffer( struct libscols_table *tb, return 0; } -static int has_pending_data(struct libscols_table *tb) -{ - struct libscols_column *cl; - struct libscols_iter itr; - - scols_reset_iter(&itr, SCOLS_ITER_FORWARD); - while (scols_table_next_column(tb, &itr, &cl) == 0) { - if (scols_column_is_hidden(cl)) - continue; - if (cl->pending_data) - return 1; - } - return 0; -} - static void fputs_color_reset(struct libscols_table *tb) { if (tb->cur_color) { @@ -349,7 +334,7 @@ static void print_empty_cell(struct libscols_table *tb, tree_ascii_art_to_buffer(tb, ln, &art); - if (!list_empty(&ln->ln_branch) && has_pending_data(tb)) + if (!list_empty(&ln->ln_branch)) ul_buffer_append_string(&art, vertical_symbol(tb)); if (scols_table_is_noencoding(tb)) @@ -423,95 +408,43 @@ static void print_newline_padding(struct libscols_table *tb, fputs_color_line_close(tb); } -/* - * Pending data - * - * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is - * printed as usually and output is truncated to match column width. - * - * The rest of the long text is printed on next extra line(s). The extra lines - * don't exist in the table (not represented by libscols_line). The data for - * the extra lines are stored in libscols_column->pending_data_buf and the - * function print_line() adds extra lines until the buffer is not empty in all - * columns. - */ - -/* set data that will be printed by extra lines */ -static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz) +static int print_pending_data(struct libscols_table *tb, struct ul_buffer *buf) { - char *p = NULL; - - if (data && *data) { - DBG(COL, ul_debugobj(cl, "setting pending data")); - assert(sz); - p = strdup(data); - if (!p) - return -ENOMEM; - } - - free(cl->pending_data_buf); - cl->pending_data_buf = p; - cl->pending_data_sz = sz; - cl->pending_data = cl->pending_data_buf; - return 0; -} - -/* the next extra line has been printed, move pending data cursor */ -static int step_pending_data(struct libscols_column *cl, size_t bytes) -{ - DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes)); - - if (bytes >= cl->pending_data_sz) - return set_pending_data(cl, NULL, 0); - - cl->pending_data += bytes; - cl->pending_data_sz -= bytes; - return 0; -} - -/* print next pending data for the column @cl */ -static int print_pending_data( - struct libscols_table *tb, - struct libscols_column *cl, - struct libscols_line *ln, /* optional */ - struct libscols_cell *ce) -{ - size_t width = cl->width, bytes; - size_t len = width, i; + struct libscols_line *ln; + struct libscols_column *cl; + struct libscols_cell *ce; char *data; - char *nextchunk = NULL; + size_t i, width = 0, len = 0, bytes = 0; - if (!cl->pending_data) - return 0; + scols_table_get_cursor(tb, &ln, &cl, &ce); + + width = cl->width; if (!width) return -EINVAL; DBG(COL, ul_debugobj(cl, "printing pending data")); - data = strdup(cl->pending_data); + if (scols_table_is_noencoding(tb)) + data = ul_buffer_get_data(buf, &bytes, &len); + else + data = ul_buffer_get_safe_data(buf, &bytes, &len, scols_column_get_safechars(cl)); + if (!data) - goto err; + return 0; - if (scols_column_is_customwrap(cl) - && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { - bytes = nextchunk - data; + /* standard multi-line cell */ + if (len > width && scols_column_is_wrap(cl) + && !scols_column_is_customwrap(cl)) { - len = scols_table_is_noencoding(tb) ? - mbs_nwidth(data, bytes) : - mbs_safe_nwidth(data, bytes, NULL); - } else + len = width; bytes = mbs_truncate(data, &len); - if (bytes == (size_t) -1) - goto err; - - if (bytes) - step_pending_data(cl, bytes); + if (bytes != (size_t) -1 && bytes > 0) + scols_column_move_wrap(cl, mbs_safe_decode_size(data)); + } fputs_color_cell_open(tb, cl, ln, ce); - fputs(data, tb->out); - free(data); /* minout -- don't fill */ if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) { @@ -535,9 +468,6 @@ static int print_pending_data( fputs(colsep(tb), tb->out); return 0; -err: - free(data); - return -errno; } static void print_json_data(struct libscols_table *tb, @@ -551,6 +481,7 @@ static void print_json_data(struct libscols_table *tb, ul_jsonwrt_value_s(&tb->json, name, data); break; case SCOLS_JSON_NUMBER: + case SCOLS_JSON_FLOAT: /* name: 123 */ ul_jsonwrt_value_raw(&tb->json, name, data); break; @@ -574,47 +505,45 @@ static void print_json_data(struct libscols_table *tb, if (!scols_column_is_customwrap(cl)) ul_jsonwrt_value_s(&tb->json, NULL, data); else do { - char *next = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data); - - if (cl->json_type == SCOLS_JSON_ARRAY_STRING) - ul_jsonwrt_value_s(&tb->json, NULL, data); - else - ul_jsonwrt_value_raw(&tb->json, NULL, data); - data = next; - } while (data); + if (cl->json_type == SCOLS_JSON_ARRAY_STRING) + ul_jsonwrt_value_s(&tb->json, NULL, data); + else + ul_jsonwrt_value_raw(&tb->json, NULL, data); + } while (scols_column_next_wrap(cl, NULL, &data) == 0); ul_jsonwrt_array_close(&tb->json); break; } } -static int print_data(struct libscols_table *tb, - struct libscols_column *cl, - struct libscols_line *ln, /* optional */ - struct libscols_cell *ce, /* optional */ - struct ul_buffer *buf) +static int print_data(struct libscols_table *tb, struct ul_buffer *buf) { + struct libscols_line *ln; /* NULL for header line! */ + struct libscols_column *cl; + struct libscols_cell *ce; size_t len = 0, i, width, bytes; - char *data, *nextchunk; + char *data = NULL; const char *name = NULL; int is_last; assert(tb); - assert(cl); - data = ul_buffer_get_data(buf, NULL, NULL); - if (!data) - data = ""; + scols_table_get_cursor(tb, &ln, &cl, &ce); + assert(cl); if (tb->format != SCOLS_FMT_HUMAN) { name = scols_table_is_shellvar(tb) ? scols_column_get_name_as_shellvar(cl) : scols_column_get_name(cl); + + data = ul_buffer_get_data(buf, NULL, NULL); + if (!data) + data = ""; } is_last = is_last_column(cl); - if (is_last && scols_table_is_json(tb) && + if (ln && is_last && scols_table_is_json(tb) && scols_table_is_tree(tb) && has_children(ln)) /* "children": [] is the real last value */ is_last = 0; @@ -642,7 +571,7 @@ static int print_data(struct libscols_table *tb, break; /* continue below */ } - /* Encode. Note that 'len' and 'width' are number of cells, not bytes. + /* Encode. Note that 'len' and 'width' are number of glyphs not bytes. */ if (scols_table_is_noencoding(tb)) data = ul_buffer_get_data(buf, &bytes, &len); @@ -653,17 +582,6 @@ static int print_data(struct libscols_table *tb, data = ""; width = cl->width; - /* custom multi-line cell based */ - if (*data && scols_column_is_customwrap(cl) - && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) { - set_pending_data(cl, nextchunk, bytes - (nextchunk - data)); - bytes = nextchunk - data; - - len = scols_table_is_noencoding(tb) ? - mbs_nwidth(data, bytes) : - mbs_safe_nwidth(data, bytes, NULL); - } - if (is_last && len < width && !scols_table_is_maxout(tb) @@ -679,12 +597,12 @@ static int print_data(struct libscols_table *tb, /* standard multi-line cell */ if (len > width && scols_column_is_wrap(cl) && !scols_column_is_customwrap(cl)) { - set_pending_data(cl, data, bytes); len = width; bytes = mbs_truncate(data, &len); - if (bytes != (size_t) -1 && bytes > 0) - step_pending_data(cl, bytes); + + if (bytes != (size_t) -1 && bytes > 0) + scols_column_move_wrap(cl, mbs_safe_decode_size(data)); } if (bytes == (size_t) -1) { @@ -701,7 +619,6 @@ static int print_data(struct libscols_table *tb, len = width; } fputs(data, tb->out); - } /* minout -- don't fill */ @@ -732,16 +649,24 @@ static int print_data(struct libscols_table *tb, return 0; } -int __cell_to_buffer(struct libscols_table *tb, - struct libscols_line *ln, - struct libscols_column *cl, - struct ul_buffer *buf) +/* + * Copy current cell data to buffer. The @cal means "calculation" phase. + */ +int __cursor_to_buffer(struct libscols_table *tb, + struct ul_buffer *buf, + int cal) { - const char *data; + const char *data = NULL; + size_t datasiz = 0; struct libscols_cell *ce; + struct libscols_line *ln; + struct libscols_column *cl; int rc = 0; assert(tb); + + scols_table_get_cursor(tb, &ln, &cl, &ce); + assert(ln); assert(cl); assert(buf); @@ -749,11 +674,8 @@ int __cell_to_buffer(struct libscols_table *tb, ul_buffer_reset_data(buf); - ce = scols_line_get_cell(ln, cl->seqnum); - data = ce ? scols_cell_get_data(ce) : NULL; - if (!scols_column_is_tree(cl)) - return data ? ul_buffer_append_string(buf, data) : 0; + goto notree; /* * Group stuff @@ -775,9 +697,79 @@ int __cell_to_buffer(struct libscols_table *tb, if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb)) ul_buffer_save_pointer(buf, SCOLS_BUFPTR_TREEEND); +notree: + if (!rc && ce) { + int do_wrap = scols_column_is_wrap(cl); + + /* Disable multi-line cells for "raw" and "export" formats. + * JSON uses data wrapping to generate arrays */ + if (do_wrap && (tb->format == SCOLS_FMT_RAW || + tb->format == SCOLS_FMT_EXPORT)) + do_wrap = 0; + + /* Wrapping enabled; append the next chunk if cell data */ + if (do_wrap) { + char *x = NULL; + + rc = cal ? scols_column_greatest_wrap(cl, ce, &x) : + scols_column_next_wrap(cl, ce, &x); + /* rc: error: <0; nodata: 1; success: 0 */ + if (rc < 0) + goto done; + data = x; + rc = 0; + if (data && *data) + datasiz = strlen(data); + if (data && datasiz) + rc = ul_buffer_append_data(buf, data, datasiz); + + /* Wrapping disabled, but data maintained by custom wrapping + * callback. Try to use data as a string, if not possible, + * append all chunks separated by \n (backward compatibility). + * */ + } else if (scols_column_is_customwrap(cl)) { + size_t len; + int i = 0; + char *x = NULL; + + data = scols_cell_get_data(ce); + datasiz = scols_cell_get_datasiz(ce); + len = data ? strnlen(data, datasiz) : 0; + + if (len && len + 1 == datasiz) + rc = ul_buffer_append_data(buf, data, datasiz); + + else while (scols_column_next_wrap(cl, ce, &x) == 0) { + /* non-string data in cell, use a nextchunk callback */ + if (!x) + continue; + datasiz = strlen(x); + if (i) + rc = ul_buffer_append_data(buf, "\n", 1); + if (!rc) + rc = ul_buffer_append_data(buf, x, datasiz); + i++; + } + + /* Wrapping disabled; let's use data as a classic string. */ + } else { + data = scols_cell_get_data(ce); + datasiz = scols_cell_get_datasiz(ce); + + if (data && *data && !datasiz) + datasiz = strlen(data); /* cell content may be updated */ - if (!rc && data) - rc = ul_buffer_append_string(buf, data); + if (data && datasiz) + rc = ul_buffer_append_data(buf, data, datasiz); + } + } + + /* reset wrapping after greatest chunk calculation */ + if (cal && scols_column_is_wrap(cl)) + scols_column_reset_wrap(cl); + +done: + DBG(COL, ul_debugobj(cl, "__cursor_to_buffer rc=%d", rc)); return rc; } @@ -801,16 +793,18 @@ static int print_line(struct libscols_table *tb, /* regular line */ scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - rc = __cell_to_buffer(tb, ln, cl, buf); - if (rc == 0) - rc = print_data(tb, cl, ln, - scols_line_get_cell(ln, cl->seqnum), - buf); - if (rc == 0 && cl->pending_data) + + scols_table_set_cursor(tb, ln, cl, scols_line_get_cell(ln, cl->seqnum)); + rc = __cursor_to_buffer(tb, buf, 0); + if (!rc) + rc = print_data(tb, buf); + if (!rc && scols_column_has_pending_wrap(cl)) pending = 1; + scols_table_reset_cursor(tb); } fputs_color_line_close(tb); @@ -821,16 +815,25 @@ static int print_line(struct libscols_table *tb, fputs(linesep(tb), tb->out); fputs_color_line_open(tb, ln); tb->termlines_used++; + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) { if (scols_column_is_hidden(cl)) continue; - if (cl->pending_data) { - rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum)); - if (rc == 0 && cl->pending_data) + + scols_table_set_cursor(tb, ln, cl, scols_line_get_cell(ln, cl->seqnum)); + if (scols_column_has_pending_wrap(cl)) { + rc = __cursor_to_buffer(tb, buf, 0); + if (!rc) + rc = print_pending_data(tb, buf); + if (!rc && scols_column_has_pending_wrap(cl)) pending = 1; + if (!rc && !pending) + scols_column_reset_wrap(cl); } else print_empty_cell(tb, cl, ln, NULL, ul_buffer_get_bufsiz(buf)); + scols_table_reset_cursor(tb); } fputs_color_line_close(tb); } @@ -963,6 +966,7 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf) continue; ul_buffer_reset_data(buf); + scols_table_set_cursor(tb, NULL, cl, &cl->header); if (cl->is_groups && scols_table_is_tree(tb) && scols_column_is_tree(cl)) { @@ -979,7 +983,8 @@ int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf) scols_column_get_name_as_shellvar(cl) : scols_column_get_name(cl)); if (!rc) - rc = print_data(tb, cl, NULL, &cl->header, buf); + rc = print_data(tb, buf); + scols_table_reset_cursor(tb); } if (rc == 0) { diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index 8a7ee9b..75fb7ff 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -19,6 +19,8 @@ #include "debug.h" #include "buffer.h" +#include <stdbool.h> + #include "libsmartcols.h" /* @@ -32,6 +34,8 @@ #define SCOLS_DEBUG_COL (1 << 5) #define SCOLS_DEBUG_BUFF (1 << 6) #define SCOLS_DEBUG_GROUP (1 << 7) +#define SCOLS_DEBUG_FLTR (1 << 8) +#define SCOLS_DEBUG_FPARAM (1 << 9) #define SCOLS_DEBUG_ALL 0xFFFF UL_DEBUG_DECLARE_MASK(libsmartcols); @@ -80,10 +84,13 @@ struct libscols_symbols { */ struct libscols_cell { char *data; + size_t datasiz; char *color; void *userdata; int flags; size_t width; + + unsigned int is_filled : 1; }; extern int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn); @@ -112,26 +119,32 @@ struct libscols_column { struct libscols_wstat wstat; /* private __scols_calculate() data */ int json_type; /* SCOLS_JSON_* */ + int data_type; /* SCOLS_DATA_* */ int flags; char *color; /* default column color */ char *safechars; /* do not encode this bytes */ - char *pending_data; - size_t pending_data_sz; - char *pending_data_buf; - int (*cmpfunc)(struct libscols_cell *, struct libscols_cell *, void *); /* cells comparison function */ void *cmpfunc_data; - size_t (*wrap_chunksize)(const struct libscols_column *, - const char *, void *); - char *(*wrap_nextchunk)(const struct libscols_column *, - char *, void *); + /* multi-line cell data wrapping */ + char *(*wrap_nextchunk)(const struct libscols_column *, char *, void *); void *wrapfunc_data; + size_t wrap_datasz; + size_t wrap_datamax; + char *wrap_data; + char *wrap_cur; + char *wrap_next; + struct libscols_cell *wrap_cell; + + void *(*datafunc)(const struct libscols_column *, + struct libscols_cell *, + void *); + void *datafunc_data; struct libscols_cell header; /* column name with color etc. */ char *shellvar; /* raw colum name in shell compatible format */ @@ -247,6 +260,10 @@ struct libscols_table { const char *cur_color; /* current active color when printing */ + struct libscols_cell *cur_cell; /* currently used cell */ + struct libscols_line *cur_line; /* currently used line */ + struct libscols_column *cur_column; /* currently used column */ + /* flags */ unsigned int ascii :1, /* don't use unicode */ colors_wanted :1, /* enable colors */ @@ -299,6 +316,18 @@ int scols_line_next_group_child(struct libscols_line *ln, struct libscols_iter *itr, struct libscols_line **chld); +/* + * column.c + */ +void scols_column_reset_wrap(struct libscols_column *cl); +int scols_column_next_wrap( struct libscols_column *cl, + struct libscols_cell *ce, + char **data); +int scols_column_greatest_wrap( struct libscols_column *cl, + struct libscols_cell *ce, + char **data); +int scols_column_has_pending_wrap(struct libscols_column *cl); +int scols_column_move_wrap(struct libscols_column *cl, size_t bytes); /* * table.c @@ -306,6 +335,13 @@ int scols_line_next_group_child(struct libscols_line *ln, int scols_table_next_group(struct libscols_table *tb, struct libscols_iter *itr, struct libscols_group **gr); +int scols_table_set_cursor(struct libscols_table *tb, + struct libscols_line *ln, + struct libscols_column *cl, + struct libscols_cell *ce); + +#define scols_table_reset_cursor(_t) scols_table_set_cursor((_t), NULL, NULL, NULL) + /* * grouping.c @@ -339,10 +375,9 @@ extern int __scols_calculate(struct libscols_table *tb, struct ul_buffer *buf); /* * print.c */ -extern int __cell_to_buffer(struct libscols_table *tb, - struct libscols_line *ln, - struct libscols_column *cl, - struct ul_buffer *buf); +int __cursor_to_buffer(struct libscols_table *tb, + struct ul_buffer *buf, + int cal); void __scols_cleanup_printing(struct libscols_table *tb, struct ul_buffer *buf); int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf); @@ -452,4 +487,124 @@ static inline int has_group_children(struct libscols_line *ln) return ln && ln->group && !list_empty(&ln->group->gr_children); } +/* + * Filter stuff + */ +enum filter_holder { + F_HOLDER_NONE, + F_HOLDER_COLUMN /* column name */ +}; + +/* node types */ +enum filter_ntype { + F_NODE_PARAM, + F_NODE_EXPR +}; + +/* expresion types */ +enum filter_etype { + F_EXPR_AND, + F_EXPR_OR, + F_EXPR_NEG, + + F_EXPR_EQ, + F_EXPR_NE, + + F_EXPR_LT, + F_EXPR_LE, + F_EXPR_GT, + F_EXPR_GE, + + F_EXPR_REG, + F_EXPR_NREG, +}; + +struct filter_node { + enum filter_ntype type; + int refcount; +}; + +#define filter_node_get_type(n) (((struct filter_node *)(n))->type) + +struct filter_param; +struct filter_expr; + +struct libscols_counter { + char *name; + struct list_head counters; + struct filter_param *param; + struct libscols_filter *filter; + + int func; + unsigned long long result; + + unsigned int neg : 1, + has_result : 1; +}; + +struct libscols_filter { + int refcount; + char *errmsg; + struct filter_node *root; + FILE *src; + + int (*filler_cb)(struct libscols_filter *, struct libscols_line *, size_t, void *); + void *filler_data; + + struct list_head params; + struct list_head counters; +}; + +struct filter_node *__filter_new_node(enum filter_ntype type, size_t sz); +void filter_ref_node(struct filter_node *n); +void filter_unref_node(struct filter_node *n); + +void filter_dump_node(struct ul_jsonwrt *json, struct filter_node *n); +int filter_eval_node(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_node *n, int *status); +/* param */ +int filter_compile_param(struct libscols_filter *fltr, struct filter_param *n); +void filter_dump_param(struct ul_jsonwrt *json, struct filter_param *n); +int filter_eval_param(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_param *n, int *status); +void filter_free_param(struct filter_param *n); +int filter_param_reset_holder(struct filter_param *n); +int filter_param_get_datatype(struct filter_param *n); + +int filter_next_param(struct libscols_filter *fltr, + struct libscols_iter *itr, struct filter_param **prm); + +int filter_compare_params(struct libscols_filter *fltr, + enum filter_etype oper, + struct filter_param *l, + struct filter_param *r, + int *status); +int filter_cast_param(struct libscols_filter *fltr, + struct libscols_line *ln, + int type, + struct filter_param *n, + struct filter_param **result); + +int is_filter_holder_node(struct filter_node *n); + +int filter_count_param(struct libscols_filter *fltr, + struct libscols_line *ln, + struct libscols_counter *ct); + +/* expr */ +void filter_free_expr(struct filter_expr *n); +void filter_dump_expr(struct ul_jsonwrt *json, struct filter_expr *n); +int filter_eval_expr(struct libscols_filter *fltr, struct libscols_line *ln, + struct filter_expr *n, int *status); + +/* required by parser */ +struct filter_node *filter_new_param(struct libscols_filter *filter, + int type, + enum filter_holder holder, + void *data); +struct filter_node *filter_new_expr(struct libscols_filter *filter, + enum filter_etype type, + struct filter_node *left, + struct filter_node *right); + #endif /* _LIBSMARTCOLS_PRIVATE_H */ diff --git a/libsmartcols/src/table.c b/libsmartcols/src/table.c index 8449c4f..3d23da8 100644 --- a/libsmartcols/src/table.c +++ b/libsmartcols/src/table.c @@ -521,6 +521,50 @@ size_t scols_table_get_nlines(const struct libscols_table *tb) return tb->nlines; } + +int scols_table_set_cursor(struct libscols_table *tb, + struct libscols_line *ln, + struct libscols_column *cl, + struct libscols_cell *ce) +{ + if (!tb) + return -EINVAL; + + tb->cur_line = ln; + tb->cur_column = cl; + tb->cur_cell = ce; + + return 0; +} + +/** + * scols_table_get_cursor: + * @tb: table + * @ln: returns current line (optional) + * @cl: returns current column (optional) + * @ce: returns current cell (optional) + * + * Returns: 0 on success, negative number in case of error. + * + * Since: 2.40 + */ +int scols_table_get_cursor(struct libscols_table *tb, + struct libscols_line **ln, + struct libscols_column **cl, + struct libscols_cell **ce) +{ + if (!tb) + return -EINVAL; + + if (ln) + *ln = tb->cur_line; + if (cl) + *cl = tb->cur_column; + if (ce) + *ce = tb->cur_cell; + return 0; +} + /** * scols_table_set_stream: * @tb: table @@ -632,6 +676,15 @@ struct libscols_column *scols_table_get_column_by_name( if (cn && strcmp(cn, name) == 0) return cl; } + + scols_reset_iter(&itr, SCOLS_ITER_FORWARD); + while (scols_table_next_column(tb, &itr, &cl) == 0) { + const char *cn = scols_column_get_name_as_shellvar(cl); + + if (cn && strcmp(cn, name) == 0) + return cl; + } + return NULL; } diff --git a/libuuid/man/uuid.3 b/libuuid/man/uuid.3 index ce71643..0b6d8bd 100644 --- a/libuuid/man/uuid.3 +++ b/libuuid/man/uuid.3 @@ -2,12 +2,12 @@ .\" Title: uuid .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_clear.3 b/libuuid/man/uuid_clear.3 index ef242ea..0ba899c 100644 --- a/libuuid/man/uuid_clear.3 +++ b/libuuid/man/uuid_clear.3 @@ -2,12 +2,12 @@ .\" Title: uuid_clear .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_CLEAR" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_CLEAR" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_compare.3 b/libuuid/man/uuid_compare.3 index 18ee6e0..2d37b5e 100644 --- a/libuuid/man/uuid_compare.3 +++ b/libuuid/man/uuid_compare.3 @@ -2,12 +2,12 @@ .\" Title: uuid_compare .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_COMPARE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_COMPARE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_copy.3 b/libuuid/man/uuid_copy.3 index ed08bd3..ce2834f 100644 --- a/libuuid/man/uuid_copy.3 +++ b/libuuid/man/uuid_copy.3 @@ -2,12 +2,12 @@ .\" Title: uuid_copy .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_COPY" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_COPY" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_generate.3 b/libuuid/man/uuid_generate.3 index dba2eee..cb88247 100644 --- a/libuuid/man/uuid_generate.3 +++ b/libuuid/man/uuid_generate.3 @@ -2,12 +2,12 @@ .\" Title: uuid_generate .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_GENERATE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_GENERATE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_is_null.3 b/libuuid/man/uuid_is_null.3 index a4cf3cc..dc61071 100644 --- a/libuuid/man/uuid_is_null.3 +++ b/libuuid/man/uuid_is_null.3 @@ -2,12 +2,12 @@ .\" Title: uuid_is_null .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_IS_NULL" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_IS_NULL" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_parse.3 b/libuuid/man/uuid_parse.3 index c70980e..0a58da9 100644 --- a/libuuid/man/uuid_parse.3 +++ b/libuuid/man/uuid_parse.3 @@ -2,12 +2,12 @@ .\" Title: uuid_parse .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_PARSE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_PARSE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_time.3 b/libuuid/man/uuid_time.3 index 475f095..18c9f4f 100644 --- a/libuuid/man/uuid_time.3 +++ b/libuuid/man/uuid_time.3 @@ -2,12 +2,12 @@ .\" Title: uuid_time .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_TIME" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_TIME" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/man/uuid_unparse.3 b/libuuid/man/uuid_unparse.3 index a1f96fd..3b86df4 100644 --- a/libuuid/man/uuid_unparse.3 +++ b/libuuid/man/uuid_unparse.3 @@ -2,12 +2,12 @@ .\" Title: uuid_unparse .\" Author: [see the "AUTHOR(S)" section] .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2023-10-23 +.\" Date: 2024-01-31 .\" Manual: Programmer's Manual -.\" Source: util-linux 2.39.3 +.\" Source: util-linux 2.40 .\" Language: English .\" -.TH "UUID_UNPARSE" "3" "2023-10-23" "util\-linux 2.39.3" "Programmer\*(Aqs Manual" +.TH "UUID_UNPARSE" "3" "2024-01-31" "util\-linux 2.40" "Programmer\*(Aqs Manual" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 diff --git a/libuuid/src/gen_uuid.c b/libuuid/src/gen_uuid.c index 826cd22..59e8c23 100644 --- a/libuuid/src/gen_uuid.c +++ b/libuuid/src/gen_uuid.c @@ -207,6 +207,28 @@ static int get_node_id(unsigned char *node_id) return 0; } +enum { STATE_FD_ERROR = -1, STATE_FD_INIT = -2 }; + +static int state_fd_init(const char *clock_file, FILE **fp) +{ + mode_t save_umask; + int state_fd; + FILE *state_f; + + save_umask = umask(0); + state_fd = open(clock_file, O_RDWR|O_CREAT|O_CLOEXEC, 0660); + (void) umask(save_umask); + if (state_fd != -1) { + state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR); + if (!state_f) { + close(state_fd); + state_fd = STATE_FD_ERROR; + } else + *fp = state_f; + } + return state_fd; +} + /* Assume that the gettimeofday() has microsecond granularity */ #define MAX_ADJUSTMENT 10 /* Reserve a clock_seq value for the 'continuous clock' implementation */ @@ -223,32 +245,16 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low, { THREAD_LOCAL int adjustment = 0; THREAD_LOCAL struct timeval last = {0, 0}; - THREAD_LOCAL int state_fd = -2; + THREAD_LOCAL int state_fd = STATE_FD_INIT; THREAD_LOCAL FILE *state_f; THREAD_LOCAL uint16_t clock_seq; struct timeval tv; uint64_t clock_reg; - mode_t save_umask; int ret = 0; - if (state_fd == -1) - ret = -1; + if (state_fd == STATE_FD_INIT) + state_fd = state_fd_init(LIBUUID_CLOCK_FILE, &state_f); - if (state_fd == -2) { - save_umask = umask(0); - state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0660); - (void) umask(save_umask); - if (state_fd != -1) { - state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR); - if (!state_f) { - close(state_fd); - state_fd = -1; - ret = -1; - } - } - else - ret = -1; - } if (state_fd >= 0) { rewind(state_f); while (flock(state_fd, LOCK_EX) < 0) { @@ -256,11 +262,13 @@ static int get_clock(uint32_t *clock_high, uint32_t *clock_low, continue; fclose(state_f); close(state_fd); - state_fd = -1; + state_fd = STATE_FD_ERROR; ret = -1; break; } - } + } else + ret = -1; + if (state_fd >= 0) { unsigned int cl; unsigned long tv1, tv2; @@ -355,44 +363,94 @@ static uint64_t get_clock_counter(void) /* * Get continuous clock value. * - * Return -1 if there is no further clock counter available, + * Return -1 if there is no valid clock counter available, * otherwise return 0. * * This implementation doesn't deliver clock counters based on * the current time because last_clock_reg is only incremented * by the number of requested UUIDs. * max_clock_offset is used to limit the offset of last_clock_reg. + * used/reserved UUIDs are written to LIBUUID_CLOCK_CONT_FILE. */ static int get_clock_cont(uint32_t *clock_high, uint32_t *clock_low, int num, uint32_t max_clock_offset) { - /* 100ns based time offset according to RFC 4122. 4.1.4. */ + /* all 64bit clock_reg values in this function represent '100ns ticks' + * due to the combination of tv_usec + MAX_ADJUSTMENT */ + + /* time offset according to RFC 4122. 4.1.4. */ const uint64_t reg_offset = (((uint64_t) 0x01B21DD2) << 32) + 0x13814000; static uint64_t last_clock_reg = 0; - uint64_t clock_reg; + static uint64_t saved_clock_reg = 0; + static int state_fd = STATE_FD_INIT; + static FILE *state_f = NULL; + uint64_t clock_reg, next_clock_reg; - if (last_clock_reg == 0) - last_clock_reg = get_clock_counter(); + if (state_fd == STATE_FD_ERROR) + return -1; clock_reg = get_clock_counter(); + + if (state_fd == STATE_FD_INIT) { + struct stat st; + + state_fd = state_fd_init(LIBUUID_CLOCK_CONT_FILE, &state_f); + if (state_fd == STATE_FD_ERROR) + return -1; + + if (fstat(state_fd, &st)) + goto error; + + if (st.st_size) { + rewind(state_f); + if (fscanf(state_f, "cont: %"SCNu64"\n", &last_clock_reg) != 1) + goto error; + } else + last_clock_reg = clock_reg; + + saved_clock_reg = last_clock_reg; + } + if (max_clock_offset) { - uint64_t clock_offset = max_clock_offset * 10000000ULL; - if (last_clock_reg < (clock_reg - clock_offset)) - last_clock_reg = clock_reg - clock_offset; + uint64_t co = 10000000ULL * (uint64_t)max_clock_offset; // clock_offset in [100ns] + + if ((last_clock_reg + co) < clock_reg) + last_clock_reg = clock_reg - co; } clock_reg += MAX_ADJUSTMENT; - if ((last_clock_reg + num) >= clock_reg) + next_clock_reg = last_clock_reg + (uint64_t)num; + if (next_clock_reg >= clock_reg) return -1; + if (next_clock_reg >= saved_clock_reg) { + uint64_t cl = next_clock_reg + 100000000ULL; // 10s interval in [100ns] + int l; + + rewind(state_f); + l = fprintf(state_f, "cont: %020"PRIu64" \n", cl); + if (l < 30 || fflush(state_f)) + goto error; + saved_clock_reg = cl; + } + *clock_high = (last_clock_reg + reg_offset) >> 32; *clock_low = last_clock_reg + reg_offset; - last_clock_reg += num; + last_clock_reg = next_clock_reg; return 0; + +error: + if (state_fd >= 0) + close(state_fd); + if (state_f) + fclose(state_f); + state_fd = STATE_FD_ERROR; + state_f = NULL; + return -1; } #if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H) diff --git a/libuuid/src/libuuid.sym b/libuuid/src/libuuid.sym index 96372a8..0f67ede 100644 --- a/libuuid/src/libuuid.sym +++ b/libuuid/src/libuuid.sym @@ -52,6 +52,15 @@ global: uuid_parse_range; } UUID_2.31; +/* + * version(s) since util-linux.2.40 + */ +UUID_2.40 { +global: + uuid_time64; /* only on 32bit architectures with 64bit time_t */ +} UUID_2.36; + + /* * __uuid_* this is not part of the official API, this is diff --git a/libuuid/src/test_uuid.c b/libuuid/src/test_uuid.c index 9907911..06d1589 100644 --- a/libuuid/src/test_uuid.c +++ b/libuuid/src/test_uuid.c @@ -66,24 +66,30 @@ static int test_uuid(const char * uuid, int isValid) static int check_uuids_in_file(const char *file) { - int fd, ret = 0; - size_t sz; - char str[UUID_STR_LEN]; + int ret = 0; + size_t alloc = 0; + ssize_t sz; + char *str = NULL; + FILE *f; uuid_t uuidBits; - if ((fd = open(file, O_RDONLY)) < 0) { + if ((f = fopen(file, "r")) == NULL) { warn("%s", file); return 1; } - while ((sz = read(fd, str, sizeof(str))) != 0) { - str[sizeof(str) - 1] = '\0'; + while ((sz = getline(&str, &alloc, f)) != -1) { + if (sz == 0 || str[0] == '\n') + continue; + if (str[sz - 1] == '\n') + str[sz - 1] = '\0'; if (uuid_parse(str, uuidBits)) { warnx("%s: %s", file, str); ret++; } } - close(fd); + fclose(f); + free(str); return ret; } diff --git a/libuuid/src/uuid.h b/libuuid/src/uuid.h index e791abf..2e3642c 100644 --- a/libuuid/src/uuid.h +++ b/libuuid/src/uuid.h @@ -109,6 +109,9 @@ extern void uuid_unparse_lower(const uuid_t uu, char *out); extern void uuid_unparse_upper(const uuid_t uu, char *out); /* uuid_time.c */ +#if defined(__USE_TIME_BITS64) && defined(__GLIBC__) +# define uuid_time uuid_time64 +#endif extern time_t uuid_time(const uuid_t uu, struct timeval *ret_tv); extern int uuid_type(const uuid_t uu); extern int uuid_variant(const uuid_t uu); diff --git a/libuuid/src/uuidP.h b/libuuid/src/uuidP.h index 200702c..6face82 100644 --- a/libuuid/src/uuidP.h +++ b/libuuid/src/uuidP.h @@ -39,7 +39,8 @@ #include "uuid.h" -#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt" +#define LIBUUID_CLOCK_FILE _PATH_LOCALSTATEDIR "/lib/libuuid/clock.txt" +#define LIBUUID_CLOCK_CONT_FILE _PATH_LOCALSTATEDIR "/lib/libuuid/clock-cont.txt" /* * Offset between 15-Oct-1582 and 1-Jan-70 diff --git a/libuuid/src/uuid_time.c b/libuuid/src/uuid_time.c index 6f07d51..9b415b3 100644 --- a/libuuid/src/uuid_time.c +++ b/libuuid/src/uuid_time.c @@ -40,6 +40,7 @@ #define UUID MYUUID #endif +#include <errno.h> #include <stdio.h> #ifdef HAVE_UNISTD_H #include <unistd.h> @@ -53,7 +54,15 @@ #include "uuidP.h" -time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) +#undef uuid_time + +/* prototype to make compiler happy */ +time_t __uuid_time(const uuid_t uu, struct timeval *ret_tv); + + +/* this function could be 32bit time_t and 32bit timeval or 64bit, + depending on compiler flags and architecture. */ +time_t __uuid_time(const uuid_t uu, struct timeval *ret_tv) { struct timeval tv; struct uuid uuid; @@ -74,6 +83,54 @@ time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) return tv.tv_sec; } +#if defined(__USE_TIME_BITS64) && defined(__GLIBC__) +extern time_t uuid_time64(const uuid_t uu, struct timeval *ret_tv) __attribute__((weak, alias("__uuid_time"))); +#else +extern time_t uuid_time(const uuid_t uu, struct timeval *ret_tv) __attribute__((weak, alias("__uuid_time"))); +#endif + +#if defined(__USE_TIME_BITS64) && defined(__GLIBC__) +struct timeval32 +{ + int32_t tv_sec; + int32_t tv_usec; +}; + +/* prototype to make compiler happy */ +int32_t __uuid_time32(const uuid_t uu, struct timeval32 *ret_tv); + +/* Check whether time fits in 32bit time_t. */ +static inline int +in_time32_t_range(time_t t) +{ + int32_t s; + + s = t; + + return s == t; +} + +int32_t __uuid_time32(const uuid_t uu, struct timeval32 *ret_tv) +{ + struct timeval tv; + time_t ret_time = __uuid_time(uu, &tv); + + if (! in_time32_t_range(ret_time)) { + ret_tv->tv_sec = -1; + ret_tv->tv_usec = -1; + errno = EOVERFLOW; + return -1; + } + + if (ret_tv) { + ret_tv->tv_sec = tv.tv_sec; + ret_tv->tv_usec = tv.tv_usec; + } + + return tv.tv_sec; +} +extern int32_t uuid_time(const uuid_t uu, struct timeval32 *ret_tv) __attribute__((weak, alias("__uuid_time32"))); +#endif int uuid_type(const uuid_t uu) { |