/** * FSAL_mknode: * Create a special object in the filesystem. * Not supported upon HPSS. * * \return ERR_FSAL_NOTSUPP. */ fsal_status_t GPFSFSAL_mknode(fsal_handle_t * parentdir_handle, /* IN */ fsal_name_t * p_node_name, /* IN */ fsal_op_context_t * p_context, /* IN */ fsal_accessmode_t accessmode, /* IN */ fsal_nodetype_t nodetype, /* IN */ fsal_dev_t * dev, /* IN */ fsal_handle_t * p_object_handle, /* OUT (handle to the created node) */ fsal_attrib_list_t * node_attributes /* [ IN/OUT ] */ ) { int rc, errsv; int setgid_bit = 0; fsal_status_t status; int fd, newfd; mode_t unix_mode = 0; dev_t unix_dev = 0; fsal_accessflags_t access_mask = 0; fsal_attrib_list_t parent_dir_attrs; /* sanity checks. * note : link_attributes is optional. */ if(!parentdir_handle || !p_context || !p_node_name) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_mknode); unix_mode = fsal2unix_mode(accessmode); /* Apply umask */ unix_mode = unix_mode & ~global_fs_info.umask; switch (nodetype) { case FSAL_TYPE_BLK: if(!dev) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_mknode); unix_mode |= S_IFBLK; unix_dev = (dev->major << 8) | (dev->minor & 0xFF); break; case FSAL_TYPE_CHR: if(!dev) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_mknode); unix_mode |= S_IFCHR; unix_dev = (dev->major << 8) | (dev->minor & 0xFF); break; case FSAL_TYPE_SOCK: unix_mode |= S_IFSOCK; break; case FSAL_TYPE_FIFO: unix_mode |= S_IFIFO; break; default: LogMajor(COMPONENT_FSAL, "Invalid node type in FSAL_mknode: %d", nodetype); Return(ERR_FSAL_INVAL, 0, INDEX_FSAL_mknode); } /* build the directory path */ status = fsal_internal_handle2fd(p_context, parentdir_handle, &fd, O_RDONLY | O_DIRECTORY); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_mknode); /* retrieve directory attributes */ parent_dir_attrs.asked_attributes = GPFS_SUPPORTED_ATTRIBUTES; status = GPFSFSAL_getattrs(parentdir_handle, p_context, &parent_dir_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_mknode); /* Check the user can write in the directory, and check weither the setgid bit on the directory */ if(fsal2unix_mode(parent_dir_attrs.mode) & S_ISGID) setgid_bit = 1; /* Set both mode and ace4 mask */ access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK | FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = fsal_internal_testAccess(p_context, access_mask, NULL, &parent_dir_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_mknode); /* creates the node, then stats it */ rc = mknodat(fd, p_node_name->name, unix_mode, unix_dev); errsv = errno; if(rc) { close(fd); Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_mknode); } /* WARNING: * After creating the new node, the node name could have been changed. * This is a race condition. However only root creates new nodes. This * is an unlikely race condition, but hopefully can be fixed someday. */ if(FSAL_IS_ERROR(status = fsal_internal_get_handle_at(fd, p_node_name, p_object_handle))) { close(fd); ReturnStatus(status, INDEX_FSAL_mknode); } if(FSAL_IS_ERROR(status = fsal_internal_handle2fd_at(fd, p_object_handle, &newfd, O_RDONLY | O_NOFOLLOW))) { close(fd); ReturnStatus(status, INDEX_FSAL_mknode); } /* the node has been created */ /* chown the file to the current user/group */ if(p_context->credential.user != geteuid()) { /* if the setgid_bit was set on the parent directory, do not change the group of the created file, because it's already the parentdir's group */ rc = fchown(newfd, p_context->credential.user, setgid_bit ? -1 : (int)p_context->credential.group); errsv = errno; if(rc) { close(fd); close(newfd); Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_mknode); } } close(fd); close(newfd); /* Fills the attributes if needed */ if(node_attributes) { status = GPFSFSAL_getattrs(p_object_handle, p_context, node_attributes); /* on error, we set a special bit in the mask. */ if(FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(node_attributes->asked_attributes); FSAL_SET_MASK(node_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR); } } /* Finished */ Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_mknode); }
/** * FSAL_mkdir: * Create a directory. * * \param parent_directory_handle (input): * Handle of the parent directory where * the subdirectory is to be created. * \param p_dirname (input): * Pointer to the name of the directory to be created. * \param cred (input): * Authentication context for the operation (user,...). * \param accessmode (input): * Mode for the directory to be created. * (the umask defined into the FSAL configuration file * will be applied on it). * \param object_handle (output): * Pointer to the handle of the created directory. * \param object_attributes (optionnal input/output): * The attributes of the created directory. * As input, it defines the attributes that the caller * wants to retrieve (by positioning flags into this structure) * and the output is built considering this input * (it fills the structure according to the flags it contains). * May be NULL. * * \return Major error codes : * - ERR_FSAL_NO_ERROR (no error) * - Another error code if an error occured. */ fsal_status_t GPFSFSAL_mkdir(fsal_handle_t * p_parent_directory_handle, /* IN */ fsal_name_t * p_dirname, /* IN */ fsal_op_context_t * p_context, /* IN */ fsal_accessmode_t accessmode, /* IN */ fsal_handle_t * p_object_handle, /* OUT */ fsal_attrib_list_t * p_object_attributes /* [ IN/OUT ] */ ) { int rc, errsv; int setgid_bit = 0; mode_t unix_mode; fsal_status_t status; int fd, newfd; fsal_accessflags_t access_mask = 0; fsal_attrib_list_t parent_dir_attrs; /* sanity checks. * note : object_attributes is optional. */ if(!p_parent_directory_handle || !p_context || !p_object_handle || !p_dirname) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_mkdir); /* convert FSAL mode to unix mode. */ unix_mode = fsal2unix_mode(accessmode); /* Apply umask */ unix_mode = unix_mode & ~global_fs_info.umask; TakeTokenFSCall(); status = fsal_internal_handle2fd(p_context, p_parent_directory_handle, &fd, O_RDONLY); ReleaseTokenFSCall(); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_mkdir); /* get directory metadata */ parent_dir_attrs.asked_attributes = GPFS_SUPPORTED_ATTRIBUTES; status = GPFSFSAL_getattrs(p_parent_directory_handle, p_context, &parent_dir_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_create); /* Check the user can write in the directory, and check the setgid bit on the directory */ if(fsal2unix_mode(parent_dir_attrs.mode) & S_ISGID) setgid_bit = 1; /* Set both mode and ace4 mask */ access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK | FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_SUBDIRECTORY); status = fsal_internal_testAccess(p_context, access_mask, NULL, &parent_dir_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_mkdir); /* build new entry path */ /* creates the directory and get its handle */ TakeTokenFSCall(); rc = mkdirat(fd, p_dirname->name, unix_mode); errsv = errno; if(rc) { close(fd); ReleaseTokenFSCall(); Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_mkdir); } ReleaseTokenFSCall(); /**** * There is a race here between mkdir creation and the open, not * sure there is any way to close it in practice. */ /* get the new handle */ TakeTokenFSCall(); status = fsal_internal_get_handle_at(fd, p_dirname, p_object_handle); ReleaseTokenFSCall(); if(FSAL_IS_ERROR(status)) { close(fd); ReturnStatus(status, INDEX_FSAL_mkdir); } TakeTokenFSCall(); status = fsal_internal_handle2fd_at(fd, p_object_handle, &newfd, O_RDONLY | O_DIRECTORY); ReleaseTokenFSCall(); if(FSAL_IS_ERROR(status)) { close(fd); ReturnStatus(status, INDEX_FSAL_mkdir); } /* the directory has been created */ /* chown the dir to the current user/group */ if(p_context->credential.user != geteuid()) { TakeTokenFSCall(); /* if the setgid_bit was set on the parent directory, do not change the group of the created file, because it's already the parentdir's group */ rc = fchown(newfd, p_context->credential.user, setgid_bit ? -1 : (int)p_context->credential.group); errsv = errno; ReleaseTokenFSCall(); if(rc) { close(fd); close(newfd); Return(posix2fsal_error(errsv), errsv, INDEX_FSAL_mkdir); } } close(fd); close(newfd); /* retrieve file attributes */ if(p_object_attributes) { status = GPFSFSAL_getattrs(p_object_handle, p_context, p_object_attributes); /* on error, we set a special bit in the mask. */ if(FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(p_object_attributes->asked_attributes); FSAL_SET_MASK(p_object_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR); } } /* OK */ Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_mkdir); }
/** * FSAL_lookup : * Looks up for an object into a directory. * * Note : if parent handle and filename are NULL, * this retrieves root's handle. * * \param parent_directory_handle (input) * Handle of the parent directory to search the object in. * \param filename (input) * The name of the object to find. * \param p_context (input) * Authentication context for the operation (user,...). * \param object_handle (output) * The handle of the object corresponding to filename. * \param object_attributes (optional input/output) * Pointer to the attributes of the object we found. * As input, it defines the attributes that the caller * wants to retrieve (by positioning flags into this structure) * and the output is built considering this input * (it fills the structure according to the flags it contains). * It can be NULL (increases performances). * * \return - ERR_FSAL_NO_ERROR, if no error. * - Another error code else. * */ fsal_status_t PTFSAL_lookup(const struct req_op_context *p_context, struct fsal_obj_handle *parent, const char *p_filename, struct attrlist *p_object_attr, ptfsal_handle_t *fh) { fsal_status_t status; int parent_fd; struct attrlist *parent_dir_attrs; fsi_stat_struct buffstat; int rc; struct pt_fsal_obj_handle *parent_hdl; FSI_TRACE(FSI_DEBUG, "Begin##################################\n"); if (p_filename != NULL) FSI_TRACE(FSI_DEBUG, "FSI - fsal_lookup file [%s]\n", p_filename); if (parent != NULL) FSI_TRACE(FSI_DEBUG, "FSI - fsal_lookup parent dir\n"); if (!parent || !p_filename) return fsalstat(ERR_FSAL_FAULT, 0); parent_hdl = container_of(parent, struct pt_fsal_obj_handle, obj_handle); parent_dir_attrs = &parent_hdl->obj_handle.attributes; /* get directory metadata */ parent_dir_attrs->mask = p_context->fsal_export->exp_ops. fs_supported_attrs(p_context->fsal_export); status = fsal_internal_handle2fd_at(p_context, parent_hdl, &parent_fd, O_RDONLY); if (FSAL_IS_ERROR(status)) return status; FSI_TRACE(FSI_DEBUG, "FSI - lookup parent directory type = %d\n", parent_dir_attrs->type); /* Be careful about junction crossing, symlinks, hardlinks,... */ switch (parent_dir_attrs->type) { case DIRECTORY: /* OK */ break; case REGULAR_FILE: case SYMBOLIC_LINK: /* not a directory */ pt_close(parent); return fsalstat(ERR_FSAL_NOTDIR, 0); default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } /* get file handle, it it exists */ /* This might be a race, but it's the best we can currently do */ rc = ptfsal_stat_by_parent_name(p_context, parent_hdl, p_filename, &buffstat); if (rc < 0) { ptfsal_closedir_fd(p_context, p_context->fsal_export, parent_fd); return fsalstat(ERR_FSAL_NOENT, errno); } memset(fh->data.handle.f_handle, 0, sizeof(fh->data.handle.f_handle)); memcpy(&fh->data.handle.f_handle, &buffstat.st_persistentHandle.handle, FSI_CCL_PERSISTENT_HANDLE_N_BYTES); fh->data.handle.handle_size = FSI_CCL_PERSISTENT_HANDLE_N_BYTES; fh->data.handle.handle_key_size = OPENHANDLE_KEY_LEN; fh->data.handle.handle_version = OPENHANDLE_VERSION; fh->data.handle.handle_type = posix2fsal_type(buffstat.st_mode); /* get object attributes */ if (p_object_attr) { p_object_attr->mask = p_context->fsal_export->exp_ops. fs_supported_attrs(p_context->fsal_export); status = PTFSAL_getattrs(p_context->fsal_export, p_context, fh, p_object_attr); if (FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(p_object_attr->mask); FSAL_SET_MASK(p_object_attr->mask, ATTR_RDATTR_ERR); } } ptfsal_closedir_fd(p_context, p_context->fsal_export, parent_fd); FSI_TRACE(FSI_DEBUG, "End##################################\n"); /* lookup complete ! */ return fsalstat(ERR_FSAL_NO_ERROR, 0); }
fsal_status_t GPFSFSAL_readdir(gpfsfsal_dir_t * p_dir_descriptor, /* IN */ gpfsfsal_cookie_t start_position, /* IN */ fsal_attrib_mask_t get_attr_mask, /* IN */ fsal_mdsize_t buffersize, /* IN */ fsal_dirent_t * p_pdirent, /* OUT */ gpfsfsal_cookie_t * p_end_position, /* OUT */ fsal_count_t * p_nb_entries, /* OUT */ fsal_boolean_t * p_end_of_dir /* OUT */ ) { fsal_status_t st; fsal_count_t max_dir_entries; fsal_name_t entry_name; char buff[BUF_SIZE]; struct linux_dirent *dp = NULL; int bpos = 0; int tmpfd = 0; char d_type; struct stat buffstat; int rc = 0; memset(buff, 0, BUF_SIZE); memset(&entry_name, 0, sizeof(fsal_name_t)); /*****************/ /* sanity checks */ /*****************/ if(!p_dir_descriptor || !p_pdirent || !p_end_position || !p_nb_entries || !p_end_of_dir) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_readdir); max_dir_entries = (buffersize / sizeof(fsal_dirent_t)); /***************************/ /* seek into the directory */ /***************************/ errno = 0; if(start_position.data.cookie == 0) { //rewinddir(p_dir_descriptor->p_dir); rc = errno; } else { //seekdir(p_dir_descriptor->p_dir, start_position.cookie); rc = errno; } if(rc) Return(posix2fsal_error(rc), rc, INDEX_FSAL_readdir); /************************/ /* browse the directory */ /************************/ *p_nb_entries = 0; while(*p_nb_entries < max_dir_entries) { /***********************/ /* read the next entry */ /***********************/ TakeTokenFSCall(); rc = syscall(SYS_getdents, p_dir_descriptor->fd, buff, BUF_SIZE); ReleaseTokenFSCall(); if(rc < 0) { rc = errno; Return(posix2fsal_error(rc), rc, INDEX_FSAL_readdir); } /* End of directory */ if(rc == 0) { *p_end_of_dir = 1; break; } /***********************************/ /* Get information about the entry */ /***********************************/ for(bpos = 0; bpos < rc;) { dp = (struct linux_dirent *)(buff + bpos); d_type = *(buff + bpos + dp->d_reclen - 1); /** @todo not used for the moment. Waiting for information on symlink management */ bpos += dp->d_reclen; /* LogFullDebug(COMPONENT_FSAL, "\tino=%8ld|%8lx off=%d|%x reclen=%d|%x name=%s|%d", dp->d_ino, dp->d_ino, (int)dp->d_off, (int)dp->d_off, dp->d_reclen, dp->d_reclen, dp->d_name, (int)dp->d_name[0] ) ; */ if(!(*p_nb_entries < max_dir_entries)) break; /* skip . and .. */ if(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; /* build the full path of the file into "fsalpath */ if(FSAL_IS_ERROR (st = FSAL_str2name(dp->d_name, FSAL_MAX_NAME_LEN, &(p_pdirent[*p_nb_entries].name)))) ReturnStatus(st, INDEX_FSAL_readdir); d_type = DT_UNKNOWN; // TODO: there is a race here, because between handle fetch // and open at things might change. we need to figure out if there // is another way to open without the pcontext strncpy(entry_name.name, dp->d_name, sizeof(entry_name.name)); entry_name.len = strlen(entry_name.name); if(FSAL_IS_ERROR (st = fsal_internal_get_handle_at(p_dir_descriptor->fd, &entry_name, &(p_pdirent[*p_nb_entries].handle)))) ReturnStatus(st, INDEX_FSAL_readdir); if(FSAL_IS_ERROR (st = fsal_internal_handle2fd_at(p_dir_descriptor->fd, &(p_pdirent[*p_nb_entries].handle), &tmpfd, O_RDONLY | O_NOFOLLOW))) { if(errno != ELOOP) /* ( p_dir_descriptor->fd, dp->d_name) is not a symlink */ ReturnStatus(st, INDEX_FSAL_readdir); else d_type = DT_LNK; } /* get object handle */ TakeTokenFSCall(); if(d_type != DT_LNK) { close(tmpfd); } else { if(fstatat(p_dir_descriptor->fd, dp->d_name, &buffstat, AT_SYMLINK_NOFOLLOW) < 0) { ReleaseTokenFSCall(); Return(posix2fsal_error(errno), errno, INDEX_FSAL_readdir); } p_pdirent[*p_nb_entries].attributes.asked_attributes = get_attr_mask; st = posix2fsal_attributes(&buffstat, &p_pdirent[*p_nb_entries].attributes); if(FSAL_IS_ERROR(st)) { ReleaseTokenFSCall(); FSAL_CLEAR_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes); FSAL_SET_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes, FSAL_ATTR_RDATTR_ERR); ReturnStatus(st, INDEX_FSAL_getattrs); } } ReleaseTokenFSCall(); if(FSAL_IS_ERROR(st)) ReturnStatus(st, INDEX_FSAL_readdir); /************************ * Fills the attributes * ************************/ if(d_type != DT_LNK) { p_pdirent[*p_nb_entries].attributes.asked_attributes = get_attr_mask; st = GPFSFSAL_getattrs(&(p_pdirent[*p_nb_entries].handle), &p_dir_descriptor->context, &p_pdirent[*p_nb_entries].attributes); if(FSAL_IS_ERROR(st)) { FSAL_CLEAR_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes); FSAL_SET_MASK(p_pdirent[*p_nb_entries].attributes.asked_attributes, FSAL_ATTR_RDATTR_ERR); } } p_pdirent[*p_nb_entries].cookie.data.cookie = dp->d_off; p_pdirent[*p_nb_entries].nextentry = NULL; if(*p_nb_entries) p_pdirent[*p_nb_entries - 1].nextentry = &(p_pdirent[*p_nb_entries]); (*p_end_position) = p_pdirent[*p_nb_entries].cookie; (*p_nb_entries)++; } /* for */ } /* While */ Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_readdir); }
/** * read_dirents * read the directory and call through the callback function for * each entry. * @param dir_hdl [IN] the directory to read * @param whence [IN] where to start (next) * @param dir_state [IN] pass thru of state to callback * @param cb [IN] callback function * @param eof [OUT] eof marker true == end of dir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct gpfs_fsal_obj_handle *myself; int dirfd; fsal_status_t status; off_t seekloc = 0; int bpos, cnt, nread; struct dirent64 *dentry; char buf[BUF_SIZE]; struct gpfs_filesystem *gpfs_fs; if (whence != NULL) seekloc = (off_t) *whence; myself = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_fs = dir_hdl->fs->private_data; status = fsal_internal_handle2fd_at(gpfs_fs->root_fd, myself->handle, &dirfd, O_RDONLY | O_DIRECTORY, 0); if (dirfd < 0) return status; seekloc = lseek(dirfd, seekloc, SEEK_SET); if (seekloc < 0) { retval = errno; fsal_error = posix2fsal_error(retval); goto done; } cnt = 0; do { nread = syscall(SYS_getdents64, dirfd, buf, BUF_SIZE); if (nread < 0) { retval = errno; fsal_error = posix2fsal_error(retval); goto done; } if (nread == 0) break; for (bpos = 0; bpos < nread;) { struct fsal_obj_handle *hdl; struct attrlist attrs; bool cb_rc; dentry = (struct dirent64 *)(buf + bpos); if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0) goto skip; /* must skip '.' and '..' */ fsal_prepare_attrs(&attrs, attrmask); status = lookup(dir_hdl, dentry->d_name, &hdl, &attrs); if (FSAL_IS_ERROR(status)) { fsal_error = status.major; goto done; } /* callback to cache inode */ cb_rc = cb(dentry->d_name, hdl, &attrs, dir_state, (fsal_cookie_t) dentry->d_off); fsal_release_attrs(&attrs); if (!cb_rc) goto done; skip: bpos += dentry->d_reclen; cnt++; } } while (nread > 0); *eof = true; done: close(dirfd); return fsalstat(fsal_error, retval); }