All checks were successful
Source release / source-package (push) Successful in 46s
The WebSDK/NCP AFP File Information records use a distinct SetInfo request bitmap and attribute word. The previous smoke-oriented implementation reused the low response-bit positions for Set Attributes, Modify Date/Time, FinderInfo, Hidden/Invisible, System, and Archive. That made the current tests pass, but it was not faithful to the documented header semantics and risked keeping AFP metadata parallel to existing NetWare attributes. Switch Set File Information to the documented request bitmap values: 0x0100 for Attributes, 0x1000 for Modify Date/Time, and 0x4000 for FinderInfo. Switch the AFP attribute word to the documented NetWare-style bits: Hidden 0x0200, System 0x0400, Subdirectory 0x1000, and Archive 0x2000. Map Hidden, System, and Archive through the existing NetWare attribute store via FILE_ATTR_H, FILE_ATTR_S, and FILE_ATTR_A. This keeps AFP Set/Get/Scan aligned with mars_nwe's existing attribute helper instead of maintaining duplicate AFP-only xattr state. FinderInfo remains AFP metadata and still uses the Modify-rights gate added earlier. Update the Linux smoke helper and suite to use --hidden / --clear-hidden while keeping --invisible / --clear-invisible as compatibility aliases. Document the corrected WebSDK bit values and the convergence rule that NetWare attributes must use mars_nwe NetWare helpers. 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
333 lines
7.5 KiB
C
333 lines
7.5 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_HIDDEN 0x0200
|
|
#define NWATALK_AFP_ATTR_SYSTEM 0x0400
|
|
#define NWATALK_AFP_ATTR_SETCLR 0x8000
|
|
#define NWATALK_AFP_ATTR_STORED_MASK \
|
|
(NWATALK_AFP_ATTR_HIDDEN | NWATALK_AFP_ATTR_SYSTEM)
|
|
|
|
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
|
|
}
|