/**************************************************************************** * fs/shm/shm_open.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 "inode/inode.h" #include "notify/notify.h" #include "shm/shmfs.h" /**************************************************************************** * Private Functions ****************************************************************************/ static int file_shm_open(FAR struct file *shm, FAR const char *name, int oflags, mode_t mode) { FAR struct inode *inode; struct inode_search_s desc; char fullpath[sizeof(CONFIG_FS_SHMFS_VFS_PATH) + CONFIG_NAME_MAX + 2]; int ret; /* Make sure that a non-NULL name is supplied */ if (!shm || !name) { return -EINVAL; } /* Remove any number of leading '/' */ while (*name == '/') { name++; } /* Empty name supplied? */ if (*name == '\0') { return -EINVAL; } /* Name too long? */ if (strnlen(name, CONFIG_NAME_MAX + 1) > CONFIG_NAME_MAX) { return -ENAMETOOLONG; } /* Get the full path to the shm object */ snprintf(fullpath, sizeof(fullpath), CONFIG_FS_SHMFS_VFS_PATH "/%s", name); /* Get the inode for this shm object */ SETUP_SEARCH(&desc, fullpath, false); inode_lock(); ret = inode_find(&desc); if (ret >= 0) { /* Something exists at this path. Get the search results */ inode = desc.node; /* Verify that the inode is an shm object */ if (!INODE_IS_SHM(inode)) { ret = -EINVAL; inode_release(inode); goto errout_with_sem; } /* It exists and is an shm object. Check if the caller wanted to * create a new object with this name. */ if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { ret = -EEXIST; inode_release(inode); goto errout_with_sem; } /* If the shared memory object already exists, truncate it to * zero bytes. */ if ((oflags & O_TRUNC) == O_TRUNC && inode->i_private != NULL) { shmfs_free_object(inode->i_private); inode->i_private = NULL; inode->i_size = 0; } } else { /* The shm does not exist. Were we asked to create it? */ if ((oflags & O_CREAT) == 0) { /* The shm does not exist and O_CREAT is not set */ ret = -ENOENT; goto errout_with_sem; } /* Create an inode in the pseudo-filesystem at this path */ ret = inode_reserve(fullpath, mode, &inode); if (ret < 0) { goto errout_with_sem; } INODE_SET_SHM(inode); inode->u.i_ops = &g_shmfs_operations; inode->i_private = NULL; atomic_fetch_add(&inode->i_crefs, 1); } /* Associate the inode with a file structure */ memset(shm, 0, sizeof(*shm)); shm->f_oflags = oflags | O_CLOEXEC | O_NOFOLLOW; shm->f_inode = inode; errout_with_sem: inode_unlock(); RELEASE_SEARCH(&desc); #ifdef CONFIG_FS_NOTIFY if (ret >= 0) { notify_open(fullpath, oflags); } #endif return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ int shm_open(FAR const char *name, int oflag, mode_t mode) { struct file shm; int ret; ret = file_shm_open(&shm, name, oflag, mode); if (ret < 0) { set_errno(-ret); return ERROR; } ret = file_allocate(shm.f_inode, shm.f_oflags, shm.f_pos, shm.f_priv, 0, false); if (ret < 0) { set_errno(-ret); file_close(&shm); return ERROR; } return ret; }