/*
- * $Id: file.c,v 1.137 2010-02-17 01:41:58 didg Exp $
+ * $Id: file.c,v 1.141 2010/03/12 15:16:49 franklahm Exp $
*
* Copyright (c) 1990,1993 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
(1 << FILPBIT_UNIXPR)))
/* -------------------------- */
-u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
- const cnid_t did, char *upath, const int len)
+uint32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
+ const cnid_t did, char *upath, const int len)
{
+ static int first = 1; /* mark if this func is called the first time */
u_int32_t adcnid;
u_int32_t dbcnid = CNID_INVALID;
+restart:
if (vol->v_cdb != NULL) {
/* prime aint with what we think is the cnid, set did to zero for
catching moved files */
case CNID_ERR_PARAM:
LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
afp_errno = AFPERR_PARAM;
- return CNID_INVALID;
+ goto exit;
case CNID_ERR_PATH:
afp_errno = AFPERR_PARAM;
- return CNID_INVALID;
+ goto exit;
default:
+ /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
+ /* we have to do it here for "dbd" because it uses "lazy opening" */
+ /* In order to not end in a loop somehow with goto restart below */
+ /* */
+ if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
+ cnid_close(vol->v_cdb);
+ free(vol->v_cnidscheme);
+ vol->v_cnidscheme = strdup("tdb");
+
+ int flags = CNID_FLAG_MEMORY;
+ if ((vol->v_flags & AFPVOL_NODEV)) {
+ flags |= CNID_FLAG_NODEV;
+ }
+ LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
+ vol->v_path);
+ vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
+ if (vol->v_cdb) {
+ /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
+ vol->v_flags &= ~AFPVOL_CACHE;
+ vol->v_flags |= AFPVOL_RO;
+#ifdef SERVERTEXT
+ /* kill ourself with SIGUSR2 aka msg pending */
+ setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
+ "Check server messages for details. Switching to read-only mode.");
+ kill(getpid(), SIGUSR2);
+#endif
+ goto restart; /* not try again with the temp CNID db */
+ } else {
+#ifdef SERVERTEXT
+ setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
+ "Check server messages for details, can't recover from this state!");
+#endif
+ }
+ }
afp_errno = AFPERR_MISC;
- return CNID_INVALID;
+ goto exit;
}
}
else if (adp && (adcnid != dbcnid)) {
}
}
}
+
+exit:
+ first = 0;
return dbcnid;
}
struct stat *st;
struct maccess ma;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "begin getmetadata:");
-#endif /* DEBUG */
upath = path->u_name;
st = &path->st;
#endif
memcpy(data, &ashort, sizeof( ashort ));
data += sizeof( ashort );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
+ path->u_name, ntohs(ashort));
break;
case FILPBIT_PDID :
memcpy(data, &dir->d_did, sizeof( u_int32_t ));
data += sizeof( u_int32_t );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
+ path->u_name, ntohl(dir->d_did));
break;
case FILPBIT_CDATE :
case FILPBIT_FNUM :
memcpy(data, &id, sizeof( id ));
data += sizeof( id );
+ LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
+ path->u_name, ntohl(id));
break;
case FILPBIT_DFLEN :
int opened = 0;
int rc;
-#ifdef DEBUG
- LOG(log_debug9, logtype_default, "begin getfilparams:");
-#endif /* DEBUG */
-
opened = PARAM_NEED_ADP(bitmap);
adp = NULL;
if ( adp ) {
ad_close_metadata( adp);
}
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "end getfilparams:");
-#endif /* DEBUG */
return( rc );
}
* renamefile and copyfile take the old and new unix pathnames
* and the new mac name.
*
+ * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
+ * passing -1 means this is not used, src path is a full path
* src the source path
* dst the dest filename in current dir
* newname the dest mac name
* adp adouble struct of src file, if open, or & zeroed one
*
*/
-int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
+int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
{
int rc;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "begin renamefile:");
-#endif /* DEBUG */
-
- if ( unix_rename( src, dst ) < 0 ) {
+ if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
switch ( errno ) {
case ENOENT :
return( AFPERR_NOOBJ );
/* FIXME warning in syslog so admin'd know there's a conflict ?*/
return AFPERR_OLOCK; /* little lie */
}
- if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
+ if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
/* on error copyfile delete dest */
return( rc );
}
- return deletefile(vol, src, 0);
+ return deletefile(vol, sdir_fd, src, 0);
default :
return( AFPERR_PARAM );
}
}
- if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
+ if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
int err;
err = errno;
* we know we are on the same device
*/
if (err) {
- unix_rename( dst, src );
+ unix_rename(-1, dst, sdir_fd, src );
/* return the first error */
switch ( err) {
case ENOENT :
ad_flush( adp );
ad_close( adp, ADFLAGS_HF );
}
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "end renamefile:");
-#endif /* DEBUG */
return( AFP_OK );
}
}
denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
- ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
+
if (denyreadset) {
- return AFPERR_DENYCONF;
+ retvalue = AFPERR_DENYCONF;
+ goto copy_exit;
}
newname = obj->newtmp;
p = ctoupath( s_vol, curdir, newname );
if (!p) {
- return AFPERR_PARAM;
-
+ retvalue = AFPERR_PARAM;
+ goto copy_exit;
}
+
#ifdef FORCE_UIDGID
/* FIXME svid != dvid && dvid's user can't read svid */
#endif
if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
- return( AFPERR_PARAM );
+ retvalue = AFPERR_PARAM;
+ goto copy_exit;
}
- if (d_vol->v_flags & AFPVOL_RO)
- return AFPERR_VLOCK;
+ if (d_vol->v_flags & AFPVOL_RO) {
+ retvalue = AFPERR_VLOCK;
+ goto copy_exit;
+ }
if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
- return afp_errno;
+ retvalue = afp_errno;
+ goto copy_exit;
}
if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
- return get_afp_errno(AFPERR_NOOBJ);
+ retvalue = get_afp_errno(AFPERR_NOOBJ);
+ goto copy_exit;
}
+
if ( *s_path->m_name != '\0' ) {
- path_error(s_path, AFPERR_PARAM);
+ retvalue =path_error(s_path, AFPERR_NOOBJ);
+ goto copy_exit;
}
/* one of the handful of places that knows about the path type */
if (copy_path_name(d_vol, newname, ibuf) < 0) {
- return( AFPERR_PARAM );
+ retvalue = AFPERR_PARAM;
+ goto copy_exit;
}
/* newname is always only a filename so curdir *is* its
* parent folder
*/
if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
- return( AFPERR_PARAM );
+ retvalue =AFPERR_PARAM;
+ goto copy_exit;
}
- if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
- return err;
+
+ if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
+ retvalue = err;
+ goto copy_exit;
}
curdir->offcnt++;
setvoltime(obj, d_vol );
+copy_exit:
+ ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
return( retvalue );
}
* because we are doing it elsewhere.
* currently if newname is NULL then adp is NULL.
*/
-int copyfile(const struct vol *s_vol, const struct vol*d_vol,
- char *src, char *dst, char *newname, struct adouble *adp)
+int copyfile(const struct vol *s_vol,
+ const struct vol *d_vol,
+ int sfd,
+ char *src,
+ char *dst,
+ char *newname,
+ struct adouble *adp)
{
struct adouble ads, add;
int err = 0;
int stat_result;
struct stat st;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "begin copyfile:");
-#endif /* DEBUG */
+ LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
+ sfd, src, dst, newname);
if (adp == NULL) {
ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
adp = &ads;
}
- ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
+
adflags = ADFLAGS_DF;
if (newname) {
adflags |= ADFLAGS_HF;
}
- if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
+ if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
ret_err = errno;
goto done;
}
st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
}
+ ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
ret_err = errno;
ad_close( adp, adflags );
if (EEXIST != ret_err) {
- deletefile(d_vol, dst, 0);
+ deletefile(d_vol, -1, dst, 0);
goto done;
}
return AFPERR_EXIST;
if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
/* copy the data fork */
if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
- err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
+ err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
}
}
}
if (ret_err) {
- deletefile(d_vol, dst, 0);
+ deletefile(d_vol, -1, dst, 0);
}
else if (stat_result == 0) {
/* set dest modification date to src date */
*/
}
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "end copyfile:");
-#endif /* DEBUG */
-
done:
switch ( ret_err ) {
case 0:
}
return 0;
}
-
-int deletefile(const struct vol *vol, char *file, int checkAttrib)
+/*
+ * dirfd can be used for unlinkat semantics
+ */
+int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
{
struct adouble ad;
- struct adouble *adp = &ad;
+ struct adouble *adp = NULL;
int adflags, err = AFP_OK;
+ int meta = 0;
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "begin deletefile:");
-#endif /* DEBUG */
+ LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
- /* try to open both forks at once */
- adflags = ADFLAGS_DF|ADFLAGS_HF;
+ ad_init(&ad, vol->v_adouble, vol->v_ad_options);
if (checkAttrib) {
/* was EACCESS error try to get only metadata */
- ad_init(&ad, vol->v_adouble, vol->v_ad_options);
/* we never want to create a resource fork here, we are going to delete it
* moreover sometimes deletefile is called with a no existent file and
* ad_open would create a 0 byte resource fork
*/
- if ( ad_metadata( file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
- ad_close( &ad, adflags );
+ if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
if ((err = check_attrib(&ad))) {
+ ad_close_metadata(&ad);
return err;
}
+ meta = 1;
}
}
- while(1) {
- ad_init(&ad, vol->v_adouble, vol->v_ad_options); /* OK */
- if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
- switch (errno) {
- case ENOENT:
- if (adflags == ADFLAGS_DF)
- return AFPERR_NOOBJ;
-
- /* that failed. now try to open just the data fork */
- adflags = ADFLAGS_DF;
- continue;
-
- case EACCES:
- adp = NULL; /* maybe it's a file with no write mode for us */
- break; /* was return AFPERR_ACCESS;*/
- case EROFS:
- return AFPERR_VLOCK;
- default:
- return( AFPERR_PARAM );
- }
+ /* try to open both forks at once */
+ adflags = ADFLAGS_DF;
+ if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
+ switch (errno) {
+ case ENOENT:
+ err = AFPERR_NOOBJ;
+ goto end;
+ case EACCES: /* maybe it's a file with no write mode for us */
+ break; /* was return AFPERR_ACCESS;*/
+ case EROFS:
+ err = AFPERR_VLOCK;
+ goto end;
+ default:
+ err = AFPERR_PARAM;
+ goto end;
}
- break; /* from the while */
+ }
+ else {
+ adp = &ad;
}
- if (adp && (adflags & ADFLAGS_HF) ) {
+ if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
+ adflags |= ADFLAGS_HF;
/* FIXME we have a pb here because we want to know if a file is open
* there's a 'priority inversion' if you can't open the ressource fork RW
* you can delete it if it's open because you can't get a write lock.
* FIXME it doesn't work for RFORK open read only and fork open without deny mode
*/
if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
- ad_close( &ad, adflags );
- return( AFPERR_BUSY );
+ err = AFPERR_BUSY;
+ goto end;
}
}
if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
err = AFPERR_BUSY;
- }
- else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
+ } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
cnid_t id;
- if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
- {
+ if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
cnid_delete(vol->v_cdb, id);
}
}
+
+end:
+ if (meta)
+ ad_close_metadata(&ad);
+
if (adp)
ad_close( &ad, adflags ); /* ad_close removes locks if any */
-#ifdef DEBUG
- LOG(log_debug9, logtype_afpd, "end deletefile:");
-#endif /* DEBUG */
-
return err;
}
}
if (NULL == ( dir = dirlookup( vol, id )) ) {
+ if (afp_errno == AFPERR_NOOBJ) {
+ err = AFPERR_NOOBJ;
+ goto delete;
+ }
return( AFPERR_PARAM );
}
else if (S_ISDIR(st.st_mode)) /* directories are bad */
return AFPERR_BADTYPE;
+delete:
if (cnid_delete(vol->v_cdb, fileid)) {
switch (errno) {
case EROFS:
}
/* now, quickly rename the file. we error if we can't. */
- if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
+ if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
goto err_exchangefile;
of_rename(vol, s_of, sdir, spath, curdir, temp);
/* rename destination to source */
- if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
+ if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
goto err_src_to_tmp;
of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
/* rename temp to destination */
- if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
+ if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
goto err_dest_to_src;
of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
* properly. */
err_temp_to_dest:
/* rename dest to temp */
- renamefile(vol, upath, temp, temp, adsp);
+ renamefile(vol, -1, upath, temp, temp, adsp);
of_rename(vol, s_of, curdir, upath, curdir, temp);
err_dest_to_src:
/* rename source back to dest */
- renamefile(vol, p, upath, path->m_name, addp);
+ renamefile(vol, -1, p, upath, path->m_name, addp);
of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
err_src_to_tmp:
/* rename temp back to source */
- renamefile(vol, temp, p, spath, adsp);
+ renamefile(vol, -1, temp, p, spath, adsp);
of_rename(vol, s_of, curdir, temp, sdir, spath);
err_exchangefile: