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);
}