From 5dc60bdb57128adbadf1d662f22bdb3815f8ef65 Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Sun, 25 Dec 2011 13:34:00 +0100 Subject: [PATCH] Fixes --- etc/afpd/fork.c | 16 +++--- include/atalk/adouble.h | 17 ++++--- libatalk/adouble/ad_open.c | 102 ++++++++++++++++++++++++------------- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c index d3c4b531..4ba0caf9 100644 --- a/etc/afpd/fork.c +++ b/etc/afpd/fork.c @@ -323,7 +323,8 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si ret = AFPERR_NOOBJ; if (access & OPENACC_WR) { /* try opening in read-write mode */ - if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDWR) < 0) { + if (ad_open(ofork->of_ad, upath, + adflags | ADFLAGS_RDWR | ADFLAGS_SETSHRMD) < 0) { switch ( errno ) { case EROFS: ret = AFPERR_VLOCK; @@ -333,14 +334,16 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si case ENOENT: if (fork == OPENFORK_DATA) { /* try to open only the data fork */ - if (ad_open(ofork->of_ad, upath, ADFLAGS_DF | ADFLAGS_RDWR) < 0) { + if (ad_open(ofork->of_ad, upath, + ADFLAGS_DF | ADFLAGS_RDWR | ADFLAGS_SETSHRMD) < 0) { goto openfork_err; } adflags = ADFLAGS_DF; } else { /* here's the deal. we only try to create the resource * fork if the user wants to open it for write acess. */ - if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) + if (ad_open(ofork->of_ad, upath, + adflags | ADFLAGS_RDWR | ADFLAGS_SETSHRMD | ADFLAGS_CREATE, 0666) < 0) goto openfork_err; ofork->of_flags |= AFPFORK_OPEN; } @@ -368,17 +371,18 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si } else { /* try opening in read-only mode */ ret = AFPERR_NOOBJ; - if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDONLY) < 0) { + /* we need w access for setting deny mode fcntl excl lock */ + if (ad_open(ofork->of_ad, upath, adflags | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) { switch ( errno ) { case EROFS: ret = AFPERR_VLOCK; + goto openfork_err; case EACCES: goto openfork_err; - break; case ENOENT: /* see if client asked for a read only data fork */ if (fork == OPENFORK_DATA) { - if (ad_open(ofork->of_ad, upath, ADFLAGS_DF | ADFLAGS_RDONLY) < 0) { + if (ad_open(ofork->of_ad, upath, ADFLAGS_DF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) { goto openfork_err; } adflags = ADFLAGS_DF; diff --git a/include/atalk/adouble.h b/include/atalk/adouble.h index 777cbe80..d4558f5e 100644 --- a/include/atalk/adouble.h +++ b/include/atalk/adouble.h @@ -214,12 +214,17 @@ struct adouble { #define ADFLAGS_DIR (1<<3) #define ADFLAGS_NOHF (1<<4) /* not an error if no ressource fork */ #define ADFLAGS_CHECK_OF (1<<6) /* check for open forks from us and other afpd's */ - -#define ADFLAGS_RDWR (1<<7) /* open read/write */ -#define ADFLAGS_RDONLY (1<<8) /* open read only */ -#define ADFLAGS_CREATE (1<<9) /* create file, open called with O_CREAT */ -#define ADFLAGS_EXCL (1<<10) /* exclusive open, open called with O_EXCL */ -#define ADFLAGS_TRUNC (1<<11) /* truncate, open called with O_TRUNC */ +#define ADFLAGS_SETSHRMD (1<<7) /* setting share mode must be done with excl fcnt lock, + which implies that the file must be openend rw. + If it can't be opened rw (eg EPERM or EROFS) it will + be opened ro and the fcntl locks will be shared, that + at least prevent other users who have rw access to the + file from placing excl locks. */ +#define ADFLAGS_RDWR (1<<8) /* open read/write */ +#define ADFLAGS_RDONLY (1<<9) /* open read only */ +#define ADFLAGS_CREATE (1<<10) /* create file, open called with O_CREAT */ +#define ADFLAGS_EXCL (1<<11) /* exclusive open, open called with O_EXCL */ +#define ADFLAGS_TRUNC (1<<12) /* truncate, open called with O_TRUNC */ #define ADVOL_NODEV (1 << 0) #define ADVOL_CACHE (1 << 1) diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index f039da9f..d4011a9c 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -575,7 +575,9 @@ static int ad2openflags(int adflags) if (adflags & ADFLAGS_RDWR) oflags |= O_RDWR; - if (adflags & ADFLAGS_RDONLY) + if ((adflags & ADFLAGS_RDONLY) && (adflags & ADFLAGS_SETSHRMD)) + oflags |= O_RDWR; + else oflags |= O_RDONLY; if (adflags & ADFLAGS_CREATE) oflags |= O_CREAT; @@ -624,16 +626,29 @@ static int ad_open_df(const char *path, int adflags, mode_t mode, struct adouble ad->ad_data_fork.adf_fd = open(path, oflags, admode); if (ad->ad_data_fork.adf_fd == -1) { - if (errno != OPEN_NOFOLLOW_ERRNO) + switch (errno) { + 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; - - 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); + 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; } - ad->ad_data_fork.adf_syml[lsz] = 0; - ad->ad_data_fork.adf_fd = -2; /* -2 means its a symlink */ } if (!st_invalid) @@ -674,43 +689,58 @@ static int ad_open_hf_v2(const char *path, int adflags, mode_t mode, struct adou 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 < 0 ) { - if (!(errno == ENOENT && (oflags & O_CREAT))) - return ad_error(ad, adflags); - /* - * We're expecting to create a new adouble header file here - * if ((oflags & O_CREAT) ==> (oflags & O_RDWR) - */ - 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) { - if (ad->ad_ops->ad_mkrf( ad_p) < 0) { - return ad_error(ad, adflags); + if (ad->ad_mdp->adf_fd < 0) { + switch (errno) { + 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; + 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 + * if ((oflags & O_CREAT) ==> (oflags & O_RDWR) + */ + 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) { + 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); + /* 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); + ad->ad_mdp->adf_flags = oflags; + /* just created, set owner if admin owner (root) */ + if (!st_invalid) + ad_chown(ad_p, &st_dir); + break; + } } else { ad->ad_mdp->adf_flags = nocreatflags; if (fstat(ad->ad_mdp->adf_fd, &st_meta) == 0 && st_meta.st_size == 0) { @@ -1162,7 +1192,7 @@ int ad_metadata(const char *name, int flags, struct adouble *adp) oflags = (flags & ADFLAGS_DIR) | ADFLAGS_HF | ADFLAGS_RDONLY; - if ((ret = ad_open(adp, name, oflags)) < 0 && errno == EACCES) { + if ((ret = ad_open(adp, name, oflags | ADFLAGS_SETSHRMD)) < 0 && errno == EACCES) { uid = geteuid(); if (seteuid(0)) { LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno)); -- 2.39.2