+/* Check that a string is a valid file name. We require that it be valid
+ * UTF-8, that it not be empty, and that it not contain embedded forward
+ * slashes. Also checks that the length of the string is not more than the
+ * maximum allowed length. This function does allow the names "." and "..".
+ * Returns TRUE if the string is allowed as a filename. */
+gboolean validate_filename(const char *filename)
+{
+ if (filename == NULL || filename[0] == '\0')
+ return FALSE;
+ if (strlen(filename) > 255)
+ return FALSE;
+ if (!g_utf8_validate(filename, -1, NULL))
+ return FALSE;
+ if (strchr(filename, '/') != NULL)
+ return FALSE;
+ return TRUE;
+}
+
+/* Look up a BlueSkyInode given an NFS filehandle. Returns NULL if the
+ * filehandle is invalid. */
+BlueSkyInode *lookup_fh(nfs_fh3 *fh)
+{
+ BlueSkyInode *inode = NULL;
+ if (fh->data.data_len == 8) {
+ uint64_t inum = GUINT64_FROM_BE(*(uint64_t *)(fh->data.data_val));
+ inode = bluesky_get_inode(fs, inum);
+ }
+ return inode;
+}
+
+int64_t decode_nfstime3(nfstime3 *time)
+{
+ int64_t result = (int64_t)time->seconds * 1000000;
+ result += time->nseconds / 1000;
+ return result;
+}
+
+void set_attributes(BlueSkyInode *inode, sattr3 *attributes)
+{
+ int64_t now = bluesky_get_current_time();
+
+ if (attributes->mode.set) {
+ inode->mode = attributes->mode.set_uint32_u.val;
+ }
+
+ if (attributes->uid.set) {
+ inode->uid = attributes->uid.set_uint32_u.val;
+ }
+
+ if (attributes->gid.set) {
+ inode->gid = attributes->gid.set_uint32_u.val;
+ }
+
+ if (attributes->size.set) {
+ if (inode->type == BLUESKY_REGULAR) {
+ bluesky_file_truncate(inode, attributes->size.set_uint64_u.val);
+ inode->mtime = now;
+ }
+ }
+
+ switch (attributes->atime.set) {
+ case DONT_CHANGE:
+ break;
+ case SET_TO_SERVER_TIME:
+ inode->atime = now;
+ break;
+ case SET_TO_CLIENT_TIME:
+ inode->atime = decode_nfstime3(&attributes->atime.set_time_u.time);
+ break;
+ }
+
+ switch (attributes->mtime.set) {
+ case DONT_CHANGE:
+ break;
+ case SET_TO_SERVER_TIME:
+ inode->mtime = now;
+ break;
+ case SET_TO_CLIENT_TIME:
+ inode->mtime = decode_nfstime3(&attributes->mtime.set_time_u.time);
+ break;
+ }
+
+ inode->ctime = now;
+ inode->change_count++;
+}
+
+/* Copy inode attributes into NFS response. The BlueSkyInode should be locked
+ * by the caller. */
+void encode_fattr3(struct fattr3 *result, BlueSkyInode *inode)