/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- * vim:expandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2001 Cluster File Systems, Inc. * * This file is part of InterMezzo, http://www.inter-mezzo.org. * * InterMezzo is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * InterMezzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with InterMezzo; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Reintegration of KML records * */ #include #include #include #include #include #include #include #include #include #include #include #include "intermezzo_fs.h" #include "intermezzo_psdev.h" static void kmlreint_pre_secure(struct kml_rec *rec, struct file *dir, struct run_ctxt *saved) { struct run_ctxt ctxt; struct presto_dentry_data *dd = presto_d2d(dir->f_dentry); int i; ctxt.fsuid = rec->prefix.hdr->fsuid; ctxt.fsgid = rec->prefix.hdr->fsgid; ctxt.fs = KERNEL_DS; ctxt.pwd = dd->dd_fset->fset_dentry; ctxt.pwdmnt = dd->dd_fset->fset_mnt; ctxt.root = ctxt.pwd; ctxt.rootmnt = ctxt.pwdmnt; if (rec->prefix.hdr->ngroups > 0) { ctxt.group_info = groups_alloc(rec->prefix.hdr->ngroups); for (i = 0; i< ctxt.group_info->ngroups; i++) GROUP_AT(ctxt.group_info,i)= rec->prefix.groups[i]; } else ctxt.group_info = groups_alloc(0); push_ctxt(saved, &ctxt); } /* Append two strings in a less-retarded fashion. */ static char * path_join(char *p1, int p1len, char *p2, int p2len) { int size = p1len + p2len + 2; /* possibly one extra /, one NULL */ char *path; path = kmalloc(size, GFP_KERNEL); if (path == NULL) return NULL; memcpy(path, p1, p1len); if (path[p1len - 1] != '/') { path[p1len] = '/'; p1len++; } memcpy(path + p1len, p2, p2len); path[p1len + p2len] = '\0'; return path; } static inline int kml_recno_equal(struct kml_rec *rec, struct presto_file_set *fset) { return (rec->suffix->recno == fset->fset_lento_recno + 1); } static inline int version_equal(struct presto_version *a, struct inode *inode) { if (a == NULL) return 1; if (inode == NULL) { CERROR("InterMezzo: NULL inode in version_equal()\n"); return 0; } if (inode->i_mtime.tv_sec == a->pv_mtime_sec && inode->i_mtime.tv_nsec == a->pv_mtime_nsec && (S_ISDIR(inode->i_mode) || inode->i_size == a->pv_size)) return 1; return 0; } static int reint_close(struct kml_rec *rec, struct file *file, struct lento_vfs_context *given_info) { struct run_ctxt saved_ctxt; int error; struct presto_file_set *fset; struct lento_vfs_context info; ENTRY; memcpy(&info, given_info, sizeof(*given_info)); CDEBUG (D_KML, "=====REINT_CLOSE::%s\n", rec->path); fset = presto_fset(file->f_dentry); if (fset->fset_flags & FSET_DATA_ON_DEMAND) { struct iattr iattr; iattr.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_SIZE; iattr.ia_mtime.tv_sec = (time_t)rec->new_objectv->pv_mtime_sec; iattr.ia_mtime.tv_nsec = (time_t)rec->new_objectv->pv_mtime_nsec; iattr.ia_ctime.tv_sec = (time_t)rec->new_objectv->pv_ctime_sec; iattr.ia_ctime.tv_nsec = (time_t)rec->new_objectv->pv_ctime_nsec; iattr.ia_size = (time_t)rec->new_objectv->pv_size; /* no kml record, but update last rcvd */ /* save fileid in dentry for later backfetch */ info.flags |= LENTO_FL_EXPECT | LENTO_FL_SET_DDFILEID; info.remote_ino = rec->ino; info.remote_generation = rec->generation; info.flags &= ~LENTO_FL_KML; kmlreint_pre_secure(rec, file, &saved_ctxt); error = lento_setattr(rec->path, &iattr, &info); pop_ctxt(&saved_ctxt); presto_d2d(file->f_dentry)->dd_flags &= ~PRESTO_DATA; } else { int minor = presto_f2m(fset); info.updated_time.tv_sec = rec->new_objectv->pv_mtime_sec; info.updated_time.tv_nsec = rec->new_objectv->pv_mtime_nsec; memcpy(&info.remote_version, rec->old_objectv, sizeof(*rec->old_objectv)); info.remote_ino = rec->ino; info.remote_generation = rec->generation; error = izo_upc_backfetch(minor, rec->path, fset->fset_name, &info); if (error) { CERROR("backfetch error %d\n", error); /* if file doesn't exist anymore, then ignore the CLOSE * and just update the last_rcvd. */ if (error == ENOENT) { CDEBUG(D_KML, "manually updating remote offset uuid %s" "recno %d offset %Lu\n", info.uuid, info.recno, (unsigned long long) info.kml_offset); error = izo_rcvd_upd_remote(fset, info.uuid, info.recno, info.kml_offset); if(error) CERROR("izo_rcvd_upd_remote error %d\n", error); } } /* propagate error to avoid further reint */ } EXIT; return error; } static int reint_create(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; ENTRY; CDEBUG (D_KML, "=====REINT_CREATE::%s\n", rec->path); info->updated_time.tv_sec = rec->new_objectv->pv_ctime_sec; info->updated_time.tv_nsec = rec->new_objectv->pv_ctime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_create(rec->path, rec->mode, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_link(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; ENTRY; CDEBUG (D_KML, "=====REINT_LINK::%s -> %s\n", rec->path, rec->target); info->updated_time.tv_sec = rec->new_objectv->pv_mtime_sec; info->updated_time.tv_nsec = rec->new_objectv->pv_mtime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_link(rec->path, rec->target, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_mkdir(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; ENTRY; CDEBUG (D_KML, "=====REINT_MKDIR::%s\n", rec->path); info->updated_time.tv_sec = rec->new_objectv->pv_ctime_sec; info->updated_time.tv_nsec = rec->new_objectv->pv_ctime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_mkdir(rec->path, rec->mode, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_mknod(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; dev_t dev; ENTRY; CDEBUG (D_KML, "=====REINT_MKNOD::%s\n", rec->path); info->updated_time.tv_sec = rec->new_objectv->pv_ctime_sec; info->updated_time.tv_nsec = rec->new_objectv->pv_ctime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); dev = rec->rdev ? old_decode_dev(rec->rdev) : MKDEV(rec->major, rec->minor); error = lento_mknod(rec->path, rec->mode, dev, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_noop(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { return 0; } static int reint_rename(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; ENTRY; CDEBUG (D_KML, "=====REINT_RENAME::%s -> %s\n", rec->path, rec->target); info->updated_time.tv_sec = rec->new_objectv->pv_mtime_sec; info->updated_time.tv_nsec = rec->new_objectv->pv_mtime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_rename(rec->path, rec->target, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_rmdir(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; char *path; ENTRY; path = path_join(rec->path, rec->pathlen - 1, rec->target, rec->targetlen); if (path == NULL) { EXIT; return -ENOMEM; } CDEBUG (D_KML, "=====REINT_RMDIR::%s\n", path); info->updated_time.tv_sec = rec->new_parentv->pv_mtime_sec; info->updated_time.tv_nsec = rec->new_parentv->pv_mtime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_rmdir(path, info); pop_ctxt(&saved_ctxt); kfree(path); EXIT; return error; } static int reint_setattr(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; struct iattr iattr; int error; ENTRY; iattr.ia_valid = rec->valid; iattr.ia_mode = (umode_t)rec->mode; iattr.ia_uid = (uid_t)rec->uid; iattr.ia_gid = (gid_t)rec->gid; iattr.ia_size = (off_t)rec->size; iattr.ia_ctime.tv_sec = rec->ctime_sec; iattr.ia_ctime.tv_nsec = rec->ctime_nsec; iattr.ia_mtime.tv_sec = rec->mtime_sec; iattr.ia_mtime.tv_nsec = rec->mtime_nsec; iattr.ia_atime = iattr.ia_mtime; /* We don't track atimes. */ iattr.ia_attr_flags = rec->flags; CDEBUG (D_KML, "=====REINT_SETATTR::%s (%d)\n", rec->path, rec->valid); kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_setattr(rec->path, &iattr, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_symlink(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; ENTRY; CDEBUG (D_KML, "=====REINT_SYMLINK::%s -> %s\n", rec->path, rec->target); info->updated_time.tv_sec = rec->new_objectv->pv_ctime_sec; info->updated_time.tv_nsec = rec->new_objectv->pv_ctime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_symlink(rec->target, rec->path, info); pop_ctxt(&saved_ctxt); EXIT; return error; } static int reint_unlink(struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info) { struct run_ctxt saved_ctxt; int error; char *path; ENTRY; path = path_join(rec->path, rec->pathlen - 1, rec->target, rec->targetlen); if (path == NULL) { EXIT; return -ENOMEM; } CDEBUG (D_KML, "=====REINT_UNLINK::%s\n", path); info->updated_time.tv_sec = rec->new_parentv->pv_mtime_sec; info->updated_time.tv_nsec = rec->new_parentv->pv_mtime_nsec; kmlreint_pre_secure(rec, dir, &saved_ctxt); error = lento_unlink(path, info); pop_ctxt(&saved_ctxt); kfree(path); EXIT; return error; } static int branch_reint_rename(struct presto_file_set *fset, struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info, char * kml_data, __u64 kml_size) { int error; ENTRY; error = reint_rename(rec, dir, info); if (error == -ENOENT) { /* normal reint failed because path was not found */ struct rec_info rec; CDEBUG(D_KML, "saving branch rename kml\n"); rec.is_kml = 1; rec.size = kml_size; error = presto_log(fset, &rec, kml_data, kml_size, NULL, 0, NULL, 0, NULL, 0); if (error == 0) error = presto_write_last_rcvd(&rec, fset, info); } EXIT; return error; } int branch_reinter(struct presto_file_set *fset, struct kml_rec *rec, struct file *dir, struct lento_vfs_context *info, char * kml_data, __u64 kml_size) { int error = 0; int op = rec->prefix.hdr->opcode; if (op == KML_OPCODE_CLOSE) { /* regular close and backfetch */ error = reint_close(rec, dir, info); } else if (op == KML_OPCODE_RENAME) { /* rename only if name already exists */ error = branch_reint_rename(fset, rec, dir, info, kml_data, kml_size); } else { /* just rewrite kml into branch/kml and update last_rcvd */ struct rec_info rec; CDEBUG(D_KML, "Saving branch kml\n"); rec.is_kml = 1; rec.size = kml_size; error = presto_log(fset, &rec, kml_data, kml_size, NULL, 0, NULL, 0, NULL, 0); if (error == 0) error = presto_write_last_rcvd(&rec, fset, info); } return error; } typedef int (*reinter_t)(struct kml_rec *rec, struct file *basedir, struct lento_vfs_context *info); static reinter_t presto_reinters[KML_OPCODE_NUM] = { [KML_OPCODE_CLOSE] = reint_close, [KML_OPCODE_CREATE] = reint_create, [KML_OPCODE_LINK] = reint_link, [KML_OPCODE_MKDIR] = reint_mkdir, [KML_OPCODE_MKNOD] = reint_mknod, [KML_OPCODE_NOOP] = reint_noop, [KML_OPCODE_RENAME] = reint_rename, [KML_OPCODE_RMDIR] = reint_rmdir, [KML_OPCODE_SETATTR] = reint_setattr, [KML_OPCODE_SYMLINK] = reint_symlink, [KML_OPCODE_UNLINK] = reint_unlink, }; static inline reinter_t get_reinter(int op) { if (op < 0 || op >= sizeof(presto_reinters) / sizeof(reinter_t)) return NULL; else return presto_reinters[op]; } int kml_reint_rec(struct file *dir, struct izo_ioctl_data *data) { char *ptr; char *end; struct kml_rec rec; int error = 0; struct lento_vfs_context info; struct presto_cache *cache; struct presto_file_set *fset; struct presto_dentry_data *dd = presto_d2d(dir->f_dentry); int op; reinter_t reinter; struct izo_rcvd_rec lr_rec; int off; ENTRY; error = presto_prep(dir->f_dentry, &cache, &fset); if ( error ) { CERROR("intermezzo: Reintegration on invalid file\n"); return error; } if (!dd || !dd->dd_fset || dd->dd_fset->fset_dentry != dir->f_dentry) { CERROR("intermezzo: reintegration on non-fset root (ino %ld)\n", dir->f_dentry->d_inode->i_ino); return -EINVAL; } if (data->ioc_plen1 > 64 * 1024) { EXIT; return -ENOSPC; } ptr = fset->fset_reint_buf; end = ptr + data->ioc_plen1; if (copy_from_user(ptr, data->ioc_pbuf1, data->ioc_plen1)) { EXIT; error = -EFAULT; goto out; } error = kml_unpack(&rec, &ptr, end); if (error) { EXIT; error = -EFAULT; goto out; } off = izo_rcvd_get(&lr_rec, fset, data->ioc_uuid); if (off < 0) { CERROR("No last_rcvd record, setting to 0\n"); memset(&lr_rec, 0, sizeof(lr_rec)); } data->ioc_kmlsize = ptr - fset->fset_reint_buf; if (rec.suffix->recno != lr_rec.lr_remote_recno + 1) { CERROR("KML record number %Lu expected, not %d\n", (unsigned long long) (lr_rec.lr_remote_recno + 1), rec.suffix->recno); #if 0 if (!version_check(&rec, dd->dd_fset, &info)) { /* FIXME: do an upcall to resolve conflicts */ CERROR("intermezzo: would be a conflict!\n"); error = -EINVAL; EXIT; goto out; } #endif } op = rec.prefix.hdr->opcode; reinter = get_reinter(op); if (!reinter) { CERROR("%s: Unrecognized KML opcode %d\n", __FUNCTION__, op); error = -EINVAL; EXIT; goto out; } info.kml_offset = data->ioc_offset + data->ioc_kmlsize; info.recno = rec.suffix->recno; info.flags = LENTO_FL_EXPECT; if (data->ioc_flags) info.flags |= LENTO_FL_KML; memcpy(info.uuid, data->ioc_uuid, sizeof(info.uuid)); if (fset->fset_flags & FSET_IS_BRANCH && data->ioc_flags) error = branch_reinter(fset, &rec, dir, &info, fset->fset_reint_buf, data->ioc_kmlsize); else error = reinter(&rec, dir, &info); out: EXIT; return error; } int izo_get_fileid(struct file *dir, struct izo_ioctl_data *data) { char *buf = NULL; char *ptr; char *end; struct kml_rec rec; struct file *file; struct presto_cache *cache; struct presto_file_set *fset; struct presto_dentry_data *dd = presto_d2d(dir->f_dentry); struct run_ctxt saved_ctxt; int error; ENTRY; error = presto_prep(dir->f_dentry, &cache, &fset); if ( error ) { CERROR("intermezzo: Reintegration on invalid file\n"); return error; } if (!dd || !dd->dd_fset || dd->dd_fset->fset_dentry != dir->f_dentry) { CERROR("intermezzo: reintegration on non-fset root (ino %ld)\n", dir->f_dentry->d_inode->i_ino); return -EINVAL; } PRESTO_ALLOC(buf, data->ioc_plen1); if (!buf) { EXIT; return -ENOMEM; } ptr = buf; end = buf + data->ioc_plen1; if (copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) { EXIT; PRESTO_FREE(buf, data->ioc_plen1); return -EFAULT; } error = kml_unpack(&rec, &ptr, end); if (error) { EXIT; PRESTO_FREE(buf, data->ioc_plen1); return -EFAULT; } kmlreint_pre_secure(&rec, dir, &saved_ctxt); file = filp_open(rec.path, O_RDONLY, 0); if (!file || IS_ERR(file)) { error = PTR_ERR(file); goto out; } data->ioc_ino = file->f_dentry->d_inode->i_ino; data->ioc_generation = file->f_dentry->d_inode->i_generation; filp_close(file, 0); CDEBUG(D_FILE, "%s ino %Lx, gen %Lx\n", rec.path, (unsigned long long) data->ioc_ino, (unsigned long long) data->ioc_generation); out: if (buf) PRESTO_FREE(buf, data->ioc_plen1); pop_ctxt(&saved_ctxt); EXIT; return error; }