/*
- $Id: ea.c,v 1.10 2009-10-23 14:09:51 franklahm Exp $
+ $Id: ea.c,v 1.19 2010-02-10 14:05:37 franklahm Exp $
Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
This program is free software; you can redistribute it and/or modify
char *u;
size_t inplen;
size_t outlen;
- uint16_t flags = CONV_ESCAPEHEX;
+ uint16_t flags = CONV_ESCAPEHEX | CONV_ALLOW_COLON;
if (!mpath)
return NULL;
*/
static int unpack_header(struct ea * restrict ea)
{
- int ret = 0, count = 0;
+ int ret = 0;
+ unsigned int count = 0;
uint32_t uint32;
char *buf;
*/
static int pack_header(struct ea * restrict ea)
{
- int count = 0, eacount = 0;
+ unsigned int count = 0, eacount = 0;
uint16_t uint16;
uint32_t uint32;
size_t bufsize = EA_HEADER_SIZE;
return 0;
}
-/*
- * Function: ea_path
- *
- * Purpose: return name of ea header filename
- *
- * Arguments:
- *
- * ea (r) ea handle
- * eaname (r) name of EA or NULL
- *
- * Returns: pointer to name in static buffer, NULL on error
- *
- * Effects:
- *
- * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
- * Files: "file" -> "file/.AppleDouble/file::EA"
- * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
- * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
- */
-static char * ea_path(const struct ea * restrict ea,
- const char * restrict eaname)
-{
- char *adname;
- static char pathbuf[MAXPATHLEN + 1];
-
- /* get name of a adouble file from uname */
- adname = ea->vol->vfs->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
- /* copy it so we can work with it */
- strlcpy(pathbuf, adname, MAXPATHLEN + 1);
- /* append "::EA" */
- strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
-
- if (eaname) {
- strlcat(pathbuf, "::", MAXPATHLEN + 1);
- if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
- return NULL;
- strlcat(pathbuf, eaname, MAXPATHLEN + 1);
- }
-
- return pathbuf;
-}
-
/*
* Function: ea_addentry
*
size_t attrsize,
int bitmap)
{
- int count = 0;
+ unsigned int count = 0;
void *tmprealloc;
/* First check if an EA of the requested name already exist */
return -1;
}
-/*
- * Function: ea_delentry
- *
- * Purpose: delete one EA from ea->ea_entries[]
- *
- * Arguments:
- *
- * ea (rw) pointer to struct ea
- * attruname (r) EA name
- *
- * Returns: new number of EA entries, -1 on error
- *
- * Effects:
- *
- * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
- * Marks it as unused just by freeing name and setting it to NULL.
- * ea_close and pack_buffer must honor this.
- */
-static int ea_delentry(struct ea * restrict ea,
- const char * restrict attruname)
-{
- int ret = 0, count = 0;
-
- if (ea->ea_count == 0) {
- LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
- return -1;
- }
-
- while (count < ea->ea_count) {
- /* search matching EA */
- if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
- free((*ea->ea_entries)[count].ea_name);
- (*ea->ea_entries)[count].ea_name = NULL;
-
- LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
- attruname, count + 1, ea->ea_count);
-
- break;
- }
- count++;
- }
-
- return ret;
-}
-
/*
* Function: create_ea_header
*
struct stat st;
char *eaname;
- if ((eaname = ea_path(ea, attruname)) == NULL) {
+ if ((eaname = ea_path(ea, attruname, 1)) == NULL) {
LOG(log_error, logtype_afpd, "write_ea('%s'): ea_path error", attruname);
return AFPERR_MISC;
}
goto exit;
}
- if ((write(fd, ibuf, attrsize)) != attrsize) {
+ if (write(fd, ibuf, attrsize) != (ssize_t)attrsize) {
LOG(log_error, logtype_afpd, "write_ea('%s'): write: %s", eaname, strerror(errno));
ret = -1;
goto exit;
return ret;
}
+/*
+ * Function: ea_delentry
+ *
+ * Purpose: delete one EA from ea->ea_entries[]
+ *
+ * Arguments:
+ *
+ * ea (rw) pointer to struct ea
+ * attruname (r) EA name
+ *
+ * Returns: new number of EA entries, -1 on error
+ *
+ * Effects:
+ *
+ * Remove entry from ea->ea_entries[]. Decrement ea->ea_count.
+ * Marks it as unused just by freeing name and setting it to NULL.
+ * ea_close and pack_buffer must honor this.
+ */
+static int ea_delentry(struct ea * restrict ea, const char * restrict attruname)
+{
+ int ret = 0;
+ unsigned int count = 0;
+
+ if (ea->ea_count == 0) {
+ LOG(log_error, logtype_afpd, "ea_delentry('%s'): illegal ea_count of 0 on deletion");
+ return -1;
+ }
+
+ while (count < ea->ea_count) {
+ /* search matching EA */
+ if (strcmp(attruname, (*ea->ea_entries)[count].ea_name) == 0) {
+ free((*ea->ea_entries)[count].ea_name);
+ (*ea->ea_entries)[count].ea_name = NULL;
+
+ LOG(log_debug, logtype_afpd, "ea_delentry('%s'): deleted no %u/%u",
+ attruname, count + 1, ea->ea_count);
+
+ break;
+ }
+ count++;
+ }
+
+ return ret;
+}
+
/*
* Function: delete_ea_file
*
*
* Returns: 0 on success, -1 on error
*/
-static int delete_ea_file(const struct ea * restrict ea,
- const char *eaname)
+static int delete_ea_file(const struct ea * restrict ea, const char *eaname)
{
int ret = 0;
char *eafile;
struct stat st;
- if ((eafile = ea_path(ea, eaname)) == NULL) {
+ if ((eafile = ea_path(ea, eaname, 1)) == NULL) {
LOG(log_error, logtype_afpd, "delete_ea_file('%s'): ea_path error", eaname);
return -1;
}
return ret;
}
+/*************************************************************************************
+ * ea_path, ea_open and ea_close are only global so that dbd can call them
+ *************************************************************************************/
+
+/*
+ * Function: ea_path
+ *
+ * Purpose: return name of ea header filename
+ *
+ * Arguments:
+ *
+ * ea (r) ea handle
+ * eaname (r) name of EA or NULL
+ * macname (r) if != 0 call mtoupath on eaname
+ *
+ * Returns: pointer to name in static buffer, NULL on error
+ *
+ * Effects:
+ *
+ * Calls ad_open, copies buffer, appends "::EA" and if supplied append eanme
+ * Files: "file" -> "file/.AppleDouble/file::EA"
+ * Dirs: "dir" -> "dir/.AppleDouble/.Parent::EA"
+ * "file" with EA "myEA" -> "file/.AppleDouble/file::EA:myEA"
+ */
+char *ea_path(const struct ea * restrict ea, const char * restrict eaname, int macname)
+{
+ char *adname;
+ static char pathbuf[MAXPATHLEN + 1];
+
+ /* get name of a adouble file from uname */
+ adname = ea->vol->ad_path(ea->filename, (ea->ea_flags & EA_DIR) ? ADFLAGS_DIR : 0);
+ /* copy it so we can work with it */
+ strlcpy(pathbuf, adname, MAXPATHLEN + 1);
+ /* append "::EA" */
+ strlcat(pathbuf, "::EA", MAXPATHLEN + 1);
+
+ if (eaname) {
+ strlcat(pathbuf, "::", MAXPATHLEN + 1);
+ if (macname)
+ if ((eaname = mtoupath(ea->vol, eaname)) == NULL)
+ return NULL;
+ strlcat(pathbuf, eaname, MAXPATHLEN + 1);
+ }
+
+ return pathbuf;
+}
+
/*
* Function: ea_open
*
* Eiterh EA_RDONLY or EA_RDWR MUST be requested
* ea (w) pointer to a struct ea that we fill
*
- * Returns: 0 on success, -1 on error
+ * Returns: 0 on success
+ * -1 on misc error with errno = EFAULT
+ * -2 if no EA header exists with errno = ENOENT
*
* Effects:
*
* file is either read or write locked depending on the open flags.
* When you're done with struct ea you must call ea_close on it.
*/
-static int ea_open(const struct vol * restrict vol,
- const char * restrict uname,
- eaflags_t eaflags,
- struct ea * restrict ea)
+int ea_open(const struct vol * restrict vol,
+ const char * restrict uname,
+ eaflags_t eaflags,
+ struct ea * restrict ea)
{
int ret = 0;
char *eaname;
ea->vol = vol; /* ea_close needs it */
ea->ea_flags = eaflags;
- /* Dont check for errors, eg when removing the file is already gone */
- stat(uname, &st);
- if (S_ISDIR(st.st_mode))
+ /* Dont care for errors, eg when removing the file is already gone */
+ if (!stat(uname, &st) && S_ISDIR(st.st_mode))
ea->ea_flags |= EA_DIR;
if ( ! (ea->filename = strdup(uname))) {
return -1;
}
- eaname = ea_path(ea, NULL);
+ eaname = ea_path(ea, NULL, 0);
LOG(log_maxdebug, logtype_afpd, "ea_open: ea_path: %s", eaname);
/* Check if it exists, if not create it if EA_CREATE is in eaflags */
if ( ! (eaflags & EA_CREATE)) {
/* creation was not requested, so return with error */
- ret = -1;
+ ret = -2;
goto exit;
}
/* header file exists, so read and parse it */
/* malloc buffer where we read disk file into */
+ if (st.st_size < EA_HEADER_SIZE) {
+ LOG(log_error, logtype_afpd, "ea_open('%s'): bogus EA header file", eaname);
+ ret = -1;
+ goto exit;
+ }
ea->ea_size = st.st_size;
ea->ea_data = malloc(st.st_size);
if (! ea->ea_data) {
}
/* read it */
- if ((read(ea->ea_fd, ea->ea_data, ea->ea_size)) != ea->ea_size) {
+ if (read(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
LOG(log_error, logtype_afpd, "ea_open: short read on header: %s", eaname);
ret = -1;
goto exit;
}
exit:
- if (ret == 0) {
+ switch (ret) {
+ case 0:
ea->ea_inited = EA_INITED;
- } else {
+ break;
+ case -1:
+ errno = EFAULT; /* force some errno distinguishable from ENOENT */
+ /* fall through */
+ case -2:
if (ea->ea_data) {
free(ea->ea_data);
ea->ea_data = NULL;
close(ea->ea_fd);
ea->ea_fd = -1;
}
+ break;
}
return ret;
* Flushes and then closes and frees all resouces held by ea handle.
* Pack data in ea into ea_data, then write ea_data to disk
*/
-static int ea_close(struct ea * restrict ea)
+int ea_close(struct ea * restrict ea)
{
- int ret = 0, count = 0;
+ int ret = 0;
+ unsigned int count = 0;
char *eaname;
struct stat st;
} else {
if (ea->ea_count == 0) {
/* Check if EA header exists and remove it */
- eaname = ea_path(ea, NULL);
+ eaname = ea_path(ea, NULL, 0);
if ((stat(eaname, &st)) == 0) {
if ((unlink(eaname)) != 0) {
LOG(log_error, logtype_afpd, "ea_close('%s'): unlink: %s",
goto exit;
}
- if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != ea->ea_size) {
+ if (write(ea->ea_fd, ea->ea_data, ea->ea_size) != (ssize_t)ea->ea_size) {
LOG(log_error, logtype_afpd, "ea_close: write: %s", strerror(errno));
ret = -1;
}
*/
int get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
{
- int ret = AFPERR_MISC, count = 0;
+ int ret = AFPERR_MISC;
+ unsigned int count = 0;
uint32_t uint32;
struct ea ea;
LOG(log_debug, logtype_afpd, "get_easize: file: %s", uname);
if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
- LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
- return AFPERR_MISC;
+ if (errno != ENOENT)
+ LOG(log_error, logtype_afpd, "get_easize: error calling ea_open for file: %s", uname);
+
+ memset(rbuf, 0, 4);
+ *rbuflen += 4;
+ return ret;
}
while (count < ea.ea_count) {
*/
int get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
{
- int ret = AFPERR_MISC, count = 0, fd = -1;
+ int ret = AFPERR_MISC, fd = -1;
+ unsigned int count = 0;
uint32_t uint32;
size_t toread;
struct ea ea;
LOG(log_debug, logtype_afpd, "get_eacontent('%s/%s')", uname, attruname);
if ((ea_open(vol, uname, EA_RDONLY, &ea)) != 0) {
- LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
- return AFPERR_MISC;
+ if (errno != ENOENT)
+ LOG(log_error, logtype_afpd, "get_eacontent('%s'): ea_open error", uname);
+ memset(rbuf, 0, 4);
+ *rbuflen += 4;
+ return ret;
}
while (count < ea.ea_count) {
if (strcmp(attruname, (*ea.ea_entries)[count].ea_name) == 0) {
- if ( (eafile = ea_path(&ea, attruname)) == NULL) {
+ if ( (eafile = ea_path(&ea, attruname, 1)) == NULL) {
ret = AFPERR_MISC;
break;
}
rbuf += 4;
*rbuflen += 4;
- if ((read(fd, rbuf, toread)) != toread) {
+ if (read(fd, rbuf, toread) != (ssize_t)toread) {
LOG(log_error, logtype_afpd, "get_eacontent('%s/%s'): short read", uname, attruname);
ret = AFPERR_MISC;
break;
*/
int list_eas(VFS_FUNC_ARGS_EA_LIST)
{
- int count = 0, attrbuflen = *buflen, ret = AFP_OK, len;
+ unsigned int count = 0;
+ int attrbuflen = *buflen, ret = AFP_OK, len;
char *buf = attrnamebuf;
struct ea ea;
int ea_deletefile(VFS_FUNC_ARGS_DELETEFILE)
{
- LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
-
- int count = 0, ret = AFP_OK;
+ unsigned int count = 0;
+ int ret = AFP_OK;
struct ea ea;
+ LOG(log_debug, logtype_afpd, "ea_deletefile('%s')", file);
+
/* Open EA stuff */
if ((ea_open(vol, file, EA_RDWR, &ea)) != 0) {
if (errno == ENOENT)
int ea_renamefile(VFS_FUNC_ARGS_RENAMEFILE)
{
- int count = 0;
+ unsigned int count = 0;
int ret = AFP_OK;
size_t easize;
char srceapath[ MAXPATHLEN + 1];
easize = (*srcea.ea_entries)[count].ea_size;
/* Build src and dst paths for rename() */
- if ((eapath = ea_path(&srcea, eaname)) == NULL) {
+ if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
strcpy(srceapath, eapath);
- if ((eapath = ea_path(&dstea, eaname)) == NULL) {
+ if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
int ea_copyfile(VFS_FUNC_ARGS_COPYFILE)
{
- int count = 0;
+ unsigned int count = 0;
int ret = AFP_OK;
size_t easize;
char srceapath[ MAXPATHLEN + 1];
easize = (*srcea.ea_entries)[count].ea_size;
/* Build src and dst paths for copy_file() */
- if ((eapath = ea_path(&srcea, eaname)) == NULL) {
+ if ((eapath = ea_path(&srcea, eaname, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
strcpy(srceapath, eapath);
- if ((eapath = ea_path(&dstea, eaname)) == NULL) {
+ if ((eapath = ea_path(&dstea, eaname, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
int ea_chown(VFS_FUNC_ARGS_CHOWN)
{
- LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
- int count = 0, ret = AFP_OK;
+ unsigned int count = 0;
+ int ret = AFP_OK;
char *eaname;
struct ea ea;
+ LOG(log_debug, logtype_afpd, "ea_chown('%s')", path);
/* Open EA stuff */
if ((ea_open(vol, path, EA_RDWR, &ea)) != 0) {
if (errno == ENOENT)
}
}
- if ((chown(ea_path(&ea, NULL), uid, gid)) != 0) {
+ if ((lchown(ea_path(&ea, NULL, 0), uid, gid)) != 0) {
switch (errno) {
case EPERM:
case EACCES:
}
while (count < ea.ea_count) {
- if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
+ if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
- if ((chown(eaname, uid, gid)) != 0) {
+ if ((lchown(eaname, uid, gid)) != 0) {
switch (errno) {
case EPERM:
case EACCES:
int ea_chmod_file(VFS_FUNC_ARGS_SETFILEMODE)
{
- LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
- int count = 0, ret = AFP_OK;
+ unsigned int count = 0;
+ int ret = AFP_OK;
const char *eaname;
struct ea ea;
+ LOG(log_debug, logtype_afpd, "ea_chmod_file('%s')", name);
/* Open EA stuff */
if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
if (errno == ENOENT)
}
/* Set mode on EA header file */
- if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
- LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL), strerror(errno));
+ if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_file('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
switch (errno) {
case EPERM:
case EACCES:
/* Set mode on EA files */
while (count < ea.ea_count) {
- if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name)) == NULL) {
+ if ((eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}
int ea_chmod_dir(VFS_FUNC_ARGS_SETDIRUNIXMODE)
{
- LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
int ret = AFP_OK;
+ unsigned int count = 0;
uid_t uid;
const char *eaname;
const char *eaname_safe = NULL;
struct ea ea;
+ LOG(log_debug, logtype_afpd, "ea_chmod_dir('%s')", name);
/* .AppleDouble already might be inaccesible, so we must run as id 0 */
uid = geteuid();
if (seteuid(0)) {
LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): seteuid: %s", name, strerror(errno));
- ret = AFPERR_MISC;
- goto exit;
+ return AFPERR_MISC;
}
/* Open EA stuff */
if ((ea_open(vol, name, EA_RDWR, &ea)) != 0) {
- if (errno == ENOENT)
- /* no EA files, nothing to do */
- return AFP_OK;
- else
- return AFPERR_MISC;
+ /* ENOENT --> no EA files, nothing to do */
+ if (errno != ENOENT)
+ ret = AFPERR_MISC;
+ if (seteuid(uid) < 0) {
+ LOG(log_error, logtype_afpd, "can't seteuid back: %s", strerror(errno));
+ exit(EXITERR_SYS);
+ }
+ return ret;
}
/* Set mode on EA header */
- if ((setfilmode(ea_path(&ea, NULL), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
- LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL), strerror(errno));
+ if ((setfilmode(ea_path(&ea, NULL, 0), ea_header_mode(mode), NULL, vol->v_umask)) != 0) {
+ LOG(log_error, logtype_afpd, "ea_chmod_dir('%s'): %s", ea_path(&ea, NULL, 0), strerror(errno));
switch (errno) {
case EPERM:
case EACCES:
}
/* Set mode on EA files */
- int count = 0;
while (count < ea.ea_count) {
eaname = (*ea.ea_entries)[count].ea_name;
/*
LOG(log_warning, logtype_afpd, "ea_chmod_dir('%s'): contains a slash", eaname);
eaname = eaname_safe;
}
- if ((eaname = ea_path(&ea, eaname)) == NULL) {
+ if ((eaname = ea_path(&ea, eaname, 1)) == NULL) {
ret = AFPERR_MISC;
goto exit;
}