Index: kioslave/file/file.cc =================================================================== --- kioslave/file/file.cc (revision 462925) +++ kioslave/file/file.cc (working copy) @@ -43,8 +43,10 @@ #ifdef USE_POSIX_ACL #include +#ifdef Q_OS_LINUX #include #endif +#endif #include #include @@ -146,13 +148,63 @@ if ( ACLString == "ACL_DELETE" ) { // user told us to delete the extended ACL, so let's write only // the minimal (UNIX permission bits) part + #ifdef Q_OS_LINUX acl = acl_from_mode( perm ); + #else + /* Implement the non POSIX.1e function acl_from_mode */ + acl_t newACL = acl_init( 3 ); + acl_entry_t entry; + acl_permset_t permset = 0; + int error = 0; + + /* Add owner entry */ + if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) { + /* Set owner permissions */ + acl_set_tag_type( entry, ACL_USER_OBJ ); + acl_get_permset( entry, &permset ); + acl_clear_perms( permset ); + if ( perm & S_IRUSR ) acl_add_perm( permset, ACL_READ ); + if ( perm & S_IWUSR ) acl_add_perm( permset, ACL_WRITE ); + if ( perm & S_IXUSR ) acl_add_perm( permset, ACL_EXECUTE ); + acl_set_permset( entry, permset ); + + /* Add group entry */ + if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) { + /* Set group permissions */ + acl_set_tag_type( entry, ACL_GROUP_OBJ ); + acl_get_permset( entry, &permset ); + acl_clear_perms( permset ); + if ( perm & S_IRGRP ) acl_add_perm( permset, ACL_READ ); + if ( perm & S_IWGRP ) acl_add_perm( permset, ACL_WRITE ); + if ( perm & S_IXGRP ) acl_add_perm( permset, ACL_EXECUTE ); + acl_set_permset( entry, permset ); + + /* Add other entry */ + if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) { + /* Set other permissions */ + acl_set_tag_type( entry, ACL_OTHER ); + acl_get_permset( entry, &permset ); + acl_clear_perms( permset ); + if ( perm & S_IROTH ) acl_add_perm( permset, ACL_READ ); + if ( perm & S_IWOTH ) acl_add_perm( permset, ACL_WRITE ); + if ( perm & S_IXOTH ) acl_add_perm( permset, ACL_EXECUTE ); + acl_set_permset( entry, permset ); + } + } + } + + if ( error ) + acl_free( &newACL ); + else + acl = newACL; + #endif } acl = acl_from_text( ACLString.latin1() ); if ( acl_valid( acl ) == 0 ) { // let's be safe ret == acl_set_file( path, ACL_TYPE_ACCESS, acl ); - ssize_t size = acl_size( acl ); - kdDebug(7101) << "Set ACL on: " << path << " to: " << acl_to_text( acl, &size ) << endl; + char *aclString = acl_to_text( acl, 0 ); + kdDebug(7101) << "Set ACL on: " << path << " to: " << aclString << endl; + acl_free ( (void*)aclString ); } acl_free( acl ); if ( ret != 0 ) return ret; // better stop trying right away @@ -166,8 +218,9 @@ acl_t acl = acl_from_text( defaultACLString.latin1() ); if ( acl_valid( acl ) == 0 ) { // let's be safe ret += acl_set_file( path, ACL_TYPE_DEFAULT, acl ); - ssize_t size = acl_size( acl ); - kdDebug(7101) << "Set Default ACL on: " << path << " to: " << acl_to_text( acl, &size ) << endl; + char *aclString = acl_to_text( acl, 0 ); + kdDebug(7101) << "Set Default ACL on: " << path << " to: " << aclString << endl; + acl_free ( (void*)aclString ); } acl_free( acl ); } @@ -1691,31 +1744,48 @@ static bool isExtendedACL( acl_t acl ) { +#ifdef Q_OS_LINUX return ( acl_equiv_mode( acl, 0 ) != 0 ); +#else + acl_entry_t entry; + int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag != ACL_USER_OBJ && + currentTag != ACL_GROUP_OBJ && + currentTag != ACL_OTHER ) + return true; + ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry ); + } + return false; +#endif } static void appendACLAtoms( const QCString & path, UDSEntry& entry, mode_t type, bool withACL ) { // first check for a noop +#ifdef Q_OS_LINUX if ( acl_extended_file( path.data() ) == 0 ) return; +#endif acl_t acl = 0; acl_t defaultAcl = 0; UDSAtom atom; bool isDir = S_ISDIR( type ); // do we have an acl for the file, and/or a default acl for the dir, if it is one? - acl = acl_get_file( path.data(), ACL_TYPE_ACCESS ); + if ( acl = acl_get_file( path.data(), ACL_TYPE_ACCESS ) ) { + if ( !isExtendedACL( acl ) ) { + acl_free( acl ); + acl = 0; + } + } + /* Sadly libacl does not provided a means of checking for extended ACL and default * ACL separately. Since a directory can have both, we need to check again. */ - if ( isDir ) { - if ( acl ) { - if ( !isExtendedACL( acl ) ) { - acl_free( acl ); - acl = 0; - } - } + if ( isDir ) defaultAcl = acl_get_file( path.data(), ACL_TYPE_DEFAULT ); - } + if ( acl || defaultAcl ) { kdDebug(7101) << path.data() << " has extended ACL entries " << endl; atom.m_uds = KIO::UDS_EXTENDED_ACL; @@ -1724,16 +1794,18 @@ } if ( withACL ) { if ( acl ) { - ssize_t size = acl_size( acl ); atom.m_uds = KIO::UDS_ACL_STRING; - atom.m_str = QString::fromLatin1( acl_to_text( acl, &size ) ); + char *aclString = acl_to_text( acl, 0 ); + atom.m_str = QString::fromLatin1( aclString ); + acl_free( (void*)aclString ); entry.append( atom ); kdDebug(7101) << path.data() << "ACL: " << atom.m_str << endl; } if ( defaultAcl ) { - ssize_t size = acl_size( defaultAcl ); atom.m_uds = KIO::UDS_DEFAULT_ACL_STRING; - atom.m_str = QString::fromLatin1( acl_to_text( defaultAcl, &size ) ); + char *defaultAclString = acl_to_text( defaultAcl, 0 ); + atom.m_str = QString::fromLatin1( defaultAclString ); + acl_free( (void*)defaultAclString ); entry.append( atom ); kdDebug(7101) << path.data() << "DEFAULT ACL: " << atom.m_str << endl; } Index: kio/kfile/kpropertiesdialog.cpp =================================================================== --- kio/kfile/kpropertiesdialog.cpp (revision 462925) +++ kio/kfile/kpropertiesdialog.cpp (working copy) @@ -74,10 +74,17 @@ #include #ifdef USE_POSIX_ACL +#ifdef Q_OS_FREEBSD extern "C" { +#include +#include +} +#else +extern "C" { # include } #endif +#endif #include #include @@ -2052,8 +2059,13 @@ // FIXME make it work with partial entries if ( properties->items().count() == 1 ) { QCString pathCString = QFile::encodeName( properties->item()->url().path() ); +#ifdef Q_OS_FREEBSD + struct statfs buf; + fileSystemSupportsACLs = ( statfs( pathCString.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS ); +#else fileSystemSupportsACLs = getxattr( pathCString.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA; +#endif } if ( fileSystemSupportsACLs ) { std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) ); Index: kio/kio/kacl.cpp =================================================================== --- kio/kio/kacl.cpp (revision 462925) +++ kio/kio/kacl.cpp (working copy) @@ -28,19 +28,59 @@ #include #ifdef USE_POSIX_ACL #include +#include +#ifdef Q_OS_LINUX #include #endif +#endif #include +#include #include #include "kacl.h" - #ifdef USE_POSIX_ACL static void printACL( acl_t acl, const QString &comment ); +#ifdef Q_OS_FREEBSD +#define acl_get_perm acl_get_perm_np #endif +#endif +class SortedEntryList : public QPtrList { +protected: + int compareItems( QPtrCollection::Item i1, + QPtrCollection::Item i2 ) + { + acl_entry_t *e1 = static_cast( i1 ); + acl_entry_t *e2 = static_cast( i2 ); + + acl_tag_t tag1, tag2; + uid_t uid1 = 0, uid2 = 0; + + acl_get_tag_type( *e1, &tag1 ); + acl_get_tag_type( *e2, &tag2 ); + + if ( tag1 == ACL_USER || tag1 == ACL_GROUP ) + uid1 = *( (uid_t*) acl_get_qualifier( *e1 ) ); + + if ( tag2 == ACL_USER || tag2 == ACL_GROUP ) + uid2 = *( (uid_t*) acl_get_qualifier( *e2 ) ); + + if ( tag1 < tag2 ) + return -1; + else if ( tag1 > tag2 ) + return 1; + + if ( uid1 < uid2 ) + return -1; + else if ( uid1 > uid2 ) + return 1; + + return 0; + } +}; + class KACL::KACLPrivate { public: KACLPrivate() : m_acl( 0 ) { init(); } @@ -76,12 +116,59 @@ } KACL::KACL( mode_t basePermissions ) -#ifdef USE_POSIX_ACL +#if defined(USE_POSIX_ACL) && defined(Q_OS_LINUX) : d( new KACLPrivate( acl_from_mode( basePermissions ) ) ) #else : d( new KACLPrivate ) #endif { +#if defined(USE_POSIX_ACL) && !defined(Q_OS_LINUX) + acl_t newACL = acl_init( 3 ); + acl_entry_t entry; + acl_permset_t permset; + int error = 0; + + /* Add owner entry */ + if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) { + /* Set owner permissions */ + acl_set_tag_type( entry, ACL_USER_OBJ ); + acl_get_permset( entry, &permset ); + acl_clear_perms( permset ); + if ( basePermissions & S_IRUSR ) acl_add_perm( permset, ACL_READ ); + if ( basePermissions & S_IWUSR ) acl_add_perm( permset, ACL_WRITE ); + if ( basePermissions & S_IXUSR ) acl_add_perm( permset, ACL_EXECUTE ); + acl_set_permset( entry, permset ); + + /* Add group entry */ + if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) { + /* Set group permissions */ + acl_set_tag_type( entry, ACL_GROUP_OBJ ); + acl_get_permset( entry, &permset ); + acl_clear_perms( permset ); + if ( basePermissions & S_IRGRP ) acl_add_perm( permset, ACL_READ ); + if ( basePermissions & S_IWGRP ) acl_add_perm( permset, ACL_WRITE ); + if ( basePermissions & S_IXGRP ) acl_add_perm( permset, ACL_EXECUTE ); + acl_set_permset( entry, permset ); + + /* Add other entry */ + if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0) { + /* Set other permissions */ + acl_set_tag_type( entry, ACL_OTHER ); + acl_get_permset( entry, &permset ); + acl_clear_perms( permset ); + if ( basePermissions & S_IROTH ) acl_add_perm( permset, ACL_READ ); + if ( basePermissions & S_IWOTH ) acl_add_perm( permset, ACL_WRITE ); + if ( basePermissions & S_IXOTH ) acl_add_perm( permset, ACL_EXECUTE ); + acl_set_permset( entry, permset ); + } + } + } + + if ( error ) + acl_free ( &newACL ); + else + d->m_acl = newACL; +#endif } KACL::KACL() @@ -102,10 +189,72 @@ bool KACL::operator==( const KACL& rhs ) const { #ifdef USE_POSIX_ACL +#ifdef Q_OS_LINUX return ( acl_cmp( d->m_acl, rhs.d->m_acl ) == 0 ); -#else +#else // Q_OS_LINUX + SortedEntryList entries1, entries2; + entries1.setAutoDelete( true ); + entries2.setAutoDelete( true ); + + /* Add ACL entries to vectors */ + acl_entry_t *entry = new acl_entry_t; + int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, entry ); + while( ret == 1 ) { + entries1.append( entry ); + entry = new acl_entry_t; + ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, entry ); + } + delete entry; + + entry = new acl_entry_t; + ret = acl_get_entry( rhs.d->m_acl, ACL_FIRST_ENTRY, entry ); + while ( ret == 1 ) { + entries2.append( entry ); + entry = new acl_entry_t; + ret = acl_get_entry( rhs.d->m_acl, ACL_NEXT_ENTRY, entry ); + } + delete entry; + + /* If the entry count differs, we are done */ + if ( entries1.count() != entries2.count() ) + return false; + + /* Sort vectors */ + entries1.sort(); + entries2.sort(); + + /* Compare all entries */ + acl_permset_t permset1, permset2; + acl_tag_t tag1, tag2; + uid_t uid1, uid2; + acl_entry_t *e1, *e2; + + for ( e1 = entries1.first(), e2 = entries2.first(); e1; e1 = entries1.next(), e2 = entries2.next() ) { + /* Compare tag */ + if ( acl_get_tag_type( *e1, &tag1 ) != 0 ) return false; + if ( acl_get_tag_type( *e2, &tag2 ) != 0 ) return false; + if ( tag1 != tag2 ) return false; + + /* Compare permissions */ + if ( acl_get_permset( *e1, &permset1 ) != 0 ) return false; + if ( acl_get_permset( *e2, &permset2 ) != 0 ) return false; + if ( *permset1 != *permset2) return false; + + /* Compare uid */ + switch( tag1 ) { + case ACL_USER: + case ACL_GROUP: + uid1 = *( (uid_t*) acl_get_qualifier( *e1 ) ); + uid2 = *( (uid_t*) acl_get_qualifier( *e2 ) ); + if ( uid1 != uid2 ) return false; + } + } + return true; -#endif +#endif // Q_OS_LINUX +#else // USE_POSIX_ACL + return true; +#endif // USE_POSIX_ACL } bool KACL::isValid() const @@ -122,10 +271,25 @@ bool KACL::isExtended() const { #ifdef USE_POSIX_ACL +#ifdef Q_OS_LINUX return ( acl_equiv_mode( d->m_acl, NULL ) != 0 ); #else + acl_entry_t entry; + int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag != ACL_USER_OBJ && + currentTag != ACL_GROUP_OBJ && + currentTag != ACL_OTHER ) + return true; + ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); + } return false; #endif +#else + return false; +#endif } #ifdef USE_POSIX_ACL @@ -166,8 +330,9 @@ static void printACL( acl_t acl, const QString &comment ) { - ssize_t size = acl_size( acl ); - kdDebug() << comment << acl_to_text( acl, &size ) << endl; + char *aclString = acl_to_text( acl, 0 ); + kdDebug() << comment << aclString << endl; + acl_free( (void*)aclString ); } static int getUidForName( const QString& name ) @@ -550,6 +715,9 @@ { bool ret = false; #ifdef USE_POSIX_ACL + if ( aclStr.isEmpty() ) + return false; + acl_t temp = acl_from_text( aclStr.latin1() ); if ( acl_valid( temp ) != 0 ) { // TODO errno is set, what to do with it here? @@ -567,8 +735,10 @@ QString KACL::asString() const { #ifdef USE_POSIX_ACL - ssize_t size = acl_size( d->m_acl ); - return QString::fromLatin1( acl_to_text( d->m_acl, &size ) ); + char *aclString = acl_to_text( d->m_acl, 0 ); + QString ret = QString::fromLatin1( aclString ); + acl_free( (void*)aclString ); + return ret; #else return QString::null; #endif Index: configure.in.in =================================================================== --- configure.in.in (revision 462925) +++ configure.in.in (working copy) @@ -50,14 +50,21 @@ USE_POSIX_ACL="yes" ACL_LIBS="" -KDE_CHECK_HEADER(attr/libattr.h, ,[USE_POSIX_ACL="no"]) -KDE_CHECK_HEADER(sys/xattr.h, ,[USE_POSIX_ACL="no"]) KDE_CHECK_HEADER(sys/acl.h, ,[USE_POSIX_ACL="no"]) -KDE_CHECK_HEADER(acl/libacl.h, ,[USE_POSIX_ACL="no"]) -KDE_CHECK_LIB(acl,acl_init, ,[USE_POSIX_ACL="no"]) +case $host in + *-*-freebsd*) + KDE_CHECK_LIB(c,acl_init,[ACL_LIBS="-lc"],[USE_POSIX_ACL="no"]) + ;; + *) + KDE_CHECK_HEADER(attr/libattr.h, ,[USE_POSIX_ACL="no"]) + KDE_CHECK_HEADER(sys/xattr.h, ,[USE_POSIX_ACL="no"]) + KDE_CHECK_HEADER(acl/libacl.h, ,[USE_POSIX_ACL="no"]) + KDE_CHECK_LIB(acl,acl_init,[ACL_LIBS="-lacl -lattr"],[USE_POSIX_ACL="no"]) + ;; +esac + if test $USE_POSIX_ACL = "yes" ; then - ACL_LIBS="-lacl -lattr" AC_DEFINE([USE_POSIX_ACL], 1, [Define if system has POSIX ACL support.]) fi AC_SUBST(ACL_LIBS)