fn create()

in src/fs.rs [442:584]


    fn create(
        &mut self,
        req: &Request<'_>,
        parent: u64,
        name: &OsStr,
        mode: u32,
        _umask: u32,
        flags: i32,
        reply: ReplyCreate,
    ) {
        debug!(
            "create(parent_dir = {}, path = {:#?}, mode = {}, flags = {:o})",
            parent, name, mode, flags
        );

        let file_type = (mode as libc::mode_t) & libc::S_IFMT;

        if file_type != libc::S_IFREG && file_type != libc::S_IFDIR {
            warn!(
                "create called for file_type {}. But we only handle FILE/DIR",
                file_type
            );
            reply.error(libc::EINVAL);
            return;
        }

        // Grab a scoped lock for the directory map (we'll update the directory)
        let mut dir_map_lock = self.directory_map.write().unwrap();

        // Find the parent in the directory map.
        let parent_ent = dir_map_lock.get_mut(&parent);

        // NOTE(boulos): I think FUSE does this check already.
        if parent_ent.is_none() {
            debug!(" -- warning/error:fuse_create called w/o parent directory");
            reply.error(ENOTDIR);
            return;
        }

        let parent_dir = parent_ent.unwrap();
        let dir_entries = &mut parent_dir.entries;

        let search_name = name.to_str().unwrap().to_string();
        let full_name = match parent_dir.name.len() {
            // Don't include the leading / for the root directory.
            0 => search_name.clone(),
            _ => format!("{}/{}", parent_dir.name, search_name),
        };

        #[cfg(debug)]
        // FUSE isn't supposed to call this for existing entries. Double check in debug mode.
        for child_pair in dir_entries.iter() {
            if child_pair.0 == search_name {
                debug!(" -- warning/error: File {} already exists!", search_name);
                reply.error(EEXIST);
                return;
            }
        }

        // Make a new inode for our new file or directory.
        let inode = self.get_inode();
        let now = SystemTime::now();

        let kind = match file_type {
            libc::S_IFREG => FileType::RegularFile,
            libc::S_IFDIR => FileType::Directory,
            _ => unreachable!(),
        };

        // If it's going to be a regular file, try to initiate the
        // Upload. If this fails, we've burned an inode, but whatever.
        let fh = match kind {
            FileType::RegularFile => {
                let create_result = self.tokio_rt.block_on(async {
                    super::gcs::create_object_with_client(
                        &self.gcs_client,
                        &self.gcs_bucket,
                        &full_name,
                    )
                    .await
                });

                if create_result.is_err() {
                    // We failed (and warned internally). Bubble up an EIO.
                    reply.error(libc::EIO);
                    return;
                }

                // Make ourselves a file handle!
                self.make_fh(create_result.unwrap())
            }
            // Directories don't need FHs for now.
            _ => 0,
        };

        let attrs: FileAttr = FileAttr {
            ino: inode,
            size: 0,
            blocks: 0,
            atime: now,
            mtime: now,
            ctime: now,
            crtime: now,
            kind,
            perm: 0o755, /* We could use mode, but whatever */
            nlink: match kind {
                FileType::RegularFile => 1,
                FileType::Directory => 2,
                _ => unreachable!(),
            },
            uid: req.uid(),
            gid: req.gid(),
            rdev: 0,
            flags: 0,
            blksize: HARDCODED_BLOCKSIZE,
        };

        // Put the node into our inode map
        self.inode_to_attr.write().unwrap().insert(inode, attrs);

        // Put the node into our parent directory listing.
        dir_entries.push((search_name.clone(), inode));

        if kind == FileType::Directory {
            // Make our own PsuedoDir
            let sub_entries: Vec<(String, Inode)> = vec![
                (String::from("."), inode), /* Self link */
                (String::from(".."), parent as Inode),
            ];

            dir_map_lock.insert(
                inode,
                PsuedoDir {
                    name: search_name,
                    entries: sub_entries,
                },
            );
        } else {
            // Otherwise, maybe prepare a stub Object?
        }

        reply.created(&TTL_30s, &attrs, 0, fh, 0);
    }