static int getforkparams(const AFPObj *obj, struct ofork *ofork, uint16_t bitmap, char *buf, size_t *buflen) { struct path path; struct stat *st; struct adouble *adp; struct dir *dir; struct vol *vol; /* can only get the length of the opened fork */ if ( ( (bitmap & ((1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN))) && (ofork->of_flags & AFPFORK_RSRC)) || ( (bitmap & ((1<<FILPBIT_RFLEN) | (1<<FILPBIT_EXTRFLEN))) && (ofork->of_flags & AFPFORK_DATA))) { return( AFPERR_BITMAP ); } if (! AD_META_OPEN(ofork->of_ad)) { adp = NULL; } else { adp = ofork->of_ad; } vol = ofork->of_vol; dir = dirlookup(vol, ofork->of_did); if (NULL == (path.m_name = utompath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) { return( AFPERR_MISC ); } path.u_name = of_name(ofork); path.id = 0; st = &path.st; if ( bitmap & ( (1<<FILPBIT_DFLEN) | (1<<FILPBIT_EXTDFLEN) | (1<<FILPBIT_FNUM) | (1 << FILPBIT_CDATE) | (1 << FILPBIT_MDATE) | (1 << FILPBIT_BDATE))) { if ( ad_data_fileno( ofork->of_ad ) <= 0 ) { /* 0 is for symlink */ if (movecwd(vol, dir) < 0) return( AFPERR_NOOBJ ); if ( lstat( path.u_name, st ) < 0 ) return( AFPERR_NOOBJ ); } else { if ( fstat( ad_data_fileno( ofork->of_ad ), st ) < 0 ) { return( AFPERR_BITMAP ); } } } return getmetadata(obj, vol, bitmap, &path, dir, buf, buflen, adp ); }
/* * build mac. path (backwards) by traversing the directory tree * * The old way: dir and path refer to an app, path is a mac format * pathname. makemacpath() builds something that looks like a cname, * but uses upaths instead of mac format paths. * * The new way: dir and path refer to an app, path is a mac format * pathname. makemacpath() builds a cname. (zero is a path separator * and it's not \0 terminated). * * See afp_getappl() for the backward compatiblity code. */ static char * makemacpath(const struct vol *vol, char *mpath, int mpathlen, struct dir *dir, char *path) { char *p; p = mpath + mpathlen; p -= strlen( path ); memcpy( p, path, strlen( path )); while ( dir->d_did != DIRDID_ROOT ) { p -= blength(dir->d_m_name) + 1; if (p < mpath) { /* FIXME: pathname too long */ return NULL; } memcpy(p, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1); if ((dir = dirlookup(vol, dir->d_pdid)) == NULL) return NULL; } return( p ); #if 0 char buffer[12 + MAXPATHLEN + 1]; int buflen = 12 + MAXPATHLEN + 1; char *ret = mpath; char *path = name; char *uname = NULL; struct bstrList *pathlist = NULL; cnid_t cnid = dir->d_pdid; /* Create list for path elements, request 16 list elements for now*/ if ((pathlist = bstListCreateMin(16)) == NULL) { LOG(log_error, logtype_afpd, "makemacpath: OOM: %s", strerror(errno)); return NULL; } while ( cnid != DIRDID_ROOT ) { /* construct path, copy already found uname to path element list*/ if ((bstrListPush(pathlist, bfromcstr(path))) != BSTR_OK) { afp_errno = AFPERR_MISC; ret = NULL; goto exit; } /* next part */ if ((uname = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { afp_errno = AFPERR_NOOBJ; ret = NULL; goto exit; } if ((path = utompath(vol, uname, cnid, utf8_encoding())) == NULL) { afp_errno = AFPERR_MISC; ret = NULL; goto exit; } } exit: if (pathlist) bstrListDestroy(pathlist); return(ret); #endif }
/*! * This function performs a CNID db search * * Uses globals c1, c2, the search criteria * * @param vol (r) volume we are searching on ... * @param dir (rw) directory we are starting from ... * @param uname (r) UNIX name of object to search * @param rmatches (r) maximum number of matches we can return * @param pos (r) position we've stopped recently * @param rbuf (w) output buffer * @param nrecs (w) number of matches * @param rsize (w) length of data written to output buffer * @param ext (r) extended search flag */ static int catsearch_db(struct vol *vol, struct dir *dir, const char *uname, int rmatches, uint32_t *pos, char *rbuf, uint32_t *nrecs, int *rsize, int ext) { static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)]; static uint32_t cur_pos; static int num_matches; int ccr ,r; int result = AFP_OK; struct path path; char *rrbuf = rbuf; char buffer[MAXPATHLEN +2]; uint16_t flags = CONV_TOLOWER; LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u, name: %s}", *pos, cur_pos, uname); if (*pos != 0 && *pos != cur_pos) { result = AFPERR_CATCHNG; goto catsearch_end; } if (cur_pos == 0 || *pos == 0) { if (convert_charset(vol->v_volcharset, vol->v_volcharset, vol->v_maccharset, uname, strlen(uname), buffer, MAXPATHLEN, &flags) == (size_t)-1) { LOG(log_error, logtype_afpd, "catsearch_db: conversion error"); result = AFPERR_MISC; goto catsearch_end; } LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer); if ((num_matches = cnid_find(vol->v_cdb, buffer, strlen(uname), resbuf, sizeof(resbuf))) == -1) { result = AFPERR_MISC; goto catsearch_end; } } while (cur_pos < num_matches) { char *name; cnid_t cnid, did; char resolvebuf[12 + MAXPATHLEN + 1]; struct dir *dir; /* Next CNID to process from buffer */ memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t)); did = cnid; if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL) goto next; LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}", cur_pos, name, ntohl(cnid)); if ((dir = dirlookup(vol, did)) == NULL) goto next; if (movecwd(vol, dir) < 0 ) goto next; memset(&path, 0, sizeof(path)); path.u_name = name; path.m_name = utompath(vol, name, cnid, utf8_encoding()); if (of_stat(vol, &path) != 0) { switch (errno) { case EACCES: case ELOOP: goto next; case ENOENT: default: result = AFPERR_MISC; goto catsearch_end; } } /* For files path.d_dir is the parent dir, for dirs its the dir itself */ if (S_ISDIR(path.st.st_mode)) if ((dir = dirlookup(vol, cnid)) == NULL) goto next; path.d_dir = dir; LOG(log_maxdebug, logtype_afpd,"catsearch_db: dir: %s, cwd: %s, name: %s", cfrombstr(dir->d_fullpath), getcwdpath(), path.u_name); /* At last we can check the search criteria */ ccr = crit_check(vol, &path); if ((ccr & 1)) { LOG(log_debug, logtype_afpd,"catsearch_db: match: %s/%s", getcwdpath(), path.u_name); /* bit 1 means that criteria has been met */ r = rslt_add(vol, &path, &rrbuf, ext); if (r == 0) { result = AFPERR_MISC; goto catsearch_end; } *nrecs += r; /* Number of matches limit */ if (--rmatches == 0) goto catsearch_pause; /* Block size limit */ if (rrbuf - rbuf >= 448) goto catsearch_pause; } next: cur_pos++; } /* while */ /* finished */ result = AFPERR_EOF; cur_pos = 0; goto catsearch_end; catsearch_pause: *pos = cur_pos; catsearch_end: /* Exiting catsearch: error condition */ *rsize = rrbuf - rbuf; LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u}", *pos, cur_pos); return result; }
/* uname - UNIX name * fname - our fname (translated to UNIX) * cidx - index in directory stack */ static int crit_check(struct vol *vol, struct path *path) { int result = 0; u_int16_t attr, flags = CONV_PRECOMPOSE; struct finderinfo *finfo = NULL, finderinfo; struct adouble *adp = NULL; time_t c_date, b_date; u_int32_t ac_date, ab_date; static char convbuf[514]; /* for convert_charset dest_len parameter +2 */ size_t len; int islnk; islnk=S_ISLNK(path->st.st_mode); if (S_ISDIR(path->st.st_mode)) { if (!c1.dbitmap) return 0; } else { if (!c1.fbitmap) return 0; /* compute the Mac name * first try without id (it's slow to find it) * An other option would be to call get_id in utompath but * we need to pass parent dir */ if (!(path->m_name = utompath(vol, path->u_name, 0 , utf8_encoding()) )) { /*retry with the right id */ cnid_t id; adp = adl_lkup(vol, path, adp); id = get_id(vol, adp, &path->st, path->d_dir->d_did, path->u_name, strlen(path->u_name)); if (!id) { /* FIXME */ return 0; } /* save the id for getfilparm */ path->id = id; if (!(path->m_name = utompath(vol, path->u_name, id , utf8_encoding()))) { return 0; } } } /* Kind of optimization: * -- first check things we've already have - filename * -- last check things we get from ad_open() * FIXME strmcp strstr (icase) */ /* Check for filename */ if ((c1.rbitmap & (1<<DIRPBIT_LNAME))) { if ( (size_t)(-1) == (len = convert_string(vol->v_maccharset, CH_UCS2, path->m_name, -1, convbuf, 512)) ) goto crit_check_ret; if ((c1.rbitmap & (1<<CATPBIT_PARTIAL))) { if (strcasestr_w( (ucs2_t*) convbuf, (ucs2_t*) c1.lname) == NULL) goto crit_check_ret; } else if (strcasecmp_w((ucs2_t*) convbuf, (ucs2_t*) c1.lname) != 0) goto crit_check_ret; } if ((c1.rbitmap & (1<<FILPBIT_PDINFO))) { if ( (size_t)(-1) == (len = convert_charset( CH_UTF8_MAC, CH_UCS2, CH_UTF8, path->m_name, strlen(path->m_name), convbuf, 512, &flags))) { goto crit_check_ret; } if (c1.rbitmap & (1<<CATPBIT_PARTIAL)) { if (strcasestr_w((ucs2_t *) convbuf, (ucs2_t*)c1.utf8name) == NULL) goto crit_check_ret; } else if (strcasecmp_w((ucs2_t *)convbuf, (ucs2_t*)c1.utf8name) != 0) goto crit_check_ret; } /* FIXME */ if ((unsigned)c2.mdate > 0x7fffffff) c2.mdate = 0x7fffffff; if ((unsigned)c2.cdate > 0x7fffffff) c2.cdate = 0x7fffffff; if ((unsigned)c2.bdate > 0x7fffffff) c2.bdate = 0x7fffffff; /* Check for modification date */ if ((c1.rbitmap & (1<<DIRPBIT_MDATE))) { if (path->st.st_mtime < c1.mdate || path->st.st_mtime > c2.mdate) goto crit_check_ret; } /* Check for creation date... */ if ((c1.rbitmap & (1<<DIRPBIT_CDATE))) { c_date = path->st.st_mtime; adp = adl_lkup(vol, path, adp); if (adp && ad_getdate(adp, AD_DATE_CREATE, &ac_date) >= 0) c_date = AD_DATE_TO_UNIX(ac_date); if (c_date < c1.cdate || c_date > c2.cdate) goto crit_check_ret; } /* Check for backup date... */ if ((c1.rbitmap & (1<<DIRPBIT_BDATE))) { b_date = path->st.st_mtime; adp = adl_lkup(vol, path, adp); if (adp && ad_getdate(adp, AD_DATE_BACKUP, &ab_date) >= 0) b_date = AD_DATE_TO_UNIX(ab_date); if (b_date < c1.bdate || b_date > c2.bdate) goto crit_check_ret; } /* Check attributes */ if ((c1.rbitmap & (1<<DIRPBIT_ATTR)) && c2.attr != 0) { if ((adp = adl_lkup(vol, path, adp))) { ad_getattr(adp, &attr); if ((attr & c2.attr) != c1.attr) goto crit_check_ret; } else goto crit_check_ret; } /* Check file type ID */ if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0) { finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); if (finfo->f_type != c1.finfo.f_type) goto crit_check_ret; } /* Check creator ID */ if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0) { if (!finfo) { finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); } if (finfo->creator != c1.finfo.creator) goto crit_check_ret; } /* Check finder info attributes */ if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0) { if (!finfo) { finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); } if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs) goto crit_check_ret; } /* Check label */ if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0) { if (!finfo) { finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); } if ((finfo->label & c2.finfo.label) != c1.finfo.label) goto crit_check_ret; } /* FIXME: Attributes check ! */ /* All criteria are met. */ result |= 1; crit_check_ret: if (adp != NULL) ad_close_metadata(adp); return result; }
static int copy(const char *path, const struct stat *statp, int tflag, struct FTW *ftw) { static int base = 0; struct stat to_stat; int dne; size_t nlen; const char *p; char *target_mid; if (alarmed) return -1; /* This currently doesn't work with "." */ if (strcmp(path, ".") == 0) { ERROR("\".\" not supported"); } const char *dir = strrchr(path, '/'); if (dir == NULL) dir = path; else dir++; if (check_netatalk_dirs(dir) != NULL) return FTW_SKIP_SUBTREE; /* * If we are in case (2) above, we need to append the * source name to the target name. */ if (type != FILE_TO_FILE) { /* * Need to remember the roots of traversals to create * correct pathnames. If there's a directory being * copied to a non-existent directory, e.g. * cp -R a/dir noexist * the resulting path name should be noexist/foo, not * noexist/dir/foo (where foo is a file in dir), which * is the case where the target exists. * * Also, check for "..". This is for correct path * concatenation for paths ending in "..", e.g. * cp -R .. /tmp * Paths ending in ".." are changed to ".". This is * tricky, but seems the easiest way to fix the problem. * * XXX * Since the first level MUST be FTS_ROOTLEVEL, base * is always initialized. */ if (ftw->level == 0) { if (type != DIR_TO_DNE) { base = ftw->base; if (strcmp(&path[base], "..") == 0) base += 1; } else base = strlen(path); } p = &path[base]; nlen = strlen(path) - base; target_mid = to.target_end; if (*p != '/' && target_mid[-1] != '/') *target_mid++ = '/'; *target_mid = 0; if (target_mid - to.p_path + nlen >= PATH_MAX) { SLOG("%s%s: name too long (not copied)", to.p_path, p); badcp = rval = 1; return 0; } (void)strncat(target_mid, p, nlen); to.p_end = target_mid + nlen; *to.p_end = 0; STRIP_TRAILING_SLASH(to); } /* Not an error but need to remember it happened */ if (stat(to.p_path, &to_stat) == -1) dne = 1; else { if (to_stat.st_dev == statp->st_dev && to_stat.st_ino == statp->st_ino) { SLOG("%s and %s are identical (not copied).", to.p_path, path); badcp = rval = 1; if (S_ISDIR(statp->st_mode)) /* without using glibc extension FTW_ACTIONRETVAL cant handle this */ return FTW_SKIP_SUBTREE; return 0; } if (!S_ISDIR(statp->st_mode) && S_ISDIR(to_stat.st_mode)) { SLOG("cannot overwrite directory %s with " "non-directory %s", to.p_path, path); badcp = rval = 1; return 0; } dne = 0; } /* Convert basename to appropiate volume encoding */ if (dvolume.volinfo.v_path) { if ((convert_dots_encoding(&svolume, &dvolume, to.p_path, MAXPATHLEN)) == -1) { SLOG("Error converting name for %s", to.p_path); badcp = rval = 1; return -1; } } switch (statp->st_mode & S_IFMT) { case S_IFLNK: if (ftw_copy_link(ftw, path, statp, !dne)) badcp = rval = 1; break; case S_IFDIR: if (!Rflag) { SLOG("%s is a directory", path); badcp = rval = 1; return -1; } /* * If the directory doesn't exist, create the new * one with the from file mode plus owner RWX bits, * modified by the umask. Trade-off between being * able to write the directory (if from directory is * 555) and not causing a permissions race. If the * umask blocks owner writes, we fail.. */ if (dne) { if (mkdir(to.p_path, statp->st_mode | S_IRWXU) < 0) ERROR("mkdir: %s: %s", to.p_path, strerror(errno)); } else if (!S_ISDIR(to_stat.st_mode)) { errno = ENOTDIR; ERROR("%s", to.p_path); } /* Create ad dir and copy ".Parent" */ if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) { /* Create ".AppleDouble" dir */ mode_t omask = umask(0); bstring addir = bfromcstr(to.p_path); bcatcstr(addir, "/.AppleDouble"); mkdir(cfrombstr(addir), 02777); bdestroy(addir); if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) { /* copy ".Parent" file */ if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) { SLOG("Error copying adouble for %s -> %s", path, to.p_path); badcp = rval = 1; break; } } /* Get CNID of Parent and add new childir to CNID database */ ppdid = pdid; if ((did = cnid_for_path(&dvolume, to.p_path, &pdid)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", to.p_path); badcp = rval = 1; return -1; } struct adouble ad; struct stat st; if (lstat(to.p_path, &st) != 0) { badcp = rval = 1; break; } ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options); if (ad_open_metadata(to.p_path, ADFLAGS_DIR, O_RDWR | O_CREAT, &ad) != 0) { ERROR("Error opening adouble for: %s", to.p_path); } ad_setid( &ad, st.st_dev, st.st_ino, did, pdid, dvolume.db_stamp); ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path))); ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime); ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime); ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime); ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START); ad_flush(&ad); ad_close_metadata(&ad); umask(omask); } if (pflag) { if (setfile(statp, -1)) rval = 1; #if 0 if (preserve_dir_acls(statp, curr->fts_accpath, to.p_path) != 0) rval = 1; #endif } break; case S_IFBLK: case S_IFCHR: SLOG("%s is a device file (not copied).", path); break; case S_IFSOCK: SLOG("%s is a socket (not copied).", path); break; case S_IFIFO: SLOG("%s is a FIFO (not copied).", path); break; default: if (ftw_copy_file(ftw, path, statp, dne)) badcp = rval = 1; if (dvolume.volinfo.v_path && dvolume.volinfo.v_adouble == AD_VERSION2) { mode_t omask = umask(0); if (svolume.volinfo.v_path && svolume.volinfo.v_adouble == AD_VERSION2) { /* copy ad-file */ if (dvolume.volume.vfs->vfs_copyfile(&dvolume.volume, -1, path, to.p_path)) { SLOG("Error copying adouble for %s -> %s", path, to.p_path); badcp = rval = 1; break; } } /* Get CNID of Parent and add new childir to CNID database */ pdid = did; cnid_t cnid; if ((cnid = cnid_for_path(&dvolume, to.p_path, &did)) == CNID_INVALID) { SLOG("Error resolving CNID for %s", to.p_path); badcp = rval = 1; return -1; } struct adouble ad; struct stat st; if (lstat(to.p_path, &st) != 0) { badcp = rval = 1; break; } ad_init(&ad, dvolume.volinfo.v_adouble, dvolume.volinfo.v_ad_options); if (ad_open_metadata(to.p_path, 0, O_RDWR | O_CREAT, &ad) != 0) { ERROR("Error opening adouble for: %s", to.p_path); } ad_setid( &ad, st.st_dev, st.st_ino, cnid, did, dvolume.db_stamp); ad_setname(&ad, utompath(&dvolume.volinfo, basename(to.p_path))); ad_setdate(&ad, AD_DATE_CREATE | AD_DATE_UNIX, st.st_mtime); ad_setdate(&ad, AD_DATE_MODIFY | AD_DATE_UNIX, st.st_mtime); ad_setdate(&ad, AD_DATE_ACCESS | AD_DATE_UNIX, st.st_mtime); ad_setdate(&ad, AD_DATE_BACKUP, AD_DATE_START); ad_flush(&ad); ad_close_metadata(&ad); umask(omask); } break; } if (vflag && !badcp) (void)printf("%s -> %s\n", path, to.p_path); return 0; }
/* ------------------------------------------------------- */ static char * private_demangle(const struct vol *vol, char *mfilename, cnid_t did, cnid_t *osx) { char *t; char *u_name; uint32_t id, file_id; static char buffer[12 + MAXPATHLEN + 1]; int len = 12 + MAXPATHLEN + 1; struct dir *dir; size_t prefix; id = file_id = 0; t = strchr(mfilename, MANGLE_CHAR); if (t == NULL) { return mfilename; } prefix = t - mfilename; /* FIXME * is prefix == 0 a valid mangled filename ? */ /* may be a mangled filename */ t++; if (*t == '0') { /* can't start with a 0 */ return mfilename; } while(isuxdigit(*t)) { id = (id *16) + hextoint(*t); t++; } if ((*t != 0 && *t != '.') || strlen(t) > MAX_EXT_LENGTH || id < 17) { return mfilename; } file_id = id = htonl(id); if (osx) { *osx = id; } /* is it a dir?, there's a conflict with pre OSX 'trash #2' */ if ((dir = dirlookup(vol, id))) { if (dir->d_pdid != did) { /* not in the same folder, there's a race with outdate cache * but we have to live with it, hopefully client will recover */ return mfilename; } if (!osx) { /* it's not from cname so mfilename and dir must be the same */ if (strcmp(cfrombstr(dir->d_m_name), mfilename) == 0) { return cfrombstr(dir->d_u_name); } } else { return demangle_checks(vol, cfrombstr(dir->d_u_name), mfilename, prefix, t); } } else if (NULL != (u_name = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) { if (id != did) { return mfilename; } if (!osx) { /* convert back to mac name and check it's the same */ t = utompath(vol, u_name, file_id, utf8_encoding(vol->v_obj)); if (!strcmp(t, mfilename)) { return u_name; } } else { return demangle_checks (vol, u_name, mfilename, prefix, t); } } return mfilename; }