+#endif
+
+#ifdef CONFIG_SYNC_FILE_RANGE
+ if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
+ /*
+ * Initiate a writeback. This is not a data integrity sync.
+ * We want to ensure that we don't leave dirty pages in the cache
+ * after write when writeout=immediate is sepcified.
+ */
+ sync_file_range(fs->fd, offset, ret,
+ SYNC_FILE_RANGE_WAIT_BEFORE |
SYNC_FILE_RANGE_WRITE);
+ }
+#endif
+ return ret;
+}
+
+static int cephfs_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred
*credp)
+{
+ D_CEPHFS("cephfs_chmod");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ ret = ceph_chmod(cfsdata->cmount, fs_path->data, credp->fc_mode);
+ return ret;
+}
+
+static int cephfs_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
+ const char *name, FsCred *credp)
+{
+ D_CEPHFS("cephfs_mknod");
+ int ret;
+ V9fsString fullname;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ v9fs_string_init(&fullname);
+ v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+ ret = ceph_mknod(cfsdata->cmount, fullname.data, credp->fc_mode,
+ credp->fc_rdev);
+
+ v9fs_string_free(&fullname);
+ return ret;
+}
+
+static int cephfs_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
+ const char *name, FsCred *credp)
+{
+ D_CEPHFS("cephfs_mkdir");
+ int ret;
+ V9fsString fullname;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ v9fs_string_init(&fullname);
+ v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+ ret = ceph_mkdir(cfsdata->cmount, fullname.data, credp->fc_mode);
+
+ v9fs_string_free(&fullname);
+ return ret;
+}
+
+static int cephfs_fstat(FsContext *fs_ctx, int fid_type,
+ V9fsFidOpenState *fs, struct stat *stbuf)
+{
+ D_CEPHFS("cephfs_fstat");
+ int fd = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ if (fid_type == P9_FID_DIR) {
+ fd = dirfd(fs->dir);
+ } else {
+ fd = fs->fd;
+ }
+ return ceph_fstat(cfsdata->cmount, fd, stbuf);
+}
+
+static int cephfs_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char
*name,
+ int flags, FsCred *credp, V9fsFidOpenState *fs)
+{
+ D_CEPHFS("cephfs_open2");
+ int fd = -1, ret = -1;
+ V9fsString fullname;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ v9fs_string_init(&fullname);
+ v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+ fd = ceph_open(cfsdata->cmount, fullname.data, flags, credp->fc_mode);
+ if (fd >= 0) {
+ /* After creating the file, need to set the cred */
+ ret = cephfs_update_file_cred(cfsdata->cmount, name, credp);
+ if (ret < 0) {
+ ceph_close(cfsdata->cmount, fd);
+ errno = -ret;
+ fd = ret;
+ } else {
+ fs->fd = fd;
+ }
+ } else {
+ errno = -fd;
+ }
+
+ v9fs_string_free(&fullname);
+ return fd;
+}
+
+static int cephfs_symlink(FsContext *fs_ctx, const char *oldpath,
+ V9fsPath *dir_path, const char *name, FsCred
*credp)
+{
+ D_CEPHFS("cephfs_symlink");
+ int ret = -1;
+ V9fsString fullname;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ v9fs_string_init(&fullname);
+ v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+ ret = ceph_symlink(cfsdata->cmount, oldpath, fullname.data);
+
+ v9fs_string_free(&fullname);
+ return ret;
+}
+
+static int cephfs_link(FsContext *ctx, V9fsPath *oldpath,
+ V9fsPath *dirpath, const char *name)
+{
+ D_CEPHFS("cephfs_link");
+ int ret = -1;
+ V9fsString newpath;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ v9fs_string_init(&newpath);
+ v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
+ ret = ceph_link(cfsdata->cmount, oldpath->data, newpath.data);
+
+ v9fs_string_free(&newpath);
+ return ret;
+}
+
+static int cephfs_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
+{
+ D_CEPHFS("cephfs_truncate");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_truncate(cfsdata->cmount, fs_path->data, size);
+
+ return ret;
+}
+
+static int cephfs_rename(FsContext *ctx, const char *oldpath,
+ const char *newpath)
+{
+ D_CEPHFS("cephfs_rename");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_rename(cfsdata->cmount, oldpath, newpath);
+ return ret;
+}
+
+static int cephfs_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred
*credp)
+{
+ D_CEPHFS("cephfs_chown");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)fs_ctx->private;
+
+ ret = ceph_chown(cfsdata->cmount, fs_path->data, credp->fc_uid,
+ credp->fc_gid);
+ return ret;
+}
+
+static int cephfs_utimensat(FsContext *ctx, V9fsPath *fs_path,
+ const struct timespec *buf)
+{
+ D_CEPHFS("cephfs_utimensat");
+ int ret = -1;
+
+#ifdef CONFIG_UTIMENSAT
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_utime(cfsdata->cmount, fs_path->data, (struct utimbuf
*)buf);
+#else
+ ret = -1;
+ errno = ENOSYS;
+#endif
+
+ return ret;
+}
+
+static int cephfs_remove(FsContext *ctx, const char *path)
+{
+ D_CEPHFS("cephfs_remove");
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+static int cephfs_fsync(FsContext *ctx, int fid_type,
+ V9fsFidOpenState *fs, int datasync)
+{
+ D_CEPHFS("cephfs_fsync");
+ int ret = -1, fd = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ if (fid_type == P9_FID_DIR) {
+ fd = dirfd(fs->dir);
+ } else {
+ fd = fs->fd;
+ }
+
+ ret = ceph_fsync(cfsdata->cmount, fd, datasync);
+ return ret;
+}
+
+static int cephfs_statfs(FsContext *ctx, V9fsPath *fs_path,
+ struct statfs *stbuf)
+{
+ D_CEPHFS("cephfs_statfs");
+ int ret;
+ char *path = fs_path->data;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_statfs(cfsdata->cmount, path, (struct statvfs*)stbuf);
+ if (ret) {
+ fprintf(stderr, "ceph_statfs=%d\n", ret);
+ }
+
+ return ret;
+}
+
+/*
+ * Get the extended attribute of normal file, if the path refer to a
symbolic
+ * link, just return the extended attributes of the syslink rather than the
+ * attributes of the link itself.
+ */
+static ssize_t cephfs_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
+ const char *name, void *value, size_t size)
+{
+ D_CEPHFS("cephfs_lgetxattr");
+ int ret;
+ char *path = fs_path->data;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_lgetxattr(cfsdata->cmount, path, name, value, size);
+ return ret;
+}
+
+static ssize_t cephfs_llistxattr(FsContext *ctx, V9fsPath *fs_path,
+ void *value, size_t size)
+{
+ D_CEPHFS("cephfs_llistxattr");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_llistxattr(cfsdata->cmount, fs_path->data, value, size);
+ return ret;
+}
+
+static int cephfs_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char
*name,
+ void *value, size_t size, int flags)
+{
+ D_CEPHFS("cephfs_lsetxattr");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_lsetxattr(cfsdata->cmount, fs_path->data, name, value, size,
+ flags);
+ return ret;
+}
+
+static int cephfs_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
+ const char *name)
+{
+ D_CEPHFS("cephfs_lremovexattr");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_lremovexattr(cfsdata->cmount, fs_path->data, name);
+ return ret;
+}
+
+static int cephfs_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+ const char *name, V9fsPath *target)
+{
+ D_CEPHFS("cephfs_name_to_path");
+ if (dir_path) {
+ v9fs_string_sprintf((V9fsString *)target, "%s/%s",
+ dir_path->data, name);
+ } else {
+ /* if the path does not start from '/' */
+ v9fs_string_sprintf((V9fsString *)target, "%s", name);
+ }
+
+ /* Bump the size for including terminating NULL */
+ target->size++;
+ return 0;
+}
+
+static int cephfs_renameat(FsContext *ctx, V9fsPath *olddir,
+ const char *old_name, V9fsPath *newdir,
+ const char *new_name)
+{
+ D_CEPHFS("cephfs_renameat");
+ int ret = -1;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ ret = ceph_rename(cfsdata->cmount, old_name, new_name);
+ return ret;
+}
+
+static int cephfs_unlinkat(FsContext *ctx, V9fsPath *dir,
+ const char *name, int flags)
+{
+ D_CEPHFS("cephfs_unlinkat");
+ int ret = 0;
+ char *path = dir->data;
+ struct stat fstat;
+ V9fsString fullname;
+ struct cephfs_data *cfsdata = (struct cephfs_data *)ctx->private;
+
+ v9fs_string_init(&fullname);
+ v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
+ path = fullname.data;
+ /* determine which kind of file is being destroyed */
+ ret = ceph_lstat(cfsdata->cmount, path, &fstat);
+ if (!ret) {
+ switch (fstat.st_mode & S_IFMT) {
+ case S_IFDIR:
+ ret = ceph_rmdir(cfsdata->cmount, path);
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ case S_IFLNK:
+ case S_IFREG:
+ case S_IFSOCK:
+ ret = ceph_unlink(cfsdata->cmount, path);
+ break;
+
+ default:
+ fprintf(stderr, "ceph_lstat unknown stmode\n");
+ break;
+ }
+ } else {
+ errno = -ret;
+ ret = -1;
+ }
+
+ v9fs_string_free(&fullname);
+ return ret;
+}
+
+/*
+ * Do two things in the init function:
+ * 1) Create a mount handle used by all cephfs interfaces.
+ * 2) Invoke ceph_mount() to initialize a link between the client and
+ * ceph monitor
+ */
+static int cephfs_init(FsContext *ctx)
+{
+ D_CEPHFS("cephfs_init");
+ int ret;
+ const char *ver = NULL;
+ struct cephfs_data *data = g_malloc(sizeof(struct cephfs_data));
+
+ if (data == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ memset(data, 0, sizeof(struct cephfs_data));
+ ret = ceph_create(&data->cmount, NULL);
+ if (ret) {
+ fprintf(stderr, "ceph_create=%d\n", ret);
+ goto err_out;
+ }
+
+ ret = ceph_conf_read_file(data->cmount, NULL);
+ if (ret) {
+ fprintf(stderr, "ceph_conf_read_file=%d\n", ret);
+ goto err_out;
+ }
+
+ ret = ceph_mount(data->cmount, ctx->fs_root);
+ if (ret) {
+ fprintf(stderr, "ceph_mount=%d\n", ret);
+ goto err_out;
+ } else {
+ ctx->private = data;
+ /* CephFS does not support FS_IOC_GETVERSIO */
+ ctx->exops.get_st_gen = NULL;