+static int ad_mkrf_ea(const char *path _U_)
+{
+ AFP_PANIC("ad_mkrf_ea: dont use");
+ return 0;
+}
+
+static int ad_mkrf_osx(const char *path _U_)
+{
+ return 0;
+}
+
+/* ----------------
+ if we are root change path user/ group
+ It can be a native function for BSD cf. FAQ.Q10
+ path: pathname to chown
+ stbuf: parent directory inode
+
+ use fstat and fchown or lchown with linux?
+*/
+#define EMULATE_SUIDDIR
+
+static int ad_chown(const char *path, struct stat *stbuf)
+{
+ int ret = 0;
+#ifdef EMULATE_SUIDDIR
+ uid_t id;
+
+ if (default_uid != (uid_t)-1) {
+ /* we are root (admin) */
+ id = (default_uid)?default_uid:stbuf->st_uid;
+ ret = lchown( path, id, stbuf->st_gid );
+ }
+#endif
+ return ret;
+}
+
+#define DEFMASK 07700 /* be conservative */
+
+/* ----------------
+ return access right and inode of path parent directory
+*/
+static int ad_mode_st(const char *path, mode_t *mode, struct stat *stbuf)
+{
+ if (*mode == 0) {
+ return -1;
+ }
+ if (ad_stat(path, stbuf) != 0) {
+ *mode &= DEFMASK;
+ return -1;
+ }
+ *mode &= stbuf->st_mode;
+ return 0;
+}
+
+/* --------------------------- */
+static int ad_header_upgrade(struct adouble *ad _U_, const char *name _U_)
+{
+ return 0;
+}
+
+static int ad_header_upgrade_ea(struct adouble *ad _U_, const char *name _U_)
+{
+ AFP_PANIC("ad_header_upgrade_ea: dont use");
+ return 0;
+}
+
+/*!
+ * Error handling for adouble header(=metadata) file open error
+ *
+ * We're called because opening ADFLAGS_HF caused an error.
+ * 1. In case ad_open is called with ADFLAGS_NOHF the error is suppressed.
+ * 2. If ad_open was called with ADFLAGS_DF we may have opened the datafork and thus
+ * ought to close it before returning with an error condition.
+ */
+static int ad_error(struct adouble *ad, int adflags)
+{
+ int err = errno;
+ if (adflags & ADFLAGS_NOHF) { /* 1 */
+ ad->ad_adflags &= ~ADFLAGS_HF;
+ return 0;
+ }
+ if (adflags & ADFLAGS_DF) { /* 2 */
+ ad_close( ad, ADFLAGS_DF );
+ err = errno;
+ }
+ return -1 ;
+}
+
+/* Map ADFLAGS to open() flags */
+static int ad2openflags(int adflags)
+{
+ int oflags = 0;
+
+ if (adflags & ADFLAGS_RDWR)
+ oflags |= O_RDWR;
+ if (adflags & ADFLAGS_RDONLY) {
+ if (adflags & ADFLAGS_SETSHRMD)
+ oflags |= O_RDWR;
+ else
+ oflags |= O_RDONLY;
+ }
+ if (adflags & ADFLAGS_CREATE)
+ oflags |= O_CREAT;
+ if (adflags & ADFLAGS_EXCL)
+ oflags |= O_EXCL;
+ if (adflags & ADFLAGS_TRUNC)
+ oflags |= O_TRUNC;
+
+ return oflags;
+}
+
+static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble *ad)
+{
+ struct stat st_dir;
+ int oflags;
+ mode_t admode;
+ int st_invalid = -1;
+ ssize_t lsz;
+
+ LOG(log_debug, logtype_default, "ad_open_df(\"%s\", %04o)",
+ fullpathname(path), mode);
+
+ if (ad_data_fileno(ad) != -1) {
+ /* the file is already open, but we want write access: */
+ if ((adflags & ADFLAGS_RDWR)
+ /* and it was denied the first time: */
+ && (ad->ad_data_fork.adf_flags & O_RDONLY)) {
+ errno = EACCES;
+ return -1;
+ }
+ /* it's not new anymore */
+ ad->ad_data_fork.adf_flags &= ~( O_TRUNC | O_CREAT );
+ ad->ad_data_fork.adf_refcount++;
+ return 0;
+ }
+
+ oflags = O_NOFOLLOW | ad2openflags(adflags);
+
+ admode = mode;
+ if ((adflags & ADFLAGS_CREATE)) {
+ st_invalid = ad_mode_st(path, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV))
+ admode = mode;
+ }
+
+ ad->ad_data_fork.adf_fd = open(path, oflags, admode);
+
+ if (ad->ad_data_fork.adf_fd == -1) {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ case EROFS:
+ if ((adflags & ADFLAGS_SETSHRMD) && (adflags & ADFLAGS_RDONLY)) {
+ oflags &= ~O_RDWR;
+ oflags |= O_RDONLY;
+ if ((ad->ad_data_fork.adf_fd = open(path, oflags, admode)) == -1)
+ return -1;
+ break;
+ }
+ return -1;
+ case OPEN_NOFOLLOW_ERRNO:
+ ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1);
+ if ((lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN)) <= 0) {
+ free(ad->ad_data_fork.adf_syml);
+ return -1;
+ }
+ ad->ad_data_fork.adf_syml[lsz] = 0;
+ ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if (!st_invalid)
+ ad_chown(path, &st_dir); /* just created, set owner if admin (root) */
+
+ ad->ad_data_fork.adf_flags = oflags;
+ adf_lock_init(&ad->ad_data_fork);
+ ad->ad_data_fork.adf_refcount++;
+
+ return 0;
+}
+
+/* TODO: error handling */
+static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adouble *ad)
+{
+ struct stat st_dir;
+ struct stat st_meta;
+ struct stat *pst = NULL;
+ const char *ad_p;
+ int oflags, nocreatflags;
+ mode_t admode;
+ int st_invalid = -1;
+
+ if (ad_meta_fileno(ad) != -1) {
+ /* the file is already open, but we want write access: */
+ if ((adflags & ADFLAGS_RDWR) &&
+ /* and it was already denied: */
+ (ad->ad_mdp->adf_flags & O_RDONLY)) {
+ errno = EACCES;
+ return -1;
+ }
+ ad_refresh(path, ad);
+ /* it's not new anymore */
+ ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
+ ad->ad_mdp->adf_refcount++;
+ return 0;
+ }
+
+ ad_p = ad->ad_ops->ad_path(path, adflags);
+ oflags = O_NOFOLLOW | ad2openflags(adflags);
+ nocreatflags = oflags & ~(O_CREAT | O_EXCL);
+
+ ad->ad_mdp->adf_fd = open(ad_p, nocreatflags);
+
+ if (ad->ad_mdp->adf_fd != -1) {
+ ad->ad_mdp->adf_flags = nocreatflags;
+ } else {
+ switch (errno) {
+ case EACCES:
+ case EPERM:
+ case EROFS:
+ if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD)) {
+ nocreatflags &= ~O_RDWR;
+ nocreatflags |= O_RDONLY;
+ if ((ad->ad_mdp->adf_fd = open(ad_p, nocreatflags)) == -1)
+ return -1;
+ ad->ad_mdp->adf_flags = nocreatflags;
+ break;
+ }
+ return -1;
+ case ENOENT:
+ if (!(oflags & O_CREAT))
+ return ad_error(ad, adflags);
+ /*
+ * We're expecting to create a new adouble header file here
+ */
+ LOG(log_debug, logtype_default, "ad_open(\"%s\"): creating adouble file",
+ fullpathname(path));
+ admode = mode;
+ errno = 0;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV))
+ admode = mode;
+ admode = ad_hf_mode(admode);
+ if ((errno == ENOENT) && (ad->ad_vers != AD_VERSION2_OSX)) {
+ if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
+ return ad_error(ad, adflags);
+ }
+ admode = mode;
+ st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
+ if ((ad->ad_options & ADVOL_UNIXPRIV))
+ admode = mode;
+ admode = ad_hf_mode(admode);
+ }
+
+ /* retry with O_CREAT */
+ ad->ad_mdp->adf_fd = open(ad_p, oflags, admode);
+ if ( ad->ad_mdp->adf_fd < 0 )
+ return ad_error(ad, adflags);
+
+ ad->ad_mdp->adf_flags = oflags;
+ /* just created, set owner if admin owner (root) */
+ if (!st_invalid)
+ ad_chown(ad_p, &st_dir);
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ if (!(ad->ad_mdp->adf_flags & O_CREAT)) {
+ /* check for 0 length files, treat them as new. */
+ if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0) {
+ if (st_meta.st_size == 0)
+ ad->ad_mdp->adf_flags |= O_TRUNC;
+ else
+ /* we have valid data in st_meta stat structure, reused it in ad_header_read */
+ pst = &st_meta;
+ }
+ }
+
+ adf_lock_init(ad->ad_mdp);
+ ad->ad_mdp->adf_refcount = 1;
+
+ if ((ad->ad_mdp->adf_flags & ( O_TRUNC | O_CREAT ))) {
+ /* This is a new adouble header file, create it */
+ if (new_ad_header(ad, path, pst, adflags) < 0) {
+ int err = errno;
+ /* the file is already deleted, perm, whatever, so return an error */
+ ad_close(ad, adflags);
+ errno = err;
+ return -1;
+ }
+ ad_flush(ad);
+ } else {
+ /* Read the adouble header in and parse it.*/
+ if (ad->ad_ops->ad_header_read(path, ad, pst) < 0
+ || ad->ad_ops->ad_header_upgrade(ad, ad_p) < 0) {
+ int err = errno;
+ ad_close(ad, adflags);
+ errno = err;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/* TODO: error handling */
+static int ad_open_hf_ea(const char *path, int adflags, int mode, struct adouble *ad)
+{
+ ssize_t rforklen;
+ int oflags;
+
+ LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\", %s, %04o)",
+ path, adflags2logstr(adflags), mode);
+
+ oflags = O_NOFOLLOW | (ad2openflags(adflags) & ~(O_CREAT | O_TRUNC));
+
+ if (ad_meta_fileno(ad) != -1) {
+ /* the file is already open, but we want write access: */
+ if ((adflags & ADFLAGS_RDWR) &&
+ /* and it was already denied: */
+ (ad->ad_mdp->adf_flags & O_RDONLY)) {
+ LOG(log_error, logtype_default, "ad_open_hf_ea(%s): rw request for ro file: %s",
+ fullpathname(path), strerror(errno));
+ errno = EACCES;
+ return -1;
+ }
+ /* it's not new anymore */
+ ad->ad_mdp->adf_flags &= ~( O_TRUNC | O_CREAT );
+ ad->ad_mdp->adf_refcount++;
+ } else {
+ if (adflags & ADFLAGS_RDWR) {
+ /* Fo a RDONLY adouble we just use sys_lgetxattr instead if sys_fgetxattr */
+ LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): opening for base file for meta adouble EA", path);
+ if ((ad_meta_fileno(ad) = open(path, oflags)) == -1)
+ goto error;
+ ad->ad_mdp->adf_flags = oflags;
+ }
+ ad->ad_mdp->adf_refcount = 1;
+ }
+
+ /* Read the adouble header in and parse it.*/
+ if (ad->ad_ops->ad_header_read(path, ad, NULL) != 0) {
+ LOG(log_error, logtype_default, "ad_open_hf_ea: no EA adouble");
+
+ if (!(adflags & ADFLAGS_CREATE))
+ goto error;
+
+ LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): creating metadata EA", path);
+
+ /* It doesnt exist, EPERM or another error */
+ if (!(errno == ENOATTR || errno == ENOENT)) {
+ LOG(log_error, logtype_default, "ad_open_hf_ea: unexpected: %s", strerror(errno));
+ goto error;
+ }
+
+ /* Create one */
+ if (new_ad_header(ad, path, NULL, adflags) < 0) {
+ LOG(log_error, logtype_default, "ad_open_hf_ea: can't create new header: %s",
+ fullpathname(path));
+ goto error;
+ }
+ ad->ad_mdp->adf_flags |= O_CREAT; /* mark as just created */
+ ad_flush(ad);
+ LOG(log_debug, logtype_default, "ad_open_hf_ea(\"%s\"): created metadata EA", path);
+ }
+
+ /* TODO: ad_rlen calculation */
+ ad->ad_rlen = 0;
+
+ return 0;
+
+error:
+ if (ad_meta_fileno(ad) != -1) {
+ close(ad_meta_fileno(ad));
+ ad_meta_fileno(ad) = -1;
+ ad->ad_mdp->adf_refcount = 0;
+ }
+ return ad_error(ad, adflags);
+}
+
+static int ad_open_hf(const char *path, int adflags, int mode, struct adouble *ad)
+{
+ int ret = 0;
+
+ LOG(log_debug, logtype_default, "ad_open_hf(\"%s\", %04o)", path, mode);
+
+ memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+ ad->ad_rlen = 0;
+
+ switch (ad->ad_vers) {
+ case AD_VERSION2:
+ ret = ad_open_hf_v2(path, adflags, mode, ad);
+ break;
+ case AD_VERSION_EA:
+ ret = ad_open_hf_ea(path, adflags, mode, ad);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret != 0) {
+ return ad_error(ad, adflags);
+ }
+ return ret;
+}
+
+
+/*!
+ * Open ressource fork
+ *
+ * Only for adouble:ea, a nullop otherwise because adouble:v2 has the ressource fork as part
+ * of the adouble file which is openend by ADFLAGS_HF.