X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=libatalk%2Fadouble%2Fad_open.c;h=b2b291b659a2e1d3e7666fa53d5ef5123d490f60;hb=b362b6f7b22b6e4e9e74760989f389149677917b;hp=5f7ca0f1864762fcaf9d9987052baeeace4d1984;hpb=56ab9d669ae6d0063d71f1c3ef4f2965f1b7e0bf;p=netatalk.git diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index 5f7ca0f1..b2b291b6 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -1,6 +1,4 @@ /* - * $Id: ad_open.c,v 1.57 2009-11-07 01:18:50 didg Exp $ - * * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) * Copyright (c) 1990,1991 Regents of The University of Michigan. * All Rights Reserved. @@ -23,8 +21,14 @@ * +1-313-763-0525 * netatalk@itd.umich.edu * - * NOTE: I don't use inline because a good compiler should be - * able to optimize all the static below. Didier + */ + +/*! + * @file + * Part of Netatalk's AppleDouble implementatation + * @note We don't use inlines because a good compiler should be + * able to optimize all the static funcs below. + * @sa include/atalk/adouble.h */ #ifdef HAVE_CONFIG_H @@ -277,6 +281,7 @@ static int ad_update(struct adouble *ad, const char *path) /* last place for failure. */ if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) { + munmap(buf, st.st_size + shiftdata); goto bail_lock; } @@ -1009,7 +1014,7 @@ int ad_stat(const char *path, struct stat *stbuf) return -1; } - return stat( p, stbuf ); + return stat(p, stbuf); } /* ---------------- @@ -1031,7 +1036,7 @@ static int ad_chown(const char *path, struct stat *stbuf) if (default_uid != (uid_t)-1) { /* we are root (admin) */ id = (default_uid)?default_uid:stbuf->st_uid; - ret = chown( path, id, stbuf->st_gid ); + ret = chown(path, id, stbuf->st_gid); } #endif return ret; @@ -1074,9 +1079,8 @@ ad_mkdir( const char *path, int mode) int st_invalid; struct stat stbuf; -#ifdef DEBUG - LOG(log_debug9, logtype_default, "ad_mkdir: Creating directory with mode %d", mode); -#endif + LOG(log_debug, logtype_default, "ad_mkdir(\"%s\", %04o) {cwd: \"%s\"}", + path, mode, getcwdpath()); st_invalid = ad_mode_st(path, &mode, &stbuf); ret = mkdir( path, mode ); @@ -1178,23 +1182,70 @@ static struct adouble_fops ad_adouble = { void ad_init(struct adouble *ad, int flags, int options) { - memset( ad, 0, sizeof( struct adouble ) ); + ad->ad_inited = 0; ad->ad_flags = flags; if (flags == AD_VERSION2_OSX) { ad->ad_ops = &ad_osx; + ad->ad_md = &ad->ad_resource_fork; } else if (flags == AD_VERSION1_SFM) { ad->ad_ops = &ad_sfm; + ad->ad_md = &ad->ad_metadata_fork; } else { ad->ad_ops = &ad_adouble; + ad->ad_md = &ad->ad_resource_fork; } ad->ad_options = options; + + ad_data_fileno(ad) = -1; + ad_reso_fileno(ad) = -1; + ad_meta_fileno(ad) = -1; + /* following can be read even if there's no + * meda data. + */ + memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); + ad->ad_rlen = 0; } -/* ------------------- - * It's not possible to open the header file O_RDONLY -- the read - * will fail and return an error. this refcounts things now. +/*! + * Open data-, metadata(header)- or ressource fork + * + * You must call ad_init() before ad_open, usually you'll just call it like this: \n + * @code + * struct adoube ad; + * ad_init(&ad, vol->v_adouble, vol->v_ad_options); + * @endcode + * + * @param path Path to file or directory + * + * @param adflags ADFLAGS_DF: open data file/fork\n + * ADFLAGS_HF: open header (metadata) file\n + * ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n + * ADFLAGS_CREATE: indicate creation\n + * ADFLAGS_NOHF: it's not an error if header file couldn't be created\n + * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n + * ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n + * ADFLAGS_RDONLY: open read only\n + * ADFLAGS_OPENFORKS: check for open forks from other processes\n + * ADFLAGS_MD: alias for ADFLAGS_HF\n + * ADFLAGS_V1COMPAT: obsolete + * + * @param oflags flags passed through to open syscall: \n + * O_RDONLY: *** FIXME *** \n + * O_RDWR: *** FIXME *** \n + * O_CREAT: create fork\n + * O_EXCL: fail if exists with O_CREAT + * + * @param mode passed to open with O_CREAT + * + * @param ad pointer to struct adouble + * + * @returns 0 on success + * + * @note It's not possible to open the header file O_RDONLY -- the read + * will fail and return an error. this refcounts things now.\n + * metadata(ressource)-fork only gets created with O_CREAT. */ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble *ad) { @@ -1207,22 +1258,13 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble int open_df = 0; if (ad->ad_inited != AD_INITED) { - ad_data_fileno(ad) = -1; - ad_reso_fileno(ad) = -1; - adf_lock_init(&ad->ad_data_fork); - adf_lock_init(&ad->ad_resource_fork); - if (ad->ad_flags != AD_VERSION1_SFM) { - ad->ad_md = &ad->ad_resource_fork; - } - else { - adf_lock_init(&ad->ad_metadata_fork); - ad->ad_md = &ad->ad_metadata_fork; - ad_meta_fileno(ad) = -1; - } ad->ad_inited = AD_INITED; ad->ad_refcount = 1; ad->ad_open_forks = 0; ad->ad_adflags = adflags; + ad->ad_resource_fork.adf_refcount = 0; + ad->ad_data_fork.adf_refcount = 0; + ad->ad_data_fork.adf_syml=0; } else { ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0); @@ -1241,14 +1283,29 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble admode = mode; } } - ad->ad_data_fork.adf_fd =open( path, hoflags, admode ); - if (ad->ad_data_fork.adf_fd < 0 ) { + + ad->ad_data_fork.adf_fd = open(path, hoflags | ad_get_syml_opt(ad), admode); + + if (ad->ad_data_fork.adf_fd == -1) { if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { hoflags = oflags; - ad->ad_data_fork.adf_fd = open( path, hoflags, admode ); + ad->ad_data_fork.adf_fd = open( path, hoflags | ad_get_syml_opt(ad), admode ); + } + if (ad->ad_data_fork.adf_fd == -1 && errno == OPEN_NOFOLLOW_ERRNO) { + int lsz; + + ad->ad_data_fork.adf_syml = malloc(MAXPATHLEN+1); + lsz = readlink(path, ad->ad_data_fork.adf_syml, MAXPATHLEN); + if (lsz <= 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 */ } } - if ( ad->ad_data_fork.adf_fd < 0) + + if ( ad->ad_data_fork.adf_fd == -1 ) return -1; AD_SET(ad->ad_data_fork.adf_off); @@ -1257,6 +1314,7 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble /* just created, set owner if admin (root) */ ad_chown(path, &st_dir); } + adf_lock_init(&ad->ad_data_fork); } else { /* the file is already open... but */ @@ -1294,21 +1352,25 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble return -1; } ad_refresh(ad); + /* it's not new anymore */ + ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT ); ad->ad_md->adf_refcount++; goto sfm; } + memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); + ad->ad_rlen = 0; ad_p = ad->ad_ops->ad_path( path, adflags ); hoflags = oflags & ~(O_CREAT | O_EXCL); if (!(adflags & ADFLAGS_RDONLY)) { hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; } - ad->ad_md->adf_fd = open( ad_p, hoflags, 0 ); + ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0); if (ad->ad_md->adf_fd < 0 ) { if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { hoflags = oflags & ~(O_CREAT | O_EXCL); - ad->ad_md->adf_fd = open( ad_p, hoflags, 0 ); + ad->ad_md->adf_fd = open(ad_p, hoflags | ad_get_syml_opt(ad), 0); } } @@ -1319,6 +1381,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble * here. * if ((oflags & O_CREAT) ==> (oflags & O_RDWR) */ + LOG(log_debug, logtype_default, "ad_open(\"%s\"): {cwd: \"%s\"} creating adouble file", + ad_p, getcwdpath()); admode = mode; errno = 0; st_invalid = ad_mode_st(ad_p, &admode, &st_dir); @@ -1326,8 +1390,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble admode = mode; } admode = ad_hf_mode(admode); - if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) { - if (ad->ad_ops->ad_mkrf( ad_p) < 0) { + if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) { + if (ad->ad_ops->ad_mkrf(ad_p) < 0) { return ad_error(ad, adflags); } admode = mode; @@ -1366,8 +1430,8 @@ int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble } AD_SET(ad->ad_md->adf_off); - memset(ad->ad_eid, 0, sizeof( ad->ad_eid )); - ad->ad_md->adf_refcount++; + ad->ad_md->adf_refcount = 1; + adf_lock_init(ad->ad_md); if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) { /* * This is a new adouble header file. Initialize the structure, @@ -1422,8 +1486,6 @@ sfm: ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF ); - hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; - ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode ); admode = mode; st_invalid = ad_mode_st(ad_p, &admode, &st_dir); @@ -1431,6 +1493,9 @@ sfm: admode = mode; } + hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; + ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode ); + if (ad->ad_resource_fork.adf_fd < 0 ) { if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { hoflags = oflags; @@ -1445,7 +1510,7 @@ sfm: errno = err; return -1; } - + adf_lock_init(&ad->ad_resource_fork); AD_SET(ad->ad_resource_fork.adf_off); ad->ad_resource_fork.adf_flags = hoflags; if ((oflags & O_CREAT) && !st_invalid) { @@ -1458,18 +1523,36 @@ sfm: return 0 ; } -/* ----------------------------------- - * return only metadata but try very hard +/*! + * @brief open metadata, possibly as root + * + * Return only metadata but try very hard ie at first try as user, then try as root. + * + * @param name name of file/dir + * @param flags ADFLAGS_DIR: name is a directory \n + * ADFLAGS_CREATE: force creation of header file, but only as user, not as root\n + * ADFLAGS_OPENFORKS: test if name is open by another afpd process + * + * @param adp pointer to struct adouble + * + * @note caller MUST pass ADFLAGS_DIR for directories. Whether ADFLAGS_CREATE really creates + * a adouble file depends on various other volume options, eg. ADVOL_CACHE */ int ad_metadata(const char *name, int flags, struct adouble *adp) { uid_t uid; - int ret, err; - int dir = flags & ADFLAGS_DIR; + int ret, err, dir; + int create = O_RDONLY; + + dir = flags & ADFLAGS_DIR; - /* Open with O_CREAT, thus enumarating a dir will create missing adouble files, see: */ - /* http://marc.info/?l=netatalk-devel&m=124039156832408&w=2 */ - if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | O_CREAT, 0666, adp)) < 0 && errno == EACCES) { + /* Check if we shall call ad_open with O_CREAT */ + if ( (adp->ad_options & ADVOL_CACHE) + && ! (adp->ad_options & ADVOL_NOADOUBLE) + && (flags & ADFLAGS_CREATE) ) { + create = O_CREAT | O_RDWR; + } + if ((ret = ad_open(name, ADFLAGS_HF | dir, create, 0666, adp)) < 0 && errno == EACCES) { uid = geteuid(); if (seteuid(0)) { LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno)); @@ -1498,6 +1581,41 @@ int ad_metadata(const char *name, int flags, struct adouble *adp) return ret; } +/* + * @brief openat like wrapper for ad_metadata + */ +int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp) +{ + int ret = 0; + int cwdfd = -1; + + if (dirfd != -1) { + if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) { + ret = -1; + goto exit; + } + } + + if (ad_metadata(name, flags, adp) < 0) { + ret = -1; + goto exit; + } + + if (dirfd != -1) { + if (fchdir(cwdfd) != 0) { + LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting"); + exit(EXITERR_SYS); + } + } + +exit: + if (cwdfd != -1) + close(cwdfd); + + return ret; + +} + /* ----------------------------------- */ static int new_rfork(const char *path, struct adouble *ad, int adflags) { @@ -1555,7 +1673,7 @@ static int new_rfork(const char *path, struct adouble *ad, int adflags) memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); } - if (stat(path, &st) < 0) { + if (ostat(path, &st, ad_get_syml_opt(ad)) < 0) { return -1; } @@ -1577,3 +1695,39 @@ int ad_refresh(struct adouble *ad) return ad->ad_ops->ad_header_read(ad, NULL); } + +int ad_openat(int dirfd, /* dir fd openat like */ + const char *path, + int adflags, + int oflags, + int mode, + struct adouble *ad) +{ + int ret = 0; + int cwdfd = -1; + + if (dirfd != -1) { + if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) { + ret = -1; + goto exit; + } + } + + if (ad_open(path, adflags, oflags, mode, ad) < 0) { + ret = -1; + goto exit; + } + + if (dirfd != -1) { + if (fchdir(cwdfd) != 0) { + LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting"); + exit(EXITERR_SYS); + } + } + +exit: + if (cwdfd != -1) + close(cwdfd); + + return ret; +}