/**************************************************************************** * fs/rpmsgfs/rpmsgfs_server.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "rpmsgfs.h" /**************************************************************************** * Private Types ****************************************************************************/ struct rpmsgfs_server_s { struct rpmsg_endpoint ept; FAR struct file **files; FAR void **dirs; int file_rows; int dir_nums; sem_t sem; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int rpmsgfs_open_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_close_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_read_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_write_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_lseek_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_ioctl_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_sync_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_dup_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_fstat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_ftruncate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_opendir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_readdir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_rewinddir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_closedir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_statfs_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_unlink_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_mkdir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_rmdir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_rename_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_stat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_fchstat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsgfs_chstat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static void rpmsgfs_ns_bind(FAR struct rpmsg_device *rdev, FAR void *priv_, FAR const char *name, uint32_t dest); static void rpmsgfs_ns_unbind(FAR struct rpmsg_endpoint *ept); static int rpmsgfs_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); /**************************************************************************** * Private Data ****************************************************************************/ static const rpmsg_ept_cb g_rpmsgfs_handler[] = { [RPMSGFS_OPEN] = rpmsgfs_open_handler, [RPMSGFS_CLOSE] = rpmsgfs_close_handler, [RPMSGFS_READ] = rpmsgfs_read_handler, [RPMSGFS_WRITE] = rpmsgfs_write_handler, [RPMSGFS_LSEEK] = rpmsgfs_lseek_handler, [RPMSGFS_IOCTL] = rpmsgfs_ioctl_handler, [RPMSGFS_SYNC] = rpmsgfs_sync_handler, [RPMSGFS_DUP] = rpmsgfs_dup_handler, [RPMSGFS_FSTAT] = rpmsgfs_fstat_handler, [RPMSGFS_FTRUNCATE] = rpmsgfs_ftruncate_handler, [RPMSGFS_OPENDIR] = rpmsgfs_opendir_handler, [RPMSGFS_READDIR] = rpmsgfs_readdir_handler, [RPMSGFS_REWINDDIR] = rpmsgfs_rewinddir_handler, [RPMSGFS_CLOSEDIR] = rpmsgfs_closedir_handler, [RPMSGFS_STATFS] = rpmsgfs_statfs_handler, [RPMSGFS_UNLINK] = rpmsgfs_unlink_handler, [RPMSGFS_MKDIR] = rpmsgfs_mkdir_handler, [RPMSGFS_RMDIR] = rpmsgfs_rmdir_handler, [RPMSGFS_RENAME] = rpmsgfs_rename_handler, [RPMSGFS_STAT] = rpmsgfs_stat_handler, [RPMSGFS_FCHSTAT] = rpmsgfs_fchstat_handler, [RPMSGFS_CHSTAT] = rpmsgfs_chstat_handler, }; /**************************************************************************** * Private Functions ****************************************************************************/ static int rpmsgfs_attach_file(FAR struct rpmsgfs_server_s *priv, FAR struct file *filep) { FAR struct file **tmp; int ret; int i; int j; nxsem_wait(&priv->sem); for (i = 0; i < priv->file_rows; i++) { for (j = 0; j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; j++) { if (priv->files[i][j].f_inode == NULL) { memcpy(&priv->files[i][j], filep, sizeof(*filep)); ret = i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK + j; goto out; } } } tmp = kmm_realloc(priv->files, sizeof(FAR struct file *) * (i + 1)); DEBUGASSERT(tmp); if (tmp == NULL) { ret = -ENFILE; goto out; } tmp[i] = kmm_zalloc(sizeof(struct file) * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK); DEBUGASSERT(tmp[i]); if (tmp[i] == NULL) { kmm_free(tmp); ret = -ENFILE; goto out; } priv->files = tmp; priv->file_rows++; memcpy(&priv->files[i][0], filep, sizeof(*filep)); ret = i * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; out: nxsem_post(&priv->sem); return ret; } static int rpmsgfs_detach_file(FAR struct rpmsgfs_server_s *priv, int fd, FAR struct file *filep) { struct file *tfilep; if (fd < 0 || fd >= priv->file_rows * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK) { return -EBADF; } nxsem_wait(&priv->sem); tfilep = &priv->files[fd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK] [fd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]; memcpy(filep, tfilep, sizeof(*filep)); memset(tfilep, 0, sizeof(*tfilep)); nxsem_post(&priv->sem); return 0; } static FAR struct file *rpmsgfs_get_file( FAR struct rpmsgfs_server_s *priv, int fd) { FAR struct file *filep; if (fd < 0 || fd >= priv->file_rows * CONFIG_NFILE_DESCRIPTORS_PER_BLOCK) { return NULL; } nxsem_wait(&priv->sem); filep = &priv->files[fd / CONFIG_NFILE_DESCRIPTORS_PER_BLOCK] [fd % CONFIG_NFILE_DESCRIPTORS_PER_BLOCK]; nxsem_post(&priv->sem); return filep; } static int rpmsgfs_attach_dir(FAR struct rpmsgfs_server_s *priv, FAR void *dir) { FAR void **tmp; int i; nxsem_wait(&priv->sem); for (i = 1; i < priv->dir_nums; i++) { if (priv->dirs[i] == NULL) { priv->dirs[i] = dir; nxsem_post(&priv->sem); return i; } } tmp = kmm_realloc(priv->dirs, sizeof(FAR void *) * (priv->dir_nums + CONFIG_NFILE_DESCRIPTORS_PER_BLOCK)); DEBUGASSERT(tmp); if (tmp == NULL) { nxsem_post(&priv->sem); return -ENOMEM; } priv->dirs = tmp; priv->dir_nums += CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; priv->dirs[i] = dir; nxsem_post(&priv->sem); return i; } static void *rpmsgfs_detach_dir(FAR struct rpmsgfs_server_s *priv, int fd) { FAR void *dir = NULL; if (fd >= 1 && fd < priv->dir_nums) { nxsem_wait(&priv->sem); dir = priv->dirs[fd]; priv->dirs[fd] = NULL; nxsem_post(&priv->sem); } return dir; } static FAR void *rpmsgfs_get_dir( FAR struct rpmsgfs_server_s *priv, int fd) { FAR void *dir = NULL; if (fd >= 1 && fd < priv->dir_nums) { nxsem_wait(&priv->sem); dir = priv->dirs[fd]; nxsem_post(&priv->sem); } return dir; } static int rpmsgfs_open_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_open_s *msg = data; struct file file; int ret; ret = file_open(&file, msg->pathname, msg->flags, msg->mode); if (ret >= 0) { ret = rpmsgfs_attach_file(priv, &file); if (ret < 0) { file_close(&file); } } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_close_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_close_s *msg = data; struct file file; int ret; ret = rpmsgfs_detach_file(priv, msg->fd, &file); if (ret >= 0) { ret = file_close(&file); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_read_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_read_s *msg = data; FAR struct rpmsgfs_read_s *rsp; FAR struct file *filep; int ret = -ENOENT; uint32_t space; rsp = rpmsg_get_tx_payload_buffer(ept, &space, true); if (!rsp) { return -ENOMEM; } *rsp = *msg; space -= sizeof(*msg); if (space > msg->count) { space = msg->count; } filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_read(filep, rsp->buf, space); } rsp->header.result = ret; return rpmsg_send_nocopy(ept, rsp, (ret < 0 ? 0 : ret) + sizeof(*rsp)); } static int rpmsgfs_write_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_write_s *msg = data; FAR struct file *filep; int ret = -ENOENT; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_write(filep, msg->buf, msg->count); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_lseek_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_lseek_s *msg = data; FAR struct file *filep; int ret = -ENOENT; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_seek(filep, msg->offset, msg->whence); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_ioctl_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_ioctl_s *msg = data; FAR struct file *filep; int ret = -ENOENT; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_ioctl(filep, msg->request, msg->arglen > 0 ? (unsigned long)msg->buf : msg->arg); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_sync_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_sync_s *msg = data; FAR struct file *filep; int ret = -ENOENT; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_fsync(filep); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_dup_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_dup_s *msg = data; FAR struct file *filep; struct file newfile; int ret = -ENOENT; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_dup2(filep, &newfile); if (ret >= 0) { ret = rpmsgfs_attach_file(priv, &newfile); if (ret < 0) { file_close(&newfile); } } } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_fstat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_fstat_s *msg = data; FAR struct file *filep; int ret = -ENOENT; struct stat buf; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_fstat(filep, &buf); if (ret >= 0) { msg->buf = buf; } } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_ftruncate_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_ftruncate_s *msg = data; FAR struct file *filep; int ret = -ENOENT; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { ret = file_truncate(filep, msg->length); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_opendir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_opendir_s *msg = data; FAR void *dir; int ret = -ENOENT; dir = opendir(msg->pathname); if (dir) { ret = rpmsgfs_attach_dir(priv, dir); if (ret < 0) { closedir(dir); } } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_readdir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_readdir_s *msg = data; FAR struct dirent *entry; int ret = -ENOENT; FAR void *dir; dir = rpmsgfs_get_dir(priv, msg->fd); if (dir) { entry = readdir(dir); if (entry) { msg->type = entry->d_type; strcpy(msg->name, entry->d_name); len += strlen(entry->d_name) + 1; ret = 0; } } msg->header.result = ret; return rpmsg_send(ept, msg, len); } static int rpmsgfs_rewinddir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_rewinddir_s *msg = data; int ret = -ENOENT; FAR void *dir; dir = rpmsgfs_get_dir(priv, msg->fd); if (dir) { rewinddir(dir); ret = 0; } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_closedir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_closedir_s *msg = data; int ret = -ENOENT; FAR void *dir; dir = rpmsgfs_detach_dir(priv, msg->fd); if (dir) { ret = closedir(dir) ? -get_errno() : 0; } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_statfs_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_statfs_s *msg = data; struct statfs buf; int ret; ret = statfs(msg->pathname, &buf); if (ret) { ret = -get_errno(); } else { msg->buf = buf; } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_unlink_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_unlink_s *msg = data; msg->header.result = nx_unlink(msg->pathname); return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_mkdir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_mkdir_s *msg = data; int ret; ret = mkdir(msg->pathname, msg->mode); msg->header.result = ret ? -get_errno() : 0; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_rmdir_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_rmdir_s *msg = data; int ret; ret = rmdir(msg->pathname); msg->header.result = ret ? -get_errno() : 0; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_rename_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_rename_s *msg = data; FAR char *newpath; size_t oldlen; int ret; oldlen = (strlen(msg->pathname) + 1 + 0x7) & ~0x7; newpath = msg->pathname + oldlen; ret = rename(msg->pathname, newpath); msg->header.result = ret ? -get_errno() : 0; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_stat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_stat_s *msg = data; struct stat buf; int ret; ret = nx_stat(msg->pathname, &buf, 1); if (ret >= 0) { msg->buf = buf; } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_fchstat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_fchstat_s *msg = data; FAR struct file *filep; int ret = -ENOENT; struct stat buf; filep = rpmsgfs_get_file(priv, msg->fd); if (filep != NULL) { buf = msg->buf; ret = file_fchstat(filep, &buf, msg->flags); } msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static int rpmsgfs_chstat_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsgfs_chstat_s *msg = data; struct timespec times[2]; int ret = 0; if (msg->flags & CH_STAT_MODE) { ret = chmod(msg->pathname, msg->buf.st_mode); if (ret < 0) { ret = -get_errno(); goto out; } } if (msg->flags & (CH_STAT_UID | CH_STAT_GID)) { ret = chown(msg->pathname, msg->buf.st_uid, msg->buf.st_gid); if (ret < 0) { ret = -get_errno(); goto out; } } if (msg->flags & (CH_STAT_ATIME | CH_STAT_MTIME)) { if (msg->flags & CH_STAT_ATIME) { times[0] = msg->buf.st_atim; } else { times[0].tv_sec = 0; times[0].tv_nsec = UTIME_OMIT; } if (msg->flags & CH_STAT_MTIME) { times[1] = msg->buf.st_mtim; } else { times[1].tv_sec = 0; times[1].tv_nsec = UTIME_OMIT; } ret = utimens(msg->pathname, times); if (ret < 0) { ret = -get_errno(); goto out; } } out: msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(*msg)); } static void rpmsgfs_ns_bind(FAR struct rpmsg_device *rdev, FAR void *priv_, FAR const char *name, uint32_t dest) { FAR struct rpmsgfs_server_s *priv; int ret; if (strncmp(name, RPMSGFS_NAME_PREFIX, strlen(RPMSGFS_NAME_PREFIX))) { return; } priv = kmm_zalloc(sizeof(*priv)); if (!priv) { return; } priv->ept.priv = priv; nxsem_init(&priv->sem, 0, 1); ret = rpmsg_create_ept(&priv->ept, rdev, name, RPMSG_ADDR_ANY, dest, rpmsgfs_ept_cb, rpmsgfs_ns_unbind); if (ret) { nxsem_destroy(&priv->sem); kmm_free(priv); } } static void rpmsgfs_ns_unbind(FAR struct rpmsg_endpoint *ept) { FAR struct rpmsgfs_server_s *priv = ept->priv; int i; int j; for (i = 0; i < priv->file_rows; i++) { for (j = 0; j < CONFIG_NFILE_DESCRIPTORS_PER_BLOCK; j++) { if (priv->files[i][j].f_inode) { file_close(&priv->files[i][j]); } } kmm_free(priv->files[i]); } for (i = 0; i < priv->dir_nums; i++) { if (priv->dirs[i]) { closedir(priv->dirs[i]); } } rpmsg_destroy_ept(&priv->ept); nxsem_destroy(&priv->sem); kmm_free(priv->files); kmm_free(priv->dirs); kmm_free(priv); } static int rpmsgfs_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { struct rpmsgfs_header_s *header = data; uint32_t command = header->command; if (command < ARRAY_SIZE(g_rpmsgfs_handler)) { return g_rpmsgfs_handler[command](ept, data, len, src, priv); } return -EINVAL; } int rpmsgfs_server_init(void) { return rpmsg_register_callback(NULL, NULL, NULL, rpmsgfs_ns_bind); }