X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=etc%2Fafpd%2Ffork.c;h=73abb1e9a102e5a5143303a08011b976cdc982f8;hp=319f33f2b727cf0239fc0b1382ca964a71361153;hb=42a0a094b72577aee3fee7f186da52dc83db001d;hpb=fbd6b007f0121c117e86d02efa0a88e1e5ff6676 diff --git a/etc/afpd/fork.c b/etc/afpd/fork.c index 319f33f2..73abb1e9 100644 --- a/etc/afpd/fork.c +++ b/etc/afpd/fork.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -65,10 +66,10 @@ static int getforkparams(const AFPObj *obj, struct ofork *ofork, uint16_t bitmap 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)))) { + if (NULL == (path.u_name = mtoupath(vol, of_name(ofork), dir->d_did, utf8_encoding(obj)))) { return( AFPERR_MISC ); } - path.u_name = of_name(ofork); + path.m_name = of_name(ofork); path.id = 0; st = &path.st; if ( bitmap & ( (1<options.flags & OPTION_SHARE_RESERV) { + shmd.f_access = (access & OPENACC_RD ? F_RDACC : 0) | (access & OPENACC_WR ? F_WRACC : 0); + if (shmd.f_access == 0) + /* we must give an access mode, otherwise fcntl will complain */ + shmd.f_access = F_RDACC; + shmd.f_deny = (access & OPENACC_DRD ? F_RDDNY : F_NODNY) | (access & OPENACC_DWR) ? F_WRDNY : 0; + shmd.f_id = ofrefnum; + + int fd = (eid == ADEID_DFORK) ? ad_data_fileno(adp) : ad_reso_fileno(adp); + + if (fd != -1 && fd != AD_SYMLINK && fcntl(fd, F_SHARE, &shmd) != 0) { + LOG(log_debug, logtype_afpd, "fork_setmode: fcntl: %s", strerror(errno)); + errno = EACCES; + return -1; + } + } +#endif + if (! (access & (OPENACC_WR | OPENACC_RD | OPENACC_DWR | OPENACC_DRD))) { return ad_lock(adp, eid, ADLOCK_RD | ADLOCK_FILELOCK, AD_FILELOCK_OPEN_NONE, 1, ofrefnum); } @@ -229,7 +251,6 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si struct stat *st; uint16_t bshort; struct path *s_path; - struct stat xxx; ibuf++; fork = *ibuf++; @@ -280,8 +301,11 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si LOG(log_error, logtype_afpd, "afp_openfork(%s): %s", s_path->m_name, strerror(errno)); return AFPERR_PARAM; } - /* FIXME should we check it first ? */ + upath = s_path->u_name; + path = s_path->m_name; + st = &s_path->st; + if (!vol_unix_priv(vol)) { if (check_access(obj, vol, upath, access ) < 0) { return AFPERR_ACCESS; @@ -292,124 +316,105 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si } } - st = &s_path->st; - /* XXX: this probably isn't the best way to do this. the already - open bits should really be set if the fork is opened by any - program, not just this one. however, that's problematic to do - if we can't write lock files somewhere. opened is also passed to - ad_open so that we can keep file locks together. - FIXME: add the fork we are opening? - */ - if ((opened = of_findname(s_path))) { + if ((opened = of_findname(vol, s_path))) { adsame = opened->of_ad; } - if ( fork == OPENFORK_DATA ) { + adflags = ADFLAGS_SETSHRMD; + + if (fork == OPENFORK_DATA) { eid = ADEID_DFORK; - adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF; + adflags |= ADFLAGS_DF; } else { eid = ADEID_RFORK; - adflags = ADFLAGS_RF | ADFLAGS_HF | ADFLAGS_NOHF; - if (!(access & OPENACC_WR)) - adflags |= ADFLAGS_NORF; + adflags |= ADFLAGS_RF; } - path = s_path->m_name; - if (( ofork = of_alloc(vol, curdir, path, &ofrefnum, eid, - adsame, st)) == NULL ) { - return( AFPERR_NFILE ); + if (access & OPENACC_WR) { + adflags |= ADFLAGS_RDWR; + if (fork != OPENFORK_DATA) + /* + * We only try to create the resource + * fork if the user wants to open it for write acess. + */ + adflags |= ADFLAGS_CREATE; + } else { + adflags |= ADFLAGS_RDONLY; } + if ((ofork = of_alloc(vol, curdir, path, &ofrefnum, eid, adsame, st)) == NULL) + return AFPERR_NFILE; + LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\", %s, %s)", fullpathname(s_path->u_name), (fork == OPENFORK_DATA) ? "data" : "reso", !(access & OPENACC_WR) ? "O_RDONLY" : "O_RDWR"); ret = AFPERR_NOOBJ; + + /* First ad_open(), opens data or ressource fork */ + if (ad_open(ofork->of_ad, upath, adflags, 0666) < 0) { + switch (errno) { + case EROFS: + ret = AFPERR_VLOCK; + case EACCES: + goto openfork_err; + case ENOENT: + goto openfork_err; + case EMFILE : + case ENFILE : + ret = AFPERR_NFILE; + goto openfork_err; + case EISDIR : + ret = AFPERR_BADTYPE; + goto openfork_err; + default: + LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) ); + ret = AFPERR_PARAM; + goto openfork_err; + } + } + + /* + * Create metadata if we open rw, otherwise only open existing metadata + */ if (access & OPENACC_WR) { - /* try opening in read-write mode */ - if (ad_open(ofork->of_ad, upath, - adflags | ADFLAGS_RDWR | ADFLAGS_SETSHRMD) < 0) { - switch ( errno ) { - case EROFS: - ret = AFPERR_VLOCK; - case EACCES: - goto openfork_err; - case ENOENT: - if (fork == OPENFORK_DATA) { - /* try to open only the data fork */ - 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_SETSHRMD | ADFLAGS_CREATE, 0666) < 0) - goto openfork_err; - ofork->of_flags |= AFPFORK_META; - } - break; - case EMFILE : - case ENFILE : - ret = AFPERR_NFILE; - goto openfork_err; - case EISDIR : - ret = AFPERR_BADTYPE; - goto openfork_err; - default: - LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) ); - ret = AFPERR_PARAM; + adflags = ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE; + } else { + adflags = ADFLAGS_HF | ADFLAGS_RDONLY; + } + + if (ad_open(ofork->of_ad, upath, adflags, 0666) == 0) { + ofork->of_flags |= AFPFORK_META; + if (ad_get_MD_flags(ofork->of_ad) & O_CREAT) { + LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): setting CNID", upath); + cnid_t id; + if ((id = get_id(vol, ofork->of_ad, st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) { + LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath); goto openfork_err; } - } - else { - /* the ressource fork is open too */ - ofork->of_flags |= AFPFORK_META; + (void)ad_setid(ofork->of_ad, st->st_dev, st->st_ino, id, dir->d_did, vol->v_stamp); + ad_flush(ofork->of_ad); } } else { - /* try opening in read-only mode */ - ret = AFPERR_NOOBJ; - 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; - 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 | ADFLAGS_SETSHRMD) < 0) { - goto openfork_err; - } - adflags = ADFLAGS_DF; - } - /* else we don't set AFPFORK_META because there's no ressource fork file - * We need to check AFPFORK_META in afp_closefork(). eg fork open read-only - * then create in open read-write. - * FIXME , it doesn't play well with byte locking example: - * ressource fork open read only - * locking set on it (no effect, there's no file!) - * ressource fork open read write now - */ - break; - case EMFILE : - case ENFILE : - ret = AFPERR_NFILE; - goto openfork_err; - case EISDIR : - ret = AFPERR_BADTYPE; - goto openfork_err; - default: - LOG(log_error, logtype_afpd, "afp_openfork(\"%s\"): %s", - fullpathname(s_path->m_name), strerror(errno) ); - goto openfork_err; - } - } else { - ofork->of_flags |= AFPFORK_META; + switch (errno) { + case EACCES: + case ENOENT: + /* no metadata? We don't care! */ + break; + case EROFS: + ret = AFPERR_VLOCK; + case EMFILE : + case ENFILE : + ret = AFPERR_NFILE; + goto openfork_err; + case EISDIR : + ret = AFPERR_BADTYPE; + goto openfork_err; + default: + LOG(log_error, logtype_afpd, "afp_openfork(%s): ad_open: %s", s_path->m_name, strerror(errno) ); + ret = AFPERR_PARAM; + goto openfork_err; } } @@ -436,7 +441,7 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si ad_getattr(ofork->of_ad, &bshort); if ((bshort & htons(ATTRBIT_NOWRITE)) && (access & OPENACC_WR)) { ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD); - of_dealloc( ofork ); + of_dealloc(ofork); ofrefnum = 0; memcpy(rbuf, &ofrefnum, sizeof(ofrefnum)); return(AFPERR_OLOCK); @@ -451,13 +456,13 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si if ((eid == ADEID_DFORK) || (ad_reso_fileno(ofork->of_ad) != -1) || (ofork->of_ad->ad_vers == AD_VERSION_EA)) { - ret = fork_setmode(ofork->of_ad, eid, access, ofrefnum); + ret = fork_setmode(obj, ofork->of_ad, eid, access, ofrefnum); /* can we access the fork? */ if (ret < 0) { ofork->of_flags |= AFPFORK_ERROR; ret = errno; ad_close( ofork->of_ad, adflags | ADFLAGS_SETSHRMD); - of_dealloc( ofork ); + of_dealloc(ofork); switch (ret) { case EAGAIN: /* return data anyway */ case EACCES: @@ -479,11 +484,14 @@ int afp_openfork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, si if ((access & OPENACC_RD)) ofork->of_flags |= AFPFORK_ACCRD; + LOG(log_debug, logtype_afpd, "afp_openfork(\"%s\"): fork: %" PRIu16, + fullpathname(s_path->m_name), ofork->of_refnum); + memcpy(rbuf, &ofrefnum, sizeof(ofrefnum)); return( AFP_OK ); openfork_err: - of_dealloc( ofork ); + of_dealloc(ofork); if (errno == EACCES) return (access & OPENACC_WR) ? AFPERR_LOCK : AFPERR_ACCESS; return ret; @@ -575,7 +583,7 @@ int afp_setforkparams(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf _U_, s goto afp_setfork_err; } - err = ad_rtruncate(ofork->of_ad, size); + err = ad_rtruncate(ofork->of_ad, mtoupath(ofork->of_vol, of_name(ofork), ofork->of_did, utf8_encoding(obj)), size); if (st_size > size) ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, size, st_size -size, ofork->of_refnum); if (err < 0) @@ -609,6 +617,7 @@ afp_setfork_err: case EDQUOT: case EFBIG: case ENOSPC: + LOG(log_error, logtype_afpd, "afp_setforkparams: DISK FULL"); return AFPERR_DFULL; default: return AFPERR_PARAM; @@ -718,14 +727,20 @@ int afp_bytelock_ext(AFPObj *obj, char *ibuf, size_t ibuflen, char *rbuf, size_t #undef UNLOCKBIT -static ssize_t read_file(struct ofork *ofork, int eid, - off_t offset, u_char nlmask, - u_char nlchar, char *rbuf, - size_t *rbuflen) +/*! + * Read *rbuflen bytes from fork at offset + * + * @param ofork (r) fork handle + * @param eid (r) data fork or ressource fork entry id + * @param offset (r) offset + * @param rbuf (r) data buffer + * @param rbuflen (rw) in: number of bytes to read, out: bytes read + * + * @return AFP status code + */ +static int read_file(const struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t *rbuflen) { ssize_t cc; - int eof = 0; - char *p, *q; cc = ad_read(ofork->of_ad, eid, offset, rbuf, *rbuflen); if ( cc < 0 ) { @@ -733,53 +748,23 @@ static ssize_t read_file(struct ofork *ofork, int eid, *rbuflen = 0; return( AFPERR_PARAM ); } - if ( (size_t)cc < *rbuflen ) { - eof = 1; - } - - /* - * Do Newline check. - */ - if ( nlmask != 0 ) { - for ( p = rbuf, q = p + cc; p < q; ) { - if (( *p++ & nlmask ) == nlchar ) { - break; - } - } - if ( p != q ) { - cc = p - rbuf; - eof = 0; - } - } - *rbuflen = cc; - if ( eof ) { - return( AFPERR_EOF ); - } + + if ((size_t)cc < *rbuflen) + return AFPERR_EOF; return AFP_OK; } -/* ----------------------------- - * with ddp, afp_read can return fewer bytes than in reqcount - * so return EOF only if read actually past end of file not - * if offset +reqcount > size of file - * e.g.: - * getfork size ==> 10430 - * read fork offset 0 size 10752 ???? ==> 4264 bytes (without EOF) - * read fork offset 4264 size 6128 ==> 4264 (without EOF) - * read fork offset 9248 size 1508 ==> 1182 (EOF) - * 10752 is a bug in Mac 7.5.x finder - * - * with dsi, should we check that reqcount < server quantum? - */ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64) { - struct ofork *ofork; - off_t offset, saveoff, reqcount, savereqcount; - ssize_t cc, err; - int eid; - uint16_t ofrefnum; - u_char nlmask, nlchar; + DSI *dsi = obj->dsi; + struct ofork *ofork; + off_t offset, saveoff, reqcount, savereqcount, size; + ssize_t cc, err; + int eid; + uint16_t ofrefnum; + + /* we break the AFP spec here by not supporting nlmask and nlchar anymore */ ibuf += 2; memcpy(&ofrefnum, ibuf, sizeof( ofrefnum )); @@ -795,27 +780,6 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si err = AFPERR_ACCESS; goto afp_read_err; } - offset = get_off_t(&ibuf, is64); - reqcount = get_off_t(&ibuf, is64); - - LOG(log_debug, logtype_afpd, - "afp_read(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)", offset, reqcount, - (ofork->of_flags & AFPFORK_DATA) ? "d" : "r"); - - if (is64) { - nlmask = nlchar = 0; - } - else { - nlmask = *ibuf++; - nlchar = *ibuf++; - } - /* if we wanted to be picky, we could add in the following - * bit: if (afp_version == 11 && !(nlmask == 0xFF || !nlmask)) - */ - if (reqcount < 0 || offset < 0) { - err = AFPERR_PARAM; - goto afp_read_err; - } if ( ofork->of_flags & AFPFORK_DATA) { eid = ADEID_DFORK; @@ -826,105 +790,117 @@ static int read_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, si goto afp_read_err; } + offset = get_off_t(&ibuf, is64); + reqcount = get_off_t(&ibuf, is64); + /* zero request count */ err = AFP_OK; if (!reqcount) { goto afp_read_err; } - LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd)", - of_name(ofork), (intmax_t)offset, (intmax_t)reqcount); + AFP_READ_START((long)reqcount); + + /* reqcount isn't always truthful. we need to deal with that. */ + size = ad_size(ofork->of_ad, eid); + + LOG(log_debug, logtype_afpd, + "afp_read(fork: %" PRIu16 " [%s], off: %" PRIu64 ", len: %" PRIu64 ", size: %" PRIu64 ")", + ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount, size); + + if (offset >= size) { + err = AFPERR_EOF; + goto afp_read_err; + } + + /* subtract off the offset */ + if (reqcount + offset > size) { + reqcount = size - offset; + err = AFPERR_EOF; + } savereqcount = reqcount; saveoff = offset; - if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, saveoff, savereqcount,ofork->of_refnum) < 0) { - err = AFPERR_LOCK; + + if (reqcount < 0 || offset < 0) { + err = AFPERR_PARAM; goto afp_read_err; } - *rbuflen = MIN(reqcount, *rbuflen); - LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): reading %jd bytes from file", - of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen); + if (obj->options.flags & OPTION_AFP_READ_LOCK) { + if (ad_tmplock(ofork->of_ad, eid, ADLOCK_RD, offset, reqcount, ofork->of_refnum) < 0) { + err = AFPERR_LOCK; + goto afp_read_err; + } + } - err = read_file(ofork, eid, offset, nlmask, nlchar, rbuf, rbuflen); - if (err < 0) +#ifdef WITH_SENDFILE + if (!(eid == ADEID_DFORK && ad_data_fileno(ofork->of_ad) == AD_SYMLINK) && + !(obj->options.flags & OPTION_NOSENDFILE)) { + int fd = ad_readfile_init(ofork->of_ad, eid, &offset, 0); + if (dsi_stream_read_file(dsi, fd, offset, reqcount, err) < 0) { + LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", + of_name(ofork), strerror(errno)); + goto afp_read_exit; + } goto afp_read_done; - LOG(log_debug, logtype_afpd, "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file", + } +#endif + + *rbuflen = MIN(reqcount, dsi->server_quantum); + + cc = read_file(ofork, eid, offset, ibuf, rbuflen); + if (cc < 0) { + err = cc; + goto afp_read_done; + } + + LOG(log_debug, logtype_afpd, + "afp_read(name: \"%s\", offset: %jd, reqcount: %jd): got %jd bytes from file", of_name(ofork), (intmax_t)offset, (intmax_t)reqcount, (intmax_t)*rbuflen); - /* dsi can stream requests. we can only do this if we're not checking - * for an end-of-line character. oh well. */ - if ((*rbuflen < reqcount) && !nlmask) { - DSI *dsi = obj->dsi; - off_t size; + offset += *rbuflen; - /* reqcount isn't always truthful. we need to deal with that. */ - size = ad_size(ofork->of_ad, eid); + /* + * dsi_readinit() returns size of next read buffer. by this point, + * we know that we're sending some data. if we fail, something + * horrible happened. + */ + if ((cc = dsi_readinit(dsi, ibuf, *rbuflen, reqcount, err)) < 0) + goto afp_read_exit; + *rbuflen = cc; - /* subtract off the offset */ - size -= offset; - if (reqcount > size) { - reqcount = size; - err = AFPERR_EOF; - } + while (*rbuflen > 0) { + /* + * This loop isn't really entered anymore, we've already + * sent the whole requested block in dsi_readinit(). + */ + cc = read_file(ofork, eid, offset, ibuf, rbuflen); + if (cc < 0) + goto afp_read_exit; offset += *rbuflen; - - /* dsi_readinit() returns size of next read buffer. by this point, - * we know that we're sending some data. if we fail, something - * horrible happened. */ - if ((cc = dsi_readinit(dsi, rbuf, *rbuflen, reqcount, err)) < 0) + /* dsi_read() also returns buffer size of next allocation */ + cc = dsi_read(dsi, ibuf, *rbuflen); /* send it off */ + if (cc < 0) goto afp_read_exit; *rbuflen = cc; - /* due to the nature of afp packets, we have to exit if we get - an error. we can't do this with translation on. */ -#ifdef WITH_SENDFILE - if (!(obj->options.flags & OPTION_NOSENDFILE)) { - if (dsi_stream_read_file(dsi, - ad_readfile_init(ofork->of_ad, eid, &offset, 0), - offset, - dsi->datasize) < 0) { - switch (errno) { - case EINVAL: - case ENOSYS: - goto afp_read_loop; - default: - LOG(log_error, logtype_afpd, "afp_read(%s): ad_readfile: %s", of_name(ofork), strerror(errno)); - goto afp_read_exit; - } - } - - dsi_readdone(dsi); - goto afp_read_done; - } - afp_read_loop: -#endif - - /* fill up our buffer. */ - while (*rbuflen > 0) { - cc = read_file(ofork, eid, offset, nlmask, nlchar, rbuf,rbuflen); - if (cc < 0) - goto afp_read_exit; - - offset += *rbuflen; - /* dsi_read() also returns buffer size of next allocation */ - cc = dsi_read(dsi, rbuf, *rbuflen); /* send it off */ - if (cc < 0) - goto afp_read_exit; - *rbuflen = cc; - } - dsi_readdone(dsi); - goto afp_read_done; - - afp_read_exit: - LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno)); - dsi_readdone(dsi); - ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum); - obj->exit(EXITERR_CLNT); } + dsi_readdone(dsi); + goto afp_read_done; + +afp_read_exit: + LOG(log_error, logtype_afpd, "afp_read(%s): %s", of_name(ofork), strerror(errno)); + dsi_readdone(dsi); + if (obj->options.flags & OPTION_AFP_READ_LOCK) + ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum); + obj->exit(EXITERR_CLNT); afp_read_done: - ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount,ofork->of_refnum); + if (obj->options.flags & OPTION_AFP_READ_LOCK) + ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, savereqcount, ofork->of_refnum); + + AFP_READ_DONE(); return err; afp_read_err: @@ -1061,7 +1037,7 @@ int flushfork(struct ofork *ofork) return( err ); } -int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) +int afp_closefork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) { struct ofork *ofork; uint16_t ofrefnum; @@ -1075,11 +1051,11 @@ int afp_closefork(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U return( AFPERR_PARAM ); } - LOG(log_debug, logtype_afpd, "afp_closefork(fork: %s)", - (ofork->of_flags & AFPFORK_DATA) ? "d" : "r"); + LOG(log_debug, logtype_afpd, "afp_closefork(fork: %" PRIu16 " [%s])", + ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "rsrc"); - if ( of_closefork( ofork ) < 0 ) { - LOG(log_error, logtype_afpd, "afp_closefork(%s): of_closefork: %s", of_name(ofork), strerror(errno) ); + if (of_closefork(obj, ofork) < 0 ) { + LOG(log_error, logtype_afpd, "afp_closefork: of_closefork: %s", strerror(errno) ); return( AFPERR_PARAM ); } @@ -1091,7 +1067,6 @@ static ssize_t write_file(struct ofork *ofork, int eid, off_t offset, char *rbuf, size_t rbuflen) { - char *p, *q; ssize_t cc; LOG(log_debug, logtype_afpd, "write_file(off: %ju, size: %zu)", @@ -1103,6 +1078,7 @@ static ssize_t write_file(struct ofork *ofork, int eid, case EDQUOT : case EFBIG : case ENOSPC : + LOG(log_error, logtype_afpd, "write_file: DISK FULL"); return( AFPERR_DFULL ); case EACCES: return AFPERR_ACCESS; @@ -1116,16 +1092,21 @@ static ssize_t write_file(struct ofork *ofork, int eid, } -/* FPWrite. NOTE: on an error, we always use afp_write_err as +/* + * FPWrite. NOTE: on an error, we always use afp_write_err as * the client may have sent us a bunch of data that's not reflected - * in reqcount et al. */ + * in reqcount et al. + */ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen, int is64) { struct ofork *ofork; - off_t offset, saveoff, reqcount, oldsize, newsize; + off_t offset, saveoff, reqcount, oldsize, newsize; int endflag, eid, err = AFP_OK; - uint16_t ofrefnum; - ssize_t cc; + uint16_t ofrefnum; + ssize_t cc; + DSI *dsi = obj->dsi; + char *rcvbuf = (char *)dsi->commands; + size_t rcvbuflen = dsi->server_quantum; /* figure out parameters */ ibuf++; @@ -1143,8 +1124,8 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s goto afp_write_err; } - LOG(log_debug, logtype_afpd, "afp_write(off: %" PRIu64 ", size: %" PRIu64 ", fork: %s)", - offset, reqcount, (ofork->of_flags & AFPFORK_DATA) ? "d" : "r"); + LOG(log_debug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu64 ")", + ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, reqcount); if ((ofork->of_flags & AFPFORK_ACCWR) == 0) { err = AFPERR_ACCESS; @@ -1179,6 +1160,7 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s /* offset can overflow on 64-bit capable filesystems. * report disk full if that's going to happen. */ if (sum_neg(is64, offset, reqcount)) { + LOG(log_error, logtype_afpd, "write_fork: DISK FULL"); err = AFPERR_DFULL; goto afp_write_err; } @@ -1189,64 +1171,86 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s goto afp_write_err; } + AFP_WRITE_START((long)reqcount); + saveoff = offset; - if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, - reqcount, ofork->of_refnum) < 0) { - err = AFPERR_LOCK; - goto afp_write_err; + if (obj->options.flags & OPTION_AFP_READ_LOCK) { + if (ad_tmplock(ofork->of_ad, eid, ADLOCK_WR, saveoff, reqcount, ofork->of_refnum) < 0) { + err = AFPERR_LOCK; + goto afp_write_err; + } } - DSI *dsi = obj->dsi; - /* find out what we have already and write it out. */ - cc = dsi_writeinit(dsi, rbuf, *rbuflen); - - if (!cc || (cc = write_file(ofork, eid, offset, rbuf, cc)) < 0) { - dsi_writeflush(dsi); - *rbuflen = 0; - ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum); - return cc; + /* find out what we have already */ + if ((cc = dsi_writeinit(dsi, rcvbuf, rcvbuflen)) > 0) { + ssize_t written; + if ((written = write_file(ofork, eid, offset, rcvbuf, cc)) != cc) { + dsi_writeflush(dsi); + *rbuflen = 0; + if (obj->options.flags & OPTION_AFP_READ_LOCK) + ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum); + if (written > 0) + /* It's used for the read size and as error code in write_file(), ugh */ + written = AFPERR_MISC; + return written; + } } offset += cc; -#if 0 /*def HAVE_SENDFILE_WRITE*/ - if ((cc = ad_writefile(ofork->of_ad, eid, dsi->socket, - offset, dsi->datasize)) < 0) { - switch (errno) { - case EDQUOT: - case EFBIG: - case ENOSPC: - cc = AFPERR_DFULL; - break; - default: - LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno) ); - goto afp_write_loop; +#ifdef WITH_RECVFILE + if (obj->options.flags & OPTION_RECVFILE) { + LOG(log_maxdebug, logtype_afpd, "afp_write(fork: %" PRIu16 " [%s], off: %" PRIu64 ", size: %" PRIu32 ")", + ofork->of_refnum, (ofork->of_flags & AFPFORK_DATA) ? "data" : "reso", offset, dsi->datasize); + + if ((cc = ad_recvfile(ofork->of_ad, eid, dsi->socket, offset, dsi->datasize, obj->options.splice_size)) < dsi->datasize) { + switch (errno) { + case EDQUOT: + case EFBIG: + case ENOSPC: + cc = AFPERR_DFULL; + dsi_writeflush(dsi); + break; + case ENOSYS: + goto afp_write_loop; + default: + /* Low level error, can't do much to back up */ + cc = AFPERR_MISC; + LOG(log_error, logtype_afpd, "afp_write: ad_writefile: %s", strerror(errno)); + } + *rbuflen = 0; + if (obj->options.flags & OPTION_AFP_READ_LOCK) + ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum); + return cc; } - dsi_writeflush(dsi); - *rbuflen = 0; - ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, - reqcount, ofork->of_refnum); - return cc; - } - offset += cc; - goto afp_write_done; -#endif /* 0, was HAVE_SENDFILE_WRITE */ + offset += cc; + goto afp_write_done; + } +#endif +afp_write_loop: /* loop until everything gets written. currently * dsi_write handles the end case by itself. */ - while ((cc = dsi_write(dsi, rbuf, *rbuflen))) { - if ((cc = write_file(ofork, eid, offset, rbuf, cc)) < 0) { + while ((cc = dsi_write(dsi, rcvbuf, rcvbuflen))) { + + if ((cc = write_file(ofork, eid, offset, rcvbuf, cc)) < 0) { dsi_writeflush(dsi); *rbuflen = 0; - ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, - reqcount, ofork->of_refnum); + if (obj->options.flags & OPTION_AFP_READ_LOCK) + ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum); return cc; } + + LOG(log_debug, logtype_afpd, "afp_write: wrote: %jd, offset: %jd", + (intmax_t)cc, (intmax_t)offset); + offset += cc; } - ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum); +afp_write_done: + if (obj->options.flags & OPTION_AFP_READ_LOCK) + ad_tmplock(ofork->of_ad, eid, ADLOCK_CLR, saveoff, reqcount, ofork->of_refnum); if ( ad_meta_fileno( ofork->of_ad ) != -1 ) /* META */ ofork->of_flags |= AFPFORK_DIRTY; @@ -1257,11 +1261,12 @@ static int write_fork(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, s ofork->of_vol->v_appended += (newsize > oldsize) ? (newsize - oldsize) : 0; *rbuflen = set_off_t (offset, rbuf, is64); + AFP_WRITE_DONE(); return( AFP_OK ); afp_write_err: - dsi_writeinit(obj->dsi, rbuf, *rbuflen); - dsi_writeflush(obj->dsi); + dsi_writeinit(dsi, rcvbuf, rcvbuflen); + dsi_writeflush(dsi); if (err != AFP_OK) { *rbuflen = 0;