All checks were successful
Source release / source-package (push) Successful in 51s
The NWAFP Set File Information attribute word uses a metadata bit that our earlier smoke code called Backup. That name is too easy to confuse with the distinct AFP backup date/time fields in the Set/Get File Information records. Treat the bit consistently as the Archive attribute in the server constants, Netatalk/xattr helper mask, Linux smoke helper, smoke suite, and documentation. Keep --backup and --clear-backup as compatibility aliases in afp_set_file_info_smoke so existing local invocations continue to work, but make the generated suite use --archive and --clear-archive. The source-level xattr payload remains unchanged: org.mars-nwe.afp.attributes still stores the same versioned attribute word and bit value 0x0040. Tests: git diff --check; bash -n tests/linux/afp_smoke_suite.sh; gcc -Iinclude -I/mnt/data/stubs -fsyntax-only tests/linux/afp_set_file_info_smoke.c
334 lines
7.6 KiB
C
334 lines
7.6 KiB
C
/* Optional Netatalk/libatalk AFP metadata backend helpers. */
|
|
#include "net.h"
|
|
#include "config.h"
|
|
#include "nwatalk.h"
|
|
#include "nwxattr.h"
|
|
#include "tools.h"
|
|
|
|
#include <string.h>
|
|
|
|
#if XATTR_SUPPORT
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#if NETATALK_SUPPORT
|
|
#include <sys/stat.h>
|
|
#include <atalk/adouble.h>
|
|
#endif
|
|
|
|
#define MARS_NWE_AFP_ENTRY_ID_XATTR "org.mars-nwe.afp.entry-id"
|
|
#define MARS_NWE_AFP_FINDER_INFO_XATTR "org.mars-nwe.afp.finder-info"
|
|
#define MARS_NWE_AFP_ATTRIBUTES_XATTR "org.mars-nwe.afp.attributes"
|
|
#define MARS_NWE_AFP_ENTRY_ID_VERSION 1
|
|
#define MARS_NWE_AFP_ATTRIBUTES_VERSION 1
|
|
|
|
#define NWATALK_AFP_ATTR_INVISIBLE 0x0001
|
|
#define NWATALK_AFP_ATTR_SYSTEM 0x0004
|
|
#define NWATALK_AFP_ATTR_ARCHIVE 0x0040
|
|
#define NWATALK_AFP_ATTR_SETCLR 0x8000
|
|
#define NWATALK_AFP_ATTR_STORED_MASK \
|
|
(NWATALK_AFP_ATTR_INVISIBLE | NWATALK_AFP_ATTR_SYSTEM | NWATALK_AFP_ATTR_ARCHIVE)
|
|
|
|
typedef struct {
|
|
uint8 version;
|
|
uint8 reserved[3];
|
|
uint8 entry_id[4];
|
|
} MARS_NWE_AFP_ENTRY_ID_XATTR_DATA;
|
|
|
|
typedef struct {
|
|
uint8 version;
|
|
uint8 reserved;
|
|
uint8 attributes[2];
|
|
} MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA;
|
|
|
|
|
|
#if XATTR_SUPPORT
|
|
static int nwatalk_get_mars_entry_id_xattr(const char *path, uint32 *entry_id)
|
|
{
|
|
MARS_NWE_AFP_ENTRY_ID_XATTR_DATA d;
|
|
ssize_t len;
|
|
uint32 id;
|
|
|
|
if (!entry_id) return(-0x9c);
|
|
*entry_id = 0;
|
|
if (!path || !*path) return(-0x9c);
|
|
|
|
memset(&d, 0, sizeof(d));
|
|
len = mars_nwe_getxattr(path, MARS_NWE_AFP_ENTRY_ID_XATTR, &d, sizeof(d));
|
|
if (len != sizeof(d) || d.version != MARS_NWE_AFP_ENTRY_ID_VERSION)
|
|
return(-0x9c);
|
|
|
|
id = GET_BE32(d.entry_id);
|
|
id &= 0x7fffffffU;
|
|
if (!id) return(-0x9c);
|
|
|
|
*entry_id = id;
|
|
return(0);
|
|
}
|
|
#else
|
|
static int nwatalk_get_mars_entry_id_xattr(const char *path, uint32 *entry_id)
|
|
{
|
|
(void)path;
|
|
if (entry_id) *entry_id = 0;
|
|
return(-0xbf);
|
|
}
|
|
#endif
|
|
|
|
int nwatalk_backend_available(void)
|
|
{
|
|
#if NETATALK_SUPPORT
|
|
return(1);
|
|
#else
|
|
return(0);
|
|
#endif
|
|
}
|
|
|
|
#if NETATALK_SUPPORT
|
|
static int nwatalk_open_adouble(const char *path, struct adouble *ad)
|
|
{
|
|
int result;
|
|
|
|
if (!path || !*path) return(-0x9c); /* invalid path */
|
|
|
|
ad_init_old(ad, AD_VERSION, 0);
|
|
|
|
result = ad_open(ad, path,
|
|
ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDONLY |
|
|
ADFLAGS_NOHF | ADFLAGS_NORF);
|
|
if (result < 0) {
|
|
return(-0x9c);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
int nwatalk_get_finder_info(const char *path, uint8 *finder_info,
|
|
int finder_info_len)
|
|
{
|
|
#if XATTR_SUPPORT
|
|
ssize_t len;
|
|
#endif
|
|
#if NETATALK_SUPPORT
|
|
struct adouble ad;
|
|
void *entry;
|
|
int result;
|
|
#endif
|
|
|
|
if (!finder_info || finder_info_len < NWATALK_FINDER_INFO_LEN) {
|
|
return(-0x9c);
|
|
}
|
|
|
|
memset(finder_info, 0, finder_info_len);
|
|
|
|
#if XATTR_SUPPORT
|
|
if (path && *path) {
|
|
len = mars_nwe_getxattr(path, MARS_NWE_AFP_FINDER_INFO_XATTR,
|
|
finder_info, NWATALK_FINDER_INFO_LEN);
|
|
if (len == NWATALK_FINDER_INFO_LEN)
|
|
return(0);
|
|
memset(finder_info, 0, finder_info_len);
|
|
}
|
|
#endif
|
|
|
|
#if NETATALK_SUPPORT
|
|
result = nwatalk_open_adouble(path, &ad);
|
|
if (result < 0) return(result);
|
|
|
|
entry = ad_entry(&ad, ADEID_FINDERI);
|
|
if (entry && ad_getentrylen(&ad, ADEID_FINDERI) >= NWATALK_FINDER_INFO_LEN) {
|
|
memcpy(finder_info, entry, NWATALK_FINDER_INFO_LEN);
|
|
}
|
|
|
|
ad_close(&ad, 0);
|
|
return(0);
|
|
#else
|
|
(void)path;
|
|
return(-0xbf); /* invalid namespace / backend unavailable */
|
|
#endif
|
|
}
|
|
|
|
int nwatalk_set_finder_info(const char *path, const uint8 *finder_info,
|
|
int finder_info_len)
|
|
{
|
|
#if XATTR_SUPPORT
|
|
if (!path || !*path || !finder_info || finder_info_len < NWATALK_FINDER_INFO_LEN)
|
|
return(-0x9c);
|
|
|
|
if (mars_nwe_setxattr(path, MARS_NWE_AFP_FINDER_INFO_XATTR,
|
|
finder_info, NWATALK_FINDER_INFO_LEN, 0)) {
|
|
int err = errno;
|
|
XDPRINTF((3,0,"AFP FinderInfo xattr write failed for %s errno=%d", path, err));
|
|
return(-0x8c); /* no write privileges / cannot persist metadata */
|
|
}
|
|
|
|
return(0);
|
|
#else
|
|
(void)path;
|
|
(void)finder_info;
|
|
(void)finder_info_len;
|
|
return(-0xbf); /* invalid namespace / xattrs unavailable */
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
int nwatalk_get_afp_attributes(const char *path, uint16 *attributes)
|
|
{
|
|
#if XATTR_SUPPORT
|
|
MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA d;
|
|
ssize_t len;
|
|
#endif
|
|
|
|
if (!attributes) return(-0x9c);
|
|
*attributes = 0;
|
|
if (!path || !*path) return(-0x9c);
|
|
|
|
#if XATTR_SUPPORT
|
|
memset(&d, 0, sizeof(d));
|
|
len = mars_nwe_getxattr(path, MARS_NWE_AFP_ATTRIBUTES_XATTR,
|
|
&d, sizeof(d));
|
|
if (len == sizeof(d) && d.version == MARS_NWE_AFP_ATTRIBUTES_VERSION) {
|
|
*attributes = GET_BE16(d.attributes) & NWATALK_AFP_ATTR_STORED_MASK;
|
|
return(0);
|
|
}
|
|
#else
|
|
(void)path;
|
|
#endif
|
|
|
|
return(-0x9c);
|
|
}
|
|
|
|
int nwatalk_set_afp_attributes(const char *path, uint16 attributes)
|
|
{
|
|
#if XATTR_SUPPORT
|
|
MARS_NWE_AFP_ATTRIBUTES_XATTR_DATA d;
|
|
uint16 stored = 0;
|
|
uint16 requested = attributes & ~NWATALK_AFP_ATTR_SETCLR;
|
|
uint16 current = 0;
|
|
|
|
if (!path || !*path) return(-0x9c);
|
|
|
|
if (requested & ~NWATALK_AFP_ATTR_STORED_MASK) {
|
|
XDPRINTF((3,0,"AFP attributes xattr write rejected for %s attrs=0x%04x", path, attributes));
|
|
return(-0x9c);
|
|
}
|
|
|
|
(void)nwatalk_get_afp_attributes(path, ¤t);
|
|
if (attributes & NWATALK_AFP_ATTR_SETCLR)
|
|
stored = current | requested;
|
|
else
|
|
stored = current & ~requested;
|
|
|
|
memset(&d, 0, sizeof(d));
|
|
d.version = MARS_NWE_AFP_ATTRIBUTES_VERSION;
|
|
U16_TO_BE16(stored, d.attributes);
|
|
|
|
if (mars_nwe_setxattr(path, MARS_NWE_AFP_ATTRIBUTES_XATTR,
|
|
&d, sizeof(d), 0)) {
|
|
int err = errno;
|
|
XDPRINTF((3,0,"AFP attributes xattr write failed for %s attrs=0x%04x errno=%d", path, stored, err));
|
|
return(-0x8c);
|
|
}
|
|
|
|
return(0);
|
|
#else
|
|
(void)path;
|
|
(void)attributes;
|
|
return(-0xbf);
|
|
#endif
|
|
}
|
|
|
|
int nwatalk_set_entry_id(const char *path, uint32 entry_id)
|
|
{
|
|
#if XATTR_SUPPORT
|
|
MARS_NWE_AFP_ENTRY_ID_XATTR_DATA d;
|
|
|
|
if (!path || !*path) return(-0x9c);
|
|
entry_id &= 0x7fffffffU;
|
|
if (!entry_id) return(-0x9c);
|
|
|
|
memset(&d, 0, sizeof(d));
|
|
d.version = MARS_NWE_AFP_ENTRY_ID_VERSION;
|
|
U32_TO_BE32(entry_id, d.entry_id);
|
|
|
|
if (mars_nwe_setxattr(path, MARS_NWE_AFP_ENTRY_ID_XATTR,
|
|
&d, sizeof(d), 0)) {
|
|
int err = errno;
|
|
XDPRINTF((5,0,"AFP entry-id xattr write ignored for %s entry=0x%08x errno=%d",
|
|
path, entry_id, err));
|
|
return(-0x8c);
|
|
}
|
|
|
|
return(0);
|
|
#else
|
|
(void)path;
|
|
(void)entry_id;
|
|
return(-0xbf);
|
|
#endif
|
|
}
|
|
|
|
int nwatalk_get_resource_fork_size(const char *path, uint32 *resource_size)
|
|
{
|
|
#if NETATALK_SUPPORT
|
|
struct adouble ad;
|
|
off_t size;
|
|
int result;
|
|
|
|
if (!resource_size) return(-0x9c);
|
|
*resource_size = 0;
|
|
|
|
result = nwatalk_open_adouble(path, &ad);
|
|
if (result < 0) return(result);
|
|
|
|
size = ad_size(&ad, ADEID_RFORK);
|
|
if (size > 0) {
|
|
if (size > 0xffffffffUL) size = 0xffffffffUL;
|
|
*resource_size = (uint32)size;
|
|
}
|
|
|
|
ad_close(&ad, 0);
|
|
return(0);
|
|
#else
|
|
(void)path;
|
|
(void)resource_size;
|
|
return(-0xbf); /* invalid namespace / backend unavailable */
|
|
#endif
|
|
}
|
|
|
|
|
|
int nwatalk_get_entry_id(const char *path, uint32 *entry_id)
|
|
{
|
|
int result;
|
|
|
|
if (!entry_id) return(-0x9c);
|
|
*entry_id = 0;
|
|
if (!path || !*path) return(-0x9c);
|
|
|
|
result = nwatalk_get_mars_entry_id_xattr(path, entry_id);
|
|
if (!result && *entry_id)
|
|
return(0);
|
|
|
|
#if NETATALK_SUPPORT
|
|
{
|
|
struct adouble ad;
|
|
struct stat stbuff;
|
|
uint32_t id;
|
|
|
|
if (stat(path, &stbuff)) return(-0x9c);
|
|
|
|
result = nwatalk_open_adouble(path, &ad);
|
|
if (result < 0) return(result);
|
|
|
|
id = ad_getid(&ad, stbuff.st_dev, stbuff.st_ino, 0, NULL);
|
|
if (id) *entry_id = (uint32)id;
|
|
|
|
ad_close(&ad, 0);
|
|
return(id ? 0 : -0x9c);
|
|
}
|
|
#else
|
|
(void)result;
|
|
return(-0xbf); /* invalid namespace / backend unavailable */
|
|
#endif
|
|
}
|