diff --git a/Makefile.am b/Makefile.am index eb20917..7160b48 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ # Copyright (c) 2007 Vreixo Formoso -# Copyright (c) 2009 - 2019 Thomas Schmitt +# Copyright (c) 2009 - 2024 Thomas Schmitt # Provided under the terms of the GNU General Public License version 2 or later. # ts A90315 : LIBBURNIA_PKGCONFDIR is defined OS specific in acinclude.m4 @@ -22,11 +22,12 @@ ACLOCAL_AMFLAGS = -I ./ libisofs_libisofs_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LIBLDFLAGS) -# Eventually enabling system adapters for ACL and EA. -# ts A90409: Eventually enabling use of zlib. -# ts B00927: Eventually enabling use of libjte (Jigdo Template Extraction) -libisofs_libisofs_la_CFLAGS = $(LIBACL_DEF) $(XATTR_DEF) $(ZLIB_DEF) \ - $(LIBJTE_DEF) +# Enabling system adapters for ACL and EA. +# ts A90409: Enabling use of zlib. +# ts B00927: Enabling use of libjte (Jigdo Template Extraction) +# ts C40713: Enabling system adapter for Linux chattr(1) flags +libisofs_libisofs_la_CFLAGS = $(LIBACL_DEF) $(XATTR_DEF) $(LFA_DEF) \ + $(ZLIB_DEF) $(LIBJTE_DEF) # ts A90114 : added aaip_0_2.* diff --git a/configure.ac b/configure.ac index 04ba48c..b427a5a 100644 --- a/configure.ac +++ b/configure.ac @@ -207,7 +207,7 @@ AC_ARG_ENABLE(xattr, [ --enable-xattr Enable use of extended file attributes by libisofs, default=yes], , enable_xattr=yes) AC_ARG_ENABLE(xattr_h_pref_attr, - [ --enable-xattr-h-pref-attr Prefer include file attr/xattr.h over sys/xattr.h, default=no], + [ --enable-xattr-h-pref-attr Prefer include file attr/xattr.h over sys/xattr.h, default=no], , enable_xattr_h_pref_attr=no) XATTR_DEF= @@ -286,6 +286,21 @@ XATTR_DEF="$XATTR_DEF $XATTR_ADDON_DEF" AC_SUBST(XATTR_DEF) +dnl ts C40713 +LFA_DEF= +AC_ARG_ENABLE(linux-attr-flags, +[ --enable-linux-attr-flags Enable processing of Linux chattr(1) flags, default=yes], + , linux_attr_flags=yes) +if test x"$linux_attr_flags" = xyes; then + AC_CHECK_HEADER(linux/fs.h, LFA_DEF="-DLibisofs_with_aaip_lfa_flagS", + LFA_DEF=) +fi +if test x"$LFA_DEF" = x; then + echo "disabled Linux chattr(1) flags" +else + echo "enabled Linux chattr(1) flags" +fi +AC_SUBST(LFA_DEF) dnl ts A90409 AC_ARG_ENABLE(zlib, @@ -306,7 +321,7 @@ AC_SUBST(ZLIB_DEF) dnl ts B00927 AC_ARG_ENABLE(libjte, -[ --enable-libjte Enable use of libjte >= 2.0 by libisofs, default=yes], +[ --enable-libjte Enable use of libjte >= 2.0 by libisofs, default=yes], , enable_libjte=yes) if test "x$enable_libjte" = xyes; then LIBJTE_DEF="-DLibisofs_with_libjtE" diff --git a/doc/susp_aaip_isofs_names.txt b/doc/susp_aaip_isofs_names.txt index acf235e..95bacc5 100644 --- a/doc/susp_aaip_isofs_names.txt +++ b/doc/susp_aaip_isofs_names.txt @@ -126,6 +126,57 @@ Registered: ------------------------------------------------------------------------------- +Name: + isofs.fa + +Purpose: + Records the file attribute flags as of Linux program chattr(1) and + ioctl(FS_IOC_GETFLAGS) in as bits in a byte string. + Known from Debian GNU/Linux 8 to 12 are: + bit0= FS_SECRM_FL 's' zero blocks on deletion + bit1= FS_UNRM_FL 'u' on delete prepare for undelete + bit2= FS_COMPR_FL 'c' compress + bit3= FS_SYNC_FL 'S' synchronous write + bit4= FS_IMMUTABLE_FL 'i' immutable + bit5= FS_APPEND_FL 'a' appending write only + bit6= FS_NODUMP_FL 'd' dump(8) shall ignore this file + bit7= FS_NOATIME_FL 'A' do not update atime + bit8= FS_DIRTY_FL 'Z' compressed dirty file + bit9= FS_COMPRBLK_FL (? one or more compressed clusters) + bit10= FS_NOCOMP_FL 'm' do not compress + bit11= FS_ECOMPR_FL 'E' compression error (old) + FS_ENCRYPT_FL 'E' encrypted file (new) + bit12= FS_BTREE_FL (? btree format dir) + FS_INDEX_FL 'I' hash-indexed directory ? + bit13= FS_IMAGIC_FL (? AFS directory) + bit14= FS_JOURNAL_DATA_FL 'j' data journalling + bit15= FS_NOTAIL_FL 't' no tail-merging + bit16= FS_DIRSYNC_FL 'D' synchronous directory updates + bit17= FS_TOPDIR_FL 'T' top of directory hierarchy + bit18= FS_HUGE_FILE_FL ('h' huge file ? 'h' old, FS_HUGE_FILE_FL new) + bit19= FS_EXTENT_FL 'e' using extents + bit20= FS_DIRECTIO_FL (? use direct i/o) (old) + FS_VERITY_FL 'V' fs-verity enabled (new) + bit21= FS_EA_INODE_FL (? Inode used for large EA) + bit22= FS_EOFBLOCKS_FL (? reserved for ext4) + bit23= FS_NOCOW_FL 'C' no copy on write + bit25= FS_DAX_FL 'x' direct access + bit28= FS_INLINE_DATA_FL 'N' data stored in inode + bit29= FS_PROJINHERIT_FL 'P' project hierarchy + bit30= FS_CASEFOLD_FL 'F' case-insensitive directory lookups + bit31= FS_RESERVED_FL (? reserved for ext2 lib) + +Format of Value: + A byte string which begins with the most significant byte. + +Example: + (FS_SECRM_FL|FS_APPEND_FL|FS_NOCOMP_FL) = 0x421 + { 4 , 33 } +Registered: + 12 Jul 2024 by Thomas Schmitt for libisofs. + +------------------------------------------------------------------------------- + Name: isofs.hb @@ -224,7 +275,7 @@ Registered: ------------------------------------------------------------------------------- This text is under -Copyright (c) 2009 - 2015 Thomas Schmitt +Copyright (c) 2009 - 2024 Thomas Schmitt It shall only be modified in sync with libisofs and other software which makes use of AAIP. Please mail change requests to mailing list or to the copyright holder in private. diff --git a/libisofs/aaip-os-dummy.c b/libisofs/aaip-os-dummy.c index e23f856..a2457e2 100644 --- a/libisofs/aaip-os-dummy.c +++ b/libisofs/aaip-os-dummy.c @@ -11,7 +11,7 @@ To be included by aaip_0_2.c - Copyright (c) 2009 - 2011 Thomas Schmitt + Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -82,6 +82,19 @@ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, } +/* Obtain the file attribute flags of the given file as bit array in uint64_t. + The bit numbers are compatible to the FS_*_FL definitions in Linux. +*/ +int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, + int *os_errno, int flag) +{ + *lfa_flags= 0; + *max_bit= -1; + *os_errno= 0; + return(0); +} + + /* ------------------------------ Setters --------------------------------- */ @@ -134,3 +147,11 @@ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, } +int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, + int *os_errno, int flag) +{ + *os_errno= 0; + return(0); +} + + diff --git a/libisofs/aaip-os-freebsd.c b/libisofs/aaip-os-freebsd.c index a6157a6..08303e1 100644 --- a/libisofs/aaip-os-freebsd.c +++ b/libisofs/aaip-os-freebsd.c @@ -7,7 +7,7 @@ To be included by aaip_0_2.c for FreeBSD, NetBSD, and OpenBSD - Copyright (c) 2009 - 2016 Thomas Schmitt + Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -388,6 +388,9 @@ static int get_single_attr(char *path, char *name, size_t *value_length, by "user." bit4= do not return trivial ACL that matches st_mode bit5= in case of symbolic link: inquire link target + bit6= do not obtain Linux style file attribute flags + (chattr). + This obtaining is not implemented here anyways. bit15= free memory of names, value_lengths, values @return 1 ok 2 ok, no permission to inspect non-user namespaces @@ -601,6 +604,10 @@ ex:; I.e. those which are not from name space EXTATTR_NAMESPACE_USER bit4= do not return trivial ACL that matches st_mode + bit5= in case of symbolic link: inquire link target + bit6= do not obtain Linux style file attribute flags + (chattr). + This obtaining is not implemented here anyways. bit15= free memory of names, value_lengths, values @return >0 ok <=0 error @@ -705,6 +712,19 @@ ex:; #endif /* Libisofs_old_freebsd_acl_adapteR */ +/* Obtain the file attribute flags of the given file as bit array in uint64_t. + The bit numbers are compatible to the FS_*_FL definitions in Linux. +*/ +int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, + int *os_errno, int flag) +{ + *lfa_flags= 0; + *max_bit= -1; + *os_errno= 0; + return(0); +} + + /* ------------------------------ Setters --------------------------------- */ @@ -1094,3 +1114,12 @@ ex:; #endif /* Libisofs_old_freebsd_acl_adapteR */ + +int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, + int *os_errno, int flag) +{ + *os_errno= 0; + return(0); +} + + diff --git a/libisofs/aaip-os-linux.c b/libisofs/aaip-os-linux.c index 70e56f0..5e1f3f0 100644 --- a/libisofs/aaip-os-linux.c +++ b/libisofs/aaip-os-linux.c @@ -7,7 +7,7 @@ To be included by aaip_0_2.c for Linux - Copyright (c) 2009 - 2022 Thomas Schmitt + Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -42,6 +42,11 @@ #endif #endif +#ifdef Libisofs_with_aaip_lfa_flagS +#include +#include +#endif + /* ------------------------------ Inquiry --------------------------------- */ @@ -50,14 +55,16 @@ Bitfield for control purposes bit0= inquire availability of ACL bit1= inquire availability of xattr - bit2 - bit7= Reserved for future types. + bit2= inquire availability of Linux-like file attribute flags + bit3 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, submit 0 @return Bitfield corresponding to flag. If bits are set, th bit0= ACL adapter is enabled bit1= xattr adapter is enabled - bit2 - bit7= Reserved for future types. + bit2= Linux-like file attribute flags adapter is enabled + bit3 - bit7= Reserved for future types. bit8 and higher: reserved, do not interpret these */ int aaip_local_attr_support(int flag) @@ -68,11 +75,21 @@ int aaip_local_attr_support(int flag) if(flag & 1) ret|= 1; #endif + #ifdef Libisofs_with_aaip_xattR if(flag & 2) ret|= 2; #endif +#ifdef Libisofs_with_aaip_lfa_flagS +#ifdef FS_IOC_GETFLAGS +#ifdef FS_IOC_SETFLAGS + if(flag & 4) + ret|= 4; +#endif +#endif +#endif + return(ret); } @@ -260,6 +277,8 @@ static int get_single_attr(char *path, char *name, size_t *value_length, by "user." bit4= do not return trivial ACL that matches st_mode bit5= in case of symbolic link: inquire link target + bit6= do not obtain Linux style file attribute flags + (chattr) bit15= free memory of names, value_lengths, values @return 1 ok (reserved for FreeBSD: 2 ok, no permission to inspect @@ -288,6 +307,11 @@ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, #ifdef Libisofs_aaip_get_attr_activE ssize_t i, num_names= 0; #endif +#ifdef Libisofs_with_aaip_lfa_flagS + uint64_t lfa_flags; + int max_bit, os_errno, lfa_length; + unsigned char lfa_value[8]; +#endif if(flag & (1 << 15)) { /* Free memory */ {ret= 1; goto ex;} @@ -349,6 +373,13 @@ ex:; if(flag & 1) num_names++; +#endif + +#ifdef Libisofs_with_aaip_lfa_flagS + + if(!(flag & 64)) + num_names++; + #endif if(num_names == 0) @@ -397,7 +428,7 @@ ex:; aaip_get_acl_text(path, &a_acl_text, flag & (16 | 32)); aaip_get_acl_text(path, &d_acl_text, 1 | (flag & 32)); if(a_acl_text == NULL && d_acl_text == NULL) - {ret= 1; goto ex;} + goto try_lfa_flags; ret= aaip_encode_both_acl(a_acl_text, d_acl_text, (mode_t) 0, &acl_len, &acl, (flag & 2)); if(ret <= 0) @@ -415,6 +446,30 @@ ex:; #endif /* Libisofs_with_aaip_acL */ +try_lfa_flags:; + +#ifdef Libisofs_with_aaip_lfa_flagS + + if(!(flag & 64)) { + ret= aaip_get_lfa_flags(path, &lfa_flags, &max_bit, &os_errno, 0); + if(ret > 0) { + ret= aaip_encode_lfa_flags(lfa_flags, lfa_value, &lfa_length, 0); + if(ret > 0) { + (*names)[*num_attrs]= strdup("isofs.fa"); + if((*names)[*num_attrs] == NULL) + {ret= -1; goto ex;} + (*values)[*num_attrs]= calloc(lfa_length, 1); + if((*values)[*num_attrs] == NULL) + {ret= -1; goto ex;} + memcpy((*values)[*num_attrs], (char *) lfa_value, lfa_length); + (*value_lengths)[*num_attrs]= lfa_length; + (*num_attrs)++; + } + } + } + +#endif /* Libisofs_with_aaip_lfa_flagS */ + ret= 1; ex:; #ifdef Libisofs_with_aaip_acL @@ -455,6 +510,69 @@ ex:; } +/* Obtain the file attribute flags of the given file as bit array in uint64_t. + The bit numbers are compatible to the FS_*_FL definitions in Linux + include file . A (possibly outdated) copy of them is in + doc/susp_aaip_isofs_names.txt, name isofs.fa . + The attribute flags of other systems may or may not be mappable to these + flags. + @param path Path to the file + @param lfa_flags Will get filled with the FS_*_FL + @param max_bit Will tell the highest bit that is possibly set + (-1 = surely no bit is valid) + @param flag Bitfield for control purposes. Submit 0. + @return 1= ok, all local attribute flags are in lfa_flags + 2= ok, but some local flags could not be mapped to + the FS_*_FL bits + 0= local flag retrieval not enabled at compile time + <0 error with system calls +*/ +int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, + int *os_errno, int flag) +{ + int ret= 0; + +#ifdef Libisofs_with_aaip_lfa_flagS + int fd; + long ioctl_result= 0; +#endif + + *lfa_flags= 0; + *max_bit= -1; + *os_errno= 0; + +#ifdef Libisofs_with_aaip_lfa_flagS +#ifdef FS_IOC_GETFLAGS + fd= open(path, O_RDONLY | O_NDELAY); + if(fd == -1) { + aaip_local_error("open", path, errno, 0); + *os_errno= errno; + return(-1); + } + ret= ioctl(fd, FS_IOC_GETFLAGS, &ioctl_result); + close(fd); + if(ret == -1) { + aaip_local_error("ioctl(FS_IOC_GETFLAGS)", path, errno, 0); + *os_errno= errno; + return(-1); + } + *lfa_flags= ioctl_result; + if(*lfa_flags < 1 << 24) + *max_bit= 23; + else if(*lfa_flags < (uint64_t) 1 << 32) + *max_bit= 31; + else + *max_bit= sizeof(long) * 8 - 1; + + ret= 1; + +#endif /* FS_IOC_GETFLAGS */ +#endif /* Libisofs_with_aaip_lfa_flagS */ + + return(ret); +} + + /* ------------------------------ Setters --------------------------------- */ @@ -749,3 +867,49 @@ ex:; } +int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, + int *os_errno, int flag) +{ + int ret= 0; + +#ifdef Libisofs_with_aaip_lfa_flagS + int fd; + long ioctl_arg; +#endif + + *os_errno= 0; + +#ifdef Libisofs_with_aaip_lfa_flagS +#ifdef FS_IOC_GETFLAGS + + if(max_bit > (int) sizeof(long) * 8 - 1) { + aaip_local_error("ioctl(FS_IOC_SETFLAGS) with too many bits", path, 0, 0); + return(-1); + } + + fd= open(path, O_RDONLY | O_NDELAY); + if(fd == -1) { + aaip_local_error("open", path, errno, 0); + *os_errno= errno; + return(-1); + } + if(max_bit < 0) + ioctl_arg= 0; + else + ioctl_arg= lfa_flags; + ret= ioctl(fd, FS_IOC_SETFLAGS, ioctl_arg); + close(fd); + if(ret == -1) { + aaip_local_error("ioctl(FS_IOC_SETFLAGS)", path, errno, 0); + *os_errno= errno; + return(-1); + } + ret= 1; + +#endif /* FS_IOC_GETFLAGS */ +#endif /* Libisofs_with_aaip_lfa_flagS */ + + return(ret); +} + + diff --git a/libisofs/aaip_0_2.c b/libisofs/aaip_0_2.c index 03bdd77..53fd822 100644 --- a/libisofs/aaip_0_2.c +++ b/libisofs/aaip_0_2.c @@ -900,6 +900,26 @@ int aaip_add_acl_st_mode(char *acl_text, mode_t st_mode, int flag) } +int aaip_encode_lfa_flags(uint64_t lfa_flags, unsigned char value[8], + int *length, int flag) +{ + int i, l; + + *length = 1; + value[0] = 0; + + /* How many bytes are needed to catch all set bits ? Minimum is 1. */ + l= 8; + for(i= 1; i < l; i++) + if(lfa_flags < (uint64_t) 1 << (i * 8)) + break; + *length= i; + for(i= 0; i < *length; i++) + value[*length - 1 - i]= (lfa_flags >> (8 * i)) & 0xff; + return(1); +} + + /* --------------------------------- Decoder ---------------------------- */ /* --- private --- */ diff --git a/libisofs/aaip_0_2.h b/libisofs/aaip_0_2.h index 71f2632..efa4e93 100644 --- a/libisofs/aaip_0_2.h +++ b/libisofs/aaip_0_2.h @@ -9,7 +9,7 @@ test/aaip_0_2.h - Public declarations - Copyright (c) 2009 - 2016 Thomas Schmitt + Copyright (c) 2009 - 2024 Thomas Schmitt This file is part of the libisofs project; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 @@ -155,6 +155,17 @@ int aaip_cleanout_st_mode(char *acl_text, mode_t *st_mode, int flag); int aaip_add_acl_st_mode(char *acl_text, mode_t st_mode, int flag); +/* Encode a Linux style file attribute flag bits array to a byte string + which represents the flags bits in isofs.fa . + @param lfa_flags Bit array as obtained by aaip_get_lfa_flags() + @param value Will be filled with 1 to 8 byte values + @param length Will return the number of filled-in value bytes + @return <0 failure +*/ +int aaip_encode_lfa_flags(uint64_t lfa_flags, unsigned char value[8], + int *length, int flag); + + /* ------ OS interface ------ */ /* See also API iso_local_attr_support(). @@ -162,14 +173,17 @@ int aaip_add_acl_st_mode(char *acl_text, mode_t st_mode, int flag); Bitfield for control purposes bit0= inquire availability of ACL bit1= inquire availability of xattr - bit2 - bit7= Reserved for future types. + bit2= inquire availability of Linux-like file attribute flags + bit3 - bit7= Reserved for future types. It is permissibile to set them to 1 already now. bit8 and higher: reserved, submit 0 @return Bitfield corresponding to flag. If bits are set, th bit0= ACL adapter is enabled bit1= xattr adapter is enabled - bit2 - bit7= Reserved for future types. + bit2= Linux-like file attribute flags adapter is enabled + bit3 - bit7= Reserved for future types. + It is permissibile to set them to 1 already now. bit8 and higher: reserved, do not interpret these */ int aaip_local_attr_support(int flag); @@ -210,6 +224,9 @@ int aaip_get_acl_text(char *path, char **text, int flag); I.e. those with a name which does not begin by "user." bit4= do not return trivial ACL that matches st_mode + bit5= in case of symbolic link: inquire link target + bit6= do not obtain Linux style file attribute flags + (chattr) bit15= free memory of names, value_lengths, values @return >0 ok <=0 error @@ -218,6 +235,28 @@ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag); +/* Obtain the file attribute flags of the given file as bit array in uint64_t. + The bit numbers are compatible to the FS_*_FL definitions in Linux + include file . A (possibly outdated) copy of them is in + doc/susp_aaip_isofs_names.txt, name isofs.fa . + The attribute flags of other systems may or may not be mappable to these + flags. + @param path Path to the file + @param lfa_flags Will get filled with the FS_*_FL + @param max_bit Will tell the highest bit that is possibly set + (-1 = surely no bit is valid) + @param os_errno Will get filled with errno if a system call fails + @param flag Bitfield for control purposes. Submit 0. + @return 1= ok, all local attribute flags are in lfa_flags + 2= ok, but some local flags could not be mapped to + the FS_*_FL bits + 0= local flags retrieval not enabled at compile time + <0 error with system calls +*/ +int aaip_get_lfa_flags(char *path, uint64_t *lfa_flags, int *max_bit, + int *os_errno, int flag); + + /* --------------------------------- Decoder ---------------------------- */ /* @@ -515,5 +554,29 @@ int aaip_set_attr_list(char *path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int *errnos, int flag); + +/* Bring the given file attribute flags into effect with the given file. + The bit numbers are compatible to the FS_*_FL definitions in Linux + include file . A (possibly outdated) copy of them is in + doc/susp_aaip_isofs_names.txt, name isofs.fa . + The attribute flags of other systems may or may not be mappable to these + flags. + @param path Path to the file + @param lfa_flags File attribute flag bits + @param max_bit Gives an upper limit of the highest set flag bit. + (-1 = surely no bit is valid) + On Linux this must be smaller than sizeof(long) * 8. + @param os_errno Will get filled with errno if a system call fails + @param flag Bitfield for control purposes. Submit 0. + @return 1= ok, all lfa_flags bits were written + 2= ok, but some FS_*_FL bits could not be mapped to + local flags + 0= local flags setting not enabled at compile time + <0 error with system calls or with max_bit +*/ +int aaip_set_lfa_flags(char *path, uint64_t lfa_flags, int max_bit, + int *os_errno, int flag); + + #endif /* ! Aaip_h_is_includeD */ diff --git a/libisofs/builder.c b/libisofs/builder.c index 426868f..a74ff7b 100644 --- a/libisofs/builder.c +++ b/libisofs/builder.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2015 Thomas Schmitt + * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -253,7 +253,8 @@ int default_create_node(IsoNodeBuilder *builder, IsoImage *image, ret = iso_file_source_get_aa_string(src, &aa_string, 1 | (image->builder_ignore_acl << 1) | (image->builder_ignore_ea << 2) | - (image->builder_take_all_ea << 3)); + (image->builder_take_all_ea << 3) | + ((!image->builder_ignore_lfa_flags) << 4)); if(ret == 2) image->blind_on_local_get_attrs = 1; if (ret > 0 && aa_string != NULL) { diff --git a/libisofs/fs_local.c b/libisofs/fs_local.c index 094b4cb..f195d19 100644 --- a/libisofs/fs_local.c +++ b/libisofs/fs_local.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2017 Thomas Schmitt + * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -507,12 +507,14 @@ int lfs_get_aa_string(IsoFileSource *src, unsigned char **aa_string, int flag) *aa_string = NULL; - if ((flag & 6 ) == 6) { /* Neither ACL nor xattr shall be read */ + if ((flag & (2 | 4 | 16) ) == (2 | 4)) { + /* Neither ACL nor xattr shall be read, lfa_flags are not wanted */ ret = 1; goto ex; } /* Obtain EAs and ACLs ("access" and "default"). ACLs encoded according to AAIP ACL representation. Clean out st_mode ACL entries. + Obtain Linux style attribute flags. */ path = iso_file_source_get_path(src); if (path == NULL) { @@ -521,7 +523,8 @@ int lfs_get_aa_string(IsoFileSource *src, unsigned char **aa_string, int flag) } ret = aaip_get_attr_list(path, &num_attrs, &names, &value_lengths, &values, - (!(flag & 2)) | 2 | (flag & 4) | (flag & 8) | 16); + (!(flag & 2)) | 2 | (flag & 4) | (flag & 8) | 16 | + ((!(flag & 16)) << 6)); if (ret <= 0) { if (ret == -2) ret = ISO_AAIP_NO_GET_LOCAL; @@ -863,11 +866,13 @@ int iso_local_set_acl_text(char *disk_path, char *text, int flag) int iso_local_get_attrs(char *disk_path, size_t *num_attrs, char ***names, size_t **value_lengths, char ***values, int flag) { - int ret; + int ret, lfa; + lfa = (flag & 64) ^ 64; ret = aaip_get_attr_list(disk_path, num_attrs, names, value_lengths, values, - (flag & (1 | 4 | 8 | 32 | (1 << 15))) | 2 | 16); + (flag & (1 | 4 | 8 | 32 | (1 << 15))) | + 2 | 16 | lfa); if (ret <= 0) return ISO_AAIP_NO_GET_LOCAL; return 1 + (ret == 2); @@ -940,3 +945,90 @@ int iso_local_get_perms_wo_acl(char *disk_path, mode_t *st_mode, int flag) return 1; } +/* + * @param flag + * Bitfield for control purposes + * bit5= in case of symbolic link: inquire link target + * @return + * 1= ok, lfa_flags is valid + * 2= ok, but some local flags could not be mapped to the FS_*_FL bits + * 3= ok, symbolic link encountered, flag bit5 not set, lfa_flags set to 0 + * 0= local flags retrieval not enabled at compile time + * <0= error with system calls + */ +int iso_local_get_lfa_flags(char *disk_path, uint64_t *lfa_flags, int *max_bit, + int *os_errno, int flag) +{ + int ret; + struct stat stbuf; + + *lfa_flags = 0; + *max_bit = -1; + *os_errno = 0; + if (flag & 32) + ret = stat(disk_path, &stbuf); + else + ret = lstat(disk_path, &stbuf); + if (ret == -1) { + *os_errno = errno; + return -1; + } + if ((stbuf.st_mode & S_IFMT) == S_IFLNK && !(flag & 32)) + return 3; + ret= aaip_get_lfa_flags(disk_path, lfa_flags, max_bit, os_errno, 0); + if(ret == 0) + return ISO_AAIP_NOT_ENABLED; + if (ret < 0) + return ISO_AAIP_NO_GET_LOCAL; + return ret; +} + + +/* + * @param flag Bitfield for control purposes + * bit0= do not try to set known superuser flags + * bit1= set only known chattr settable flags + * bit5= in case of symbolic link: inquire link target + * @return + * 1 = ok, all lfa_flags bits were written + * 2 = ok, but some FS_*_FL bits could not be mapped to local flags + * 3 = ok, symbolic link encountered, flag bit5 not set, nothing done + * <0 = error + */ +int iso_local_set_lfa_flags(char *disk_path, uint64_t lfa_flags, int max_bit, + int *os_errno, int flag) +{ + int ret; + struct stat stbuf; + + /* chattr letters: User: sucSdAmtDTCxPF + Superuser: iaj + Non-settable: Z(9)EI(13)heV(21)(22)N(31) + */ + static uint64_t known_user_mask = 0x628384cf; + static uint64_t known_su_mask = 0x00004030; + + *os_errno = 0; + if (flag & 32) + ret = stat(disk_path, &stbuf); + else + ret = lstat(disk_path, &stbuf); + if (ret == -1) { + *os_errno = errno; + return -1; + } + if ((stbuf.st_mode & S_IFMT) == S_IFLNK && !(flag & 32)) + return 3; + if (flag & 1) + lfa_flags &= ~known_su_mask; + if (flag & 2) + lfa_flags &= known_user_mask | known_su_mask; + ret= aaip_set_lfa_flags(disk_path, lfa_flags, max_bit, os_errno, 0); + if(ret == 0) + return ISO_AAIP_NOT_ENABLED; + if(ret < 0) + return ISO_AAIP_NO_SET_LOCAL; + return ret; +} + + diff --git a/libisofs/image.c b/libisofs/image.c index dc90e07..dec591d 100644 --- a/libisofs/image.c +++ b/libisofs/image.c @@ -200,6 +200,7 @@ int iso_image_new(const char *name, IsoImage **image) img->import_src = NULL; img->builder_ignore_acl = 1; img->builder_ignore_ea = 1; + img->builder_ignore_lfa_flags = 1; img->truncate_mode = 1; img->truncate_length = LIBISOFS_NODE_NAME_MAX; img->truncate_buffer[0] = 0; @@ -629,6 +630,7 @@ void iso_image_set_ignore_aclea(IsoImage *image, int what) { image->builder_ignore_acl = (what & 1); image->builder_ignore_ea = !!(what & 2); + image->builder_ignore_lfa_flags= !(what & 4); image->builder_take_all_ea = !!(what & 8); } @@ -637,6 +639,7 @@ int iso_image_get_ignore_aclea(IsoImage *image) { return image->builder_ignore_acl | (image->builder_ignore_ea << 1) | + ((!image->builder_ignore_lfa_flags) << 2) | (image->builder_take_all_ea << 3); } diff --git a/libisofs/image.h b/libisofs/image.h index 3c4a0d0..8bb102b 100644 --- a/libisofs/image.h +++ b/libisofs/image.h @@ -145,6 +145,13 @@ struct Iso_Image */ unsigned int builder_ignore_ea : 1; + /** + * Whether to ignore Linux style file attribute flags (chattr). + * Not in effect with loading a complete ISO image but only with image + * manipulation. + */ + unsigned int builder_ignore_lfa_flags : 1; + /** * If not builder_ignore_ea : import all xattr namespaces from local * filesystem, not only "user. diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 8fd4951..cd35cc7 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -1307,6 +1307,10 @@ int iso_image_new(const char *name, IsoImage **image); * A bit field which sets the behavior: * bit0= ignore ACLs if the external file object bears some * bit1= ignore xattr if the external file object bears some + * bit2= read Linux-like file attribute flags (chattr) + * if the external file object bears some. + * (I.e. a do-not-ignore, because ignoring was default before) + * @since 1.5.8 * bit3= if not bit1: import all xattr namespaces, not only "user." * @since 1.5.0 * all other bits are reserved @@ -7263,6 +7267,9 @@ int iso_file_source_readlink(IsoFileSource *src, char *buf, size_t bufsiz); * bit3= if not bit2: import all xattr namespaces from * local filesystem, not only "user." * @since 1.5.0 + * bit4= Try to get Linux-like file attribute flags (chattr) + * as "isofs.fa" + * @since 1.5.8 * @return 1 means success (*aa_string == NULL is possible) * <0 means failure and must b a valid libisofs error code * (e.g. ISO_FILE_ERROR if no better one can be found). @@ -7780,6 +7787,53 @@ int iso_node_set_attrs(IsoNode *node, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag); +/** + * Obtain the Linux-like file attribute flags (chattr) as bit array. + * The bit numbers are compatible to the FS_*_FL definitions in Linux + * include file . A (possibly outdated) copy of them is in + * doc/susp_aaip_isofs_names.txt, name isofs.fa . + * + * @param node + * The node that is to be inquired. + * @param lfa_flags + * Will get filled with the FS_*_FL bits + * @param max_bit + * Will tell the highest bit that is possibly set + * (-1 = surely no bit is valid) + * @param flag + * Bitfield for control purposes. Submit 0. + * @return + * 1 = ok, lfa_flags and max_bit are valid + * 0 = no Linux-like file attributes are associated with the node + * < 0 = error + * + * @since 1.5.8 + */ +int iso_node_get_lfa_flags(IsoNode *node, uint64_t *lfa_flags, int *max_bit, + int flag); + +/** + * Set the Linux-like file attribute flags (chattr) which are associated with + * the node. + * The bit numbers are compatible to the FS_*_FL definitions in Linux + * include file . A (possibly outdated) copy of them is in + * doc/susp_aaip_isofs_names.txt, name isofs.fa . + * + * @param node + * The node that is to be manipulated. + * @param lfa_flags + * File attribute flag bits + * @param flag + * Bitfield for control purposes. Submit 0. + * @return + * 1 = ok + * < 0 = error + * + * @since 1.5.8 + */ +int iso_node_set_lfa_flags(IsoNode *node, uint64_t lfa_flags, int flag); + + /* ----- This is an interface to ACL and xattr of the local filesystem ----- */ /** @@ -7806,7 +7860,10 @@ int iso_node_set_attrs(IsoNode *node, size_t num_attrs, char **names, * Bitfield corresponding to flag. * bit0= ACL adapter is enabled * bit1= xattr adapter is enabled - * bit2 - bit7= Reserved for future types. + * bit2= Linux-like file attribute flags (chattr) adapter is enabled + * @since 1.5.8 + * bit3 - bit7= Reserved for future types. + * It is permissibile to set them to 1 already now. * bit8 and higher: reserved, do not interpret these * * @since 1.1.6 @@ -7888,7 +7945,8 @@ int iso_local_get_perms_wo_acl(char *disk_path, mode_t *st_mode, int flag); /** - * Get xattr and non-trivial ACLs of the given file in the local filesystem. + * Get xattr, non-trivial ACLs, and possible Linux-like file attribute flags + * (chattr) of the given file in the local filesystem. * The resulting data has finally to be disposed by a call to this function * with flag bit15 set. * @@ -7913,6 +7971,7 @@ int iso_local_get_perms_wo_acl(char *disk_path, mode_t *st_mode, int flag); * bit3= do not ignore non-user attributes. * I.e. those with a name which does not begin by "user." * bit5= in case of symbolic link: inquire link target + * bit6= obtain Linux-like file attribute flags (chattr) as "isofs.fa" * bit15= free memory * @return * 1 ok @@ -7978,6 +8037,121 @@ int iso_local_set_attrs(char *disk_path, size_t num_attrs, char **names, size_t *value_lengths, char **values, int flag); +/** + * Obtain the Linux-like file attribute flags (chattr) of the given file as + * bit array. + * The bit numbers are compatible to the FS_*_FL definitions in Linux + * include file . A (possibly outdated) copy of them is in + * doc/susp_aaip_isofs_names.txt, name isofs.fa . + * The attribute flags of other systems may or may not be mappable to these + * flags. + * @param disk_path + * Path to the file + * @param lfa_flags + * Will get filled with the FS_*_FL bits + * @param max_bit + * Will tell the highest bit that is possibly set + * (-1 = surely no bit is valid) + * @param os_errno + * Will get filled with errno if a system call fails. + * Else it will be filled with 0. + * @param flag + * Bitfield for control purposes + * bit5= in case of symbolic link: inquire link target + * @return + * 1 = ok, lfa_flags is valid + * 2 = ok, but some local flags could not be mapped to the FS_*_FL bits + * 3 = ok, symbolic link encountered, flag bit5 not set,lfa_flags set to 0 + * <0 = error + * + * @since 1.5.8 + */ +int iso_local_get_lfa_flags(char *disk_path, uint64_t *lfa_flags, int *max_bit, + int *os_errno, int flag); + + +/** + * Bring the given Linux-like file attribute flags (chattr) into effect with + * the given file. + * The bit numbers are compatible to the FS_*_FL definitions in Linux + * include file . A (possibly outdated) copy of them is in + * doc/susp_aaip_isofs_names.txt, name isofs.fa . + * The attribute flags of other systems may or may not be mappable to these + * flags. + * @param disk_path + * Path to the file + * @param lfa_flags + * File attribute flag bits + * @param max_bit + * Gives an upper limit of the highest possibly set flag bit. + * (-1 = surely no bit is valid) + * On Linux this must be smaller than sizeof(long) * 8. + * @param os_errno + * Will get filled with errno if a system call fails. + * Else it will be filled with 0. + * @param flag + * Bitfield for control purposes + * bit0= do not try to set known chattr superuser flags: iaj + * bit1= set only known chattr settable flags: sucSiadAmjtDTCxPF + * bit5= in case of symbolic link: operate on link target + * @return + * 1 = ok, all lfa_flags bits were written + * 2 = ok, but some FS_*_FL bits could not be mapped to local flags + * 3 = ok, symbolic link encountered, flag bit5 not set, nothing done + * <0 = error + * + * @since 1.5.8 + */ +int iso_local_set_lfa_flags(char *disk_path, uint64_t lfa_flags, int max_bit, + int *os_errno, int flag); + + +/** + * Convert the known set bits of the given Linux-like file attribute flags + * to a string of flag letters like expected by chattr(1). + * + * @param lfa_flags + * File attribute flag bits to be converted + * @param flags_text + * Will return the string of known set flag letters. + * If it is not returned as NULL, then submit it to free(2) when + * no longer needed. + * @param flag + * Bitfield for control purposes. + * bit0= produce lsattr(1) format with '-' and peculiar sequence + * (This might be more prone to ISO_LFA_UNKNOWN_BIT. + * Some versions of lsattr showed less bits. Maybe newer versions + * will show more bits.) + * @return + * >0 = success + * <0 = error + * ISO_LFA_UNKNOWN_BIT indicates a partial result in flags_text + * + * @since 1.5.8 + */ +int iso_util_encode_lfa_flags(uint64_t lfa_flags, char **flags_text, int flag); + + +/** + * Convert the given string of flag letters to a bit array usable by + * iso_*_set_lfa_flags(). The letters are as expected by chattr(1) or shown + * by lsattr(1). + * + * @param flags_text + * The string of flag letters to be converted + * @param lfa_flags + * Will return the resultig file attribute flag bits + * @param flag + * Bitfield for control purposes. Submit 0. + * @return + * >0 = success + * <0 = error + * + * @since 1.5.8 + */ +int iso_util_decode_lfa_flags(char *flags_text, uint64_t *lfa_flags, int flag); + + /* Default in case that the compile environment has no macro PATH_MAX. */ #define Libisofs_default_path_maX 4096 @@ -9460,6 +9634,14 @@ int iso_conv_name_chars(IsoWriteOpts *opts, char *name, size_t name_len, (WARNING,HIGH, -429) */ #define ISO_CE_REMOVING_ATTR 0xD030FE53 +/** Unknown Linux-like chattr letter encountered during conversion + (WARNING,HIGH, -430) */ +#define ISO_LFA_UNKNOWN_LETTER 0xD030FE52 + +/** Unknown Linux-like file attribute flag bit encountered during conversion + (WARNING,HIGH, -431) */ +#define ISO_LFA_UNKNOWN_BIT 0xD030FE51 + /* Internal developer note: Place new error codes directly above this comment. diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index 7216da6..5097e5d 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -384,3 +384,13 @@ iso_zisofs_set_params; serial_id; local: *; }; + +LIBISOFS6_1.5.8 { +iso_local_get_lfa_flags; +iso_local_set_lfa_flags; +iso_node_get_lfa_flags; +iso_node_set_lfa_flags; +iso_util_decode_lfa_flags; +iso_util_encode_lfa_flags; +} LIBISOFS6; + diff --git a/libisofs/messages.c b/libisofs/messages.c index 5d677ed..2582d7e 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -573,6 +573,10 @@ const char *iso_error_to_msg(int errcode) return "Too many CE entries for single file when mounted by Linux"; case ISO_CE_REMOVING_ATTR: return "Too many CE entries for single file, removing attributes"; + case ISO_LFA_UNKNOWN_LETTER: + return "Unknown Linux-like chattr letter encountered during conversion"; + case ISO_LFA_UNKNOWN_BIT: + return "Unknown Linux-like file attribute flag bit encountered during conversion"; default: return "Unknown error"; } diff --git a/libisofs/node.c b/libisofs/node.c index e1e653a..5fa8996 100644 --- a/libisofs/node.c +++ b/libisofs/node.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Vreixo Formoso - * Copyright (c) 2009 - 2023 Thomas Schmitt + * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -2057,6 +2057,7 @@ ex:; /* @param flag bit0= delete ACL, too + bit1= delete file attribute flags (isofs.fa), too */ int iso_node_remove_fattr(IsoNode *node, int flag) { @@ -2072,7 +2073,8 @@ int iso_node_remove_fattr(IsoNode *node, int flag) /* Delete variables of all namespaces except isofs */ w = 0; for (i = 0; i < num_attrs; i++) { - if (strncmp(names[i], "isofs.", 6) != 0) { + if (strncmp(names[i], "isofs.", 6) != 0 || + ((flag & 2) && strcmp(names[i], "isofs.fa") == 0)) { free(names[i]); names[i] = NULL; free(values[i]); @@ -2452,6 +2454,53 @@ ex:; } +int iso_node_set_lfa_flags(IsoNode *node, uint64_t lfa_flags, int flag) +{ + static char *names = "isofs.fa"; + static size_t value_lengths[1]; + unsigned char value[8]; + char *valuept; + int ret, l; + + ret = aaip_encode_lfa_flags(lfa_flags, value, &l, 0); + if (ret < 0) + return ret; + value_lengths[0] = l; + valuept= (char *) value; + ret = iso_node_set_attrs(node, (size_t) 1, + &names, value_lengths, &valuept, 2 | 8); + return ret; +} + + +int iso_node_get_lfa_flags(IsoNode *node, uint64_t *lfa_flags, int *max_bit, + int flag) +{ + int ret, i; + size_t value_len; + char *value = NULL; + + *lfa_flags = 0; + *max_bit = -1; + + ret = iso_node_lookup_attr(node, "isofs.fa", &value_len, &value, 0); + if (ret <= 0) + return ret; + if (value_len <= 0) { + *max_bit = 7; + return 1; + } + if (value_len > 8) { + value += value_len - 8; + value_len = 8; + } + for (i = 0; i < (int) value_len; i++) + *lfa_flags = (*lfa_flags << 8) | ((unsigned char *) value)[i]; + *max_bit = value_len * 8 - 1; + return 1; +} + + /* Function to identify and manage ZF parameters. * data is supposed to be a pointer to struct zisofs_zf_info */ diff --git a/libisofs/rockridge.c b/libisofs/rockridge.c index 91e90b9..f432e9f 100644 --- a/libisofs/rockridge.c +++ b/libisofs/rockridge.c @@ -1,7 +1,7 @@ /* * Copyright (c) 2007 Vreixo Formoso * Copyright (c) 2007 Mario Danic - * Copyright (c) 2009 - 2023 Thomas Schmitt + * Copyright (c) 2009 - 2024 Thomas Schmitt * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 @@ -1600,10 +1600,10 @@ try_again: } } else if (retry == 2) { if ((t->opts->max_ce_drop_attr & 15) >= 2) { - ret = iso_node_remove_fattr(n->node, 1); + ret = iso_node_remove_fattr(n->node, 1 | 2); if (ret > 0) { iso_msg_submit(t->image->id, ISO_CE_REMOVING_ATTR, 0, - "Removed ACL"); + "Removed ACL and attribute flags"); goto try_again; } } diff --git a/libisofs/util.c b/libisofs/util.c index 8c51307..c71d226 100644 --- a/libisofs/util.c +++ b/libisofs/util.c @@ -2510,3 +2510,103 @@ int iso_nowtime(time_t *now, int flag) return 2; } + +/* Bit sequence as of Linux 6.1.0 FS_*_FL . + Letters as of e2fsprogs 1.47.1, man chattr and lib/pf.c for lsattr(1). +*/ +static char lfa_flag_letters[] = { 's', 'u', 'c', 'S', 'i', 'a', 'd', 'A', + 'Z', '-', 'm', 'E', 'I', '-', 'j', 't', + 'D', 'T', 'h', 'e', 'V', '-', '-', 'C', + '-', 'x', '-', '-', 'N', 'P', 'F', '-' }; + +/* Sequence and coverage as of e2fsprogs 1.47.1, lib/pf.c for lsattr(1) */ + +static int lsattr_permutation[] = { 0, 1, 3, 16, 4, 5, 6, 7, + 2, 11, 14, 12, 15, 17, 19, 23, + 25, 30, 28, 29, 20, 10, -1}; +static int num_lsattr_bits = 22; +static int max_lsattr_bit = 30; +static int non_lsattr_bits[] = { 8, 9, 13, 18, 21, 22, 24, 26, 27, -1 }; + + +/* @param flag bit0= produce lsattr format with '-' and peculiar sequence +*/ +int iso_util_encode_lfa_flags(uint64_t lfa_flags, char **flags_text, int flag) +{ + int i, len = 0, w = 0, was_unknown = 0, pi; + + *flags_text = NULL; + if (flag & 1) + goto lsattr_format; + + for (i = 0; i < 64; i++) + if (lfa_flags & (((uint64_t) 1) << i)) + len++; + *flags_text = calloc(len + 1, 1); + if (*flags_text == NULL) + return ISO_OUT_OF_MEM; + + for (i = 0; i < 64; i++) { + if (!(lfa_flags & (((uint64_t) 1) << i))) + continue; + if (lfa_flag_letters[i] == '-') { + was_unknown = 1; + } else { + (*flags_text)[w++] = lfa_flag_letters[i]; + } + } + flags_text[w] = 0; + if (was_unknown) + return ISO_LFA_UNKNOWN_BIT; + return ISO_SUCCESS; + +lsattr_format:; + + *flags_text = calloc(num_lsattr_bits + 1, 1); + if (*flags_text == NULL) + return ISO_OUT_OF_MEM; + + for (i = 0; i < num_lsattr_bits; i++) { + pi = lsattr_permutation[i]; + if (pi < 0) + break; + if (lfa_flags & (((uint64_t) 1) << pi)) { + (*flags_text)[w++] = lfa_flag_letters[pi]; + } else { + (*flags_text)[w++] = '-'; + } + } + (*flags_text)[num_lsattr_bits] = 0; + + for (i = 0; non_lsattr_bits[i] >= 0; i++) + if (lfa_flags & (((uint64_t) 1) << non_lsattr_bits[i])) + return ISO_LFA_UNKNOWN_BIT; + for (i= max_lsattr_bit + 1; i < 63; i++) + if (lfa_flags & (((uint64_t) 1) << i)) + return ISO_LFA_UNKNOWN_BIT; + return ISO_SUCCESS; +} + + +int iso_util_decode_lfa_flags(char *flags_text, uint64_t *lfa_flags, int flag) +{ + int i, j, was_unknown = 0; + + *lfa_flags = 0; + for (i = 0; flags_text[i] != 0; i++) { + if (flags_text[i] == '-') + continue; + for (j = 0; j < 64; j++) + if (lfa_flag_letters[j] == flags_text[i]) + break; + if (j >= 64) { + was_unknown = 1; + continue; + } + *lfa_flags |= ((uint64_t) 1) << j; + } + if (was_unknown) + return ISO_LFA_UNKNOWN_LETTER; + return 1; +} +