/*
- * $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.
*/
#include <atalk/util.h>
#include <atalk/cnid.h>
#include <atalk/unix.h>
+#include <atalk/globals.h>
+#include <atalk/fce_api.h>
#include "directory.h"
+#include "dircache.h"
#include "desktop.h"
#include "volume.h"
#include "fork.h"
#include "file.h"
#include "filedir.h"
-#include "globals.h"
#include "unix.h"
/* the format for the finderinfo fields (from IM: Toolbox Essentials):
}
}
- if (islink){
+ if (islink && !vol_syml_opt(vol)) {
u_int16_t linkflag;
memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
linkflag |= htons(FINDERINFO_ISALIAS);
else {
u_int16_t temp;
- if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
- aint = 255;
+ if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
+ aint = UTF8FILELEN_EARLY;
utf8 = vol->v_kTextEncoding;
memcpy(data, &utf8, sizeof(utf8));
(1 << FILPBIT_FNUM) |\
(1 << FILPBIT_UNIXPR)))
-/* -------------------------- */
-u_int32_t get_id(const struct vol *vol, struct adouble *adp, const struct stat *st,
- const cnid_t did, char *upath, const int len)
+/*!
+ * @brief Get CNID for did/upath args both from database and adouble file
+ *
+ * 1. Get the objects CNID as stored in its adouble file
+ * 2. Get the objects CNID from the database
+ * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
+ * 4. In case 2 and 3 differ, store 3 in the adouble file
+ *
+ * @param vol (rw) volume
+ * @param adp (rw) adouble struct of object upath, might be NULL
+ * @param st (r) stat of upath, must NOT be NULL
+ * @param did (r) parent CNID of upath
+ * @param upath (r) name of object
+ * @param len (r) strlen of upath
+ */
+uint32_t get_id(struct vol *vol,
+ struct adouble *adp,
+ const struct stat *st,
+ const cnid_t did,
+ const 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 */
- adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
+ adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
- dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
+ dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
/* Throw errors if cnid_add fails. */
if (dbcnid == CNID_INVALID) {
switch (errno) {
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)) { /* (3) */
+ 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) {
+ vol->v_flags &= ~AFPVOL_CACHE;
+ if (!(vol->v_flags & AFPVOL_TM)) {
+ vol->v_flags |= AFPVOL_RO;
+ 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);
+ }
+ goto restart; /* now try again with the temp CNID db */
+ } else {
+ 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!");
+ }
+ }
afp_errno = AFPERR_MISC;
- return CNID_INVALID;
+ goto exit;
}
}
- else if (adp && (adcnid != dbcnid)) {
+ else if (adp && (adcnid != dbcnid)) { /* 4 */
/* Update the ressource fork. For a folder adp is always null */
- LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
+ LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
+ getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
ad_flush(adp);
}
}
}
+
+exit:
+ first = 0;
return dbcnid;
}
struct stat *st;
struct maccess ma;
+ LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
upath = path->u_name;
st = &path->st;
-
data = buf;
if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
|| (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
|| (bitmap & (1 << FILPBIT_FNUM))) {
- if (!path->id)
- id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
- else
+ if (!path->id) {
+ bstring fullpath;
+ struct dir *cachedfile;
+ int len = strlen(upath);
+ if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
+ id = cachedfile->d_did;
+ else {
+ id = get_id(vol, adp, st, dir->d_did, upath, len);
+
+ /* Add it to the cache */
+ LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
+ ntohl(dir->d_did), upath, ntohl(id));
+
+ /* Get macname from unixname first */
+ if (path->m_name == NULL) {
+ if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
+ LOG(log_error, logtype_afpd, "getmetadata: utompath error");
+ return AFPERR_MISC;
+ }
+ }
+
+ /* Build fullpath */
+ if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
+ || (bconchar(fullpath, '/') != BSTR_OK)
+ || (bcatcstr(fullpath, upath)) != BSTR_OK) {
+ LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
+ LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
+ return AFPERR_MISC;
+ }
+
+ if ((dircache_add(vol, cachedfile)) != 0) {
+ LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
+ return AFPERR_MISC;
+ }
+ }
+ } else {
id = path->id;
+ }
+
if (id == CNID_INVALID)
return afp_errno;
+
if (!path->m_name) {
path->m_name = utompath(vol, upath, id, utf8_encoding());
}
/* FIXME do we want a visual clue if the file is read only
*/
struct maccess ma;
- accessmode( ".", &ma, dir , NULL);
+ accessmode(vol, ".", &ma, dir , NULL);
if ((ma.ma_user & AR_UWRITE)) {
- accessmode( upath, &ma, dir , st);
+ accessmode(vol, upath, &ma, dir , st);
if (!(ma.ma_user & AR_UWRITE)) {
ashort |= htons(ATTRBIT_NOWRITE);
}
break;
case FILPBIT_UNIXPR :
/* accessmode may change st_mode with ACLs */
- accessmode( upath, &ma, dir , st);
+ accessmode(vol, upath, &ma, dir , st);
aint = htonl(st->st_uid);
memcpy( data, &aint, sizeof( aint ));
int opened = 0;
int rc;
+ LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
+
opened = PARAM_NEED_ADP(bitmap);
adp = NULL;
upath = s_path->u_name;
/* if upath is deleted we already in trouble anyway */
- if ((of = of_findname(s_path))) {
+ if ((of = of_findname(vol, s_path))) {
adp = of->of_ad;
} else {
ad_init(&ad, vol->v_adouble, vol->v_ad_options);
return( AFPERR_ACCESS );
case EDQUOT:
case ENOSPC :
+ LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
return( AFPERR_DFULL );
default :
return( AFPERR_PARAM );
path = s_path->m_name;
ad_setname(adp, path);
+
+ struct stat st;
+ if (lstat(upath, &st) != 0) {
+ LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
+ upath, strerror(errno));
+ ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
+ return AFPERR_MISC;
+ }
+
+ (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
+
ad_flush( adp);
+
+ fce_register_new_file(s_path);
+
ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
createfile_done:
- curdir->offcnt++;
+ curdir->d_offcnt++;
#ifdef DROPKLUDGE
if (vol->v_flags & AFPVOL_DROPBOX) {
u_int16_t bitmap = f_bitmap;
u_int32_t cdate,bdate;
u_char finder_buf[32];
+ int fp;
+ ssize_t len;
+ char symbuf[MAXPATHLEN+1];
#ifdef DEBUG
LOG(log_debug9, logtype_afpd, "begin setfilparams:");
break;
case FILPBIT_FINFO :
change_mdate = 1;
- memcpy(finder_buf, buf, 32 );
- if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
- // SLFINFO
- int fp;
- ssize_t len;
- int erc=1;
- char buf[PATH_MAX+1];
- if ((fp=open(path->u_name,O_RDONLY))>=0){
- if ((len=read(fp,buf,PATH_MAX+1))){
- if (unlink(path->u_name)==0){
- buf[len]=0;
- erc = symlink(buf, path->u_name);
- if (!erc)
- of_stat(path);
- }
- }
- close(fp);
+ if (memcmp(buf,"slnkrhap",8) == 0
+ && !(S_ISLNK(path->st.st_mode))
+ && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
+ /* request to turn this into a symlink */
+ if ((fp = open(path->u_name, O_RDONLY)) == -1) {
+ err = AFPERR_MISC;
+ goto setfilparam_done;
+ }
+ len = read(fp, symbuf, MAXPATHLEN);
+ close(fp);
+ if (!(len > 0)) {
+ err = AFPERR_MISC;
+ goto setfilparam_done;
}
- if (erc!=0){
- err=AFPERR_BITMAP;
+ if (unlink(path->u_name) != 0) {
+ err = AFPERR_MISC;
goto setfilparam_done;
}
+ symbuf[len] = 0;
+ if (symlink(symbuf, path->u_name) != 0) {
+ err = AFPERR_MISC;
+ goto setfilparam_done;
+ }
+ of_stat(vol, path);
}
+ memcpy(finder_buf, buf, 32 );
buf += 32;
break;
case FILPBIT_UNIXPR :
{
int rc;
+ LOG(log_debug, logtype_afpd,
+ "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
+
if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
switch ( errno ) {
case ENOENT :
retvalue = err;
goto copy_exit;
}
- curdir->offcnt++;
+ curdir->d_offcnt++;
#ifdef DROPKLUDGE
if (vol->v_flags & AFPVOL_DROPBOX) {
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, sfd, src, dst);
+ if (ad_meta_fileno(adp) != -1)
+ err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
}
}
case EDQUOT:
case EFBIG:
case ENOSPC:
+ LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
return AFPERR_DFULL;
case ENOENT:
return AFPERR_NOOBJ;
cnid_t did = param->did;
cnid_t aint;
- if ( lstat(de->d_name, &path.st)<0 )
+ if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
return 0;
/* update or add to cnid */
}
/* FIXME use of_statdir ? */
- if (lstat(name, &st)) {
+ if (ostat(name, &st, vol_syml_opt(vol))) {
return -1;
}
if (dirreenumerate(dir, &st)) {
/* we already did it once and the dir haven't been modified */
- return dir->offcnt;
+ return dir->d_offcnt;
}
data.vol = vol;
memset(&path, 0, sizeof(path));
path.u_name = upath;
- if ( of_stat(&path) < 0 ) {
+ if (of_stat(vol, &path) < 0 ) {
#ifdef ESTALE
/* with nfs and our working directory is deleted */
if (errno == ESTALE) {
}
err = AFP_OK;
- if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
+ if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
switch (errno) {
case EACCES:
case EPERM:
}
/* ------------------------------ */
-static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
+static struct adouble *find_adouble(const struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
{
int ret;
return NULL;
}
- if ((*of = of_findname(path))) {
+ if ((*of = of_findname(vol, path))) {
/* reuse struct adouble so it won't break locks */
adp = (*of)->of_ad;
}
}
ad_init(&ads, vol->v_adouble, vol->v_ad_options);
- if (!(adsp = find_adouble( path, &s_of, &ads))) {
+ if (!(adsp = find_adouble(vol, path, &s_of, &ads))) {
return afp_errno;
}
}
ad_init(&add, vol->v_adouble, vol->v_ad_options);
- if (!(addp = find_adouble( path, &d_of, &add))) {
+ if (!(addp = find_adouble(vol, path, &d_of, &add))) {
err = afp_errno;
goto err_exchangefile;
}
/* id's need switching. src -> dest and dest -> src.
* we need to re-stat() if it was a cross device copy.
*/
- if (sid) {
- cnid_delete(vol->v_cdb, sid);
- }
- if (did) {
- cnid_delete(vol->v_cdb, did);
- }
- if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
+ if (sid)
+ cnid_delete(vol->v_cdb, sid);
+ if (did)
+ cnid_delete(vol->v_cdb, did);
+
+ if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
||
- (sid && ( (crossdev && lstat(p, &destst) < 0) ||
+ (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
) {
switch (errno) {
ad_close(addp, ADFLAGS_HF);
}
+ struct dir *cached;
+ if ((cached = dircache_search_by_did(vol, sid)) != NULL)
+ (void)dir_remove(vol, cached);
+ if ((cached = dircache_search_by_did(vol, did)) != NULL)
+ (void)dir_remove(vol, cached);
+
return err;
}