 b48b037fbe
			
		
	
	
		b48b037fbe
		
	
	
	
	
		
			
			servers/hgfs/hgfs_server => servers/hgfs servers/hgfs/libhgfs => lib/libhgfs servers/rs/service => commands/service drivers/memory/memory_driver => drivers/memory drivers/memory/ramdisk => drivers/ramdisk
		
			
				
	
	
		
			404 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* This file contains directory entry related file system call handlers.
 | |
|  *
 | |
|  * The entry points into this file are:
 | |
|  *   do_create		perform the CREATE file system call
 | |
|  *   do_mkdir		perform the MKDIR file system call
 | |
|  *   do_unlink		perform the UNLINK file system call
 | |
|  *   do_rmdir		perform the RMDIR file system call
 | |
|  *   do_rename		perform the RENAME file system call
 | |
|  *
 | |
|  * Created:
 | |
|  *   April 2009 (D.C. van Moolenbroek)
 | |
|  */
 | |
| 
 | |
| #include "inc.h"
 | |
| 
 | |
| #include <fcntl.h>
 | |
| 
 | |
| FORWARD _PROTOTYPE( int force_remove, (char *path, int dir)		);
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_create				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int do_create()
 | |
| {
 | |
| /* Create a new file.
 | |
|  */
 | |
|   char path[PATH_MAX], name[NAME_MAX+1];
 | |
|   struct inode *parent, *ino;
 | |
|   struct hgfs_attr attr;
 | |
|   hgfs_file_t handle;
 | |
|   int r;
 | |
| 
 | |
|   /* We cannot create files on a read-only file system. */
 | |
|   if (state.read_only)
 | |
| 	return EROFS;
 | |
| 
 | |
|   /* Get path, name, parent inode and possibly inode for the given path. */
 | |
|   if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
 | |
| 
 | |
|   if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   /* Are we going to need a new inode upon success?
 | |
|    * Then make sure there is one available before trying anything.
 | |
|    */
 | |
|   if (ino == NULL || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
 | |
| 	if (!have_free_inode()) {
 | |
| 		if (ino != NULL)
 | |
| 			put_inode(ino);
 | |
| 
 | |
| 		return ENFILE;
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   /* Perform the actual create call. */
 | |
|   r = hgfs_open(path, O_CREAT | O_EXCL | O_RDWR, m_in.REQ_MODE, &handle);
 | |
| 
 | |
|   if (r != OK) {
 | |
| 	/* Let's not try to be too clever with error codes here. If something
 | |
| 	 * is wrong with the directory, we'll find out later anyway.
 | |
| 	 */
 | |
| 
 | |
| 	if (ino != NULL)
 | |
| 		put_inode(ino);
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   /* Get the created file's attributes. */
 | |
|   attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
 | |
|   r = hgfs_getattr(path, &attr);
 | |
| 
 | |
|   /* If this fails, or returns a directory, we have a problem. This
 | |
|    * scenario is in fact possible with race conditions.
 | |
|    * Simulate a close and return a somewhat appropriate error.
 | |
|    */
 | |
|   if (r != OK || S_ISDIR(attr.a_mode)) {
 | |
| 	printf("HGFS: lost file after creation!\n");
 | |
| 
 | |
| 	hgfs_close(handle);
 | |
| 
 | |
| 	if (ino != NULL) {
 | |
| 		del_dentry(ino);
 | |
| 
 | |
| 		put_inode(ino);
 | |
| 	}
 | |
| 
 | |
| 	return (r == OK) ? EEXIST : r;
 | |
|   }
 | |
| 
 | |
|   /* We do assume that the HGFS open(O_CREAT|O_EXCL) did its job.
 | |
|    * If we previousy found an inode, get rid of it now. It's old.
 | |
|    */
 | |
|   if (ino != NULL) {
 | |
| 	del_dentry(ino);
 | |
| 
 | |
| 	put_inode(ino);
 | |
|   }
 | |
| 
 | |
|   /* Associate the open file handle with an inode, and reply with its details.
 | |
|    */
 | |
|   ino = get_free_inode();
 | |
| 
 | |
|   assert(ino != NULL); /* we checked before whether we had a free one */
 | |
| 
 | |
|   ino->i_file = handle;
 | |
|   ino->i_flags = I_HANDLE;
 | |
| 
 | |
|   add_dentry(parent, name, ino);
 | |
| 
 | |
|   m_out.RES_INODE_NR = INODE_NR(ino);
 | |
|   m_out.RES_MODE = get_mode(ino, attr.a_mode);
 | |
|   m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
 | |
|   m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
 | |
|   m_out.RES_UID = opt.uid;
 | |
|   m_out.RES_GID = opt.gid;
 | |
|   m_out.RES_DEV = NO_DEV;
 | |
| 
 | |
|   return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_mkdir				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int do_mkdir()
 | |
| {
 | |
| /* Make a new directory.
 | |
|  */
 | |
|   char path[PATH_MAX], name[NAME_MAX+1];
 | |
|   struct inode *parent, *ino;
 | |
|   int r;
 | |
| 
 | |
|   /* We cannot create directories on a read-only file system. */
 | |
|   if (state.read_only)
 | |
| 	return EROFS;
 | |
| 
 | |
|   /* Get the path string and possibly an inode for the given path. */
 | |
|   if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
 | |
| 
 | |
|   if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   /* Perform the actual mkdir call. */
 | |
|   r = hgfs_mkdir(path, m_in.REQ_MODE);
 | |
| 
 | |
|   if (r != OK) {
 | |
| 	if (ino != NULL)
 | |
| 		put_inode(ino);
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   /* If we thought the new dentry already existed, it was apparently gone
 | |
|    * already. Delete it.
 | |
|    */
 | |
|   if (ino != NULL) {
 | |
| 	del_dentry(ino);
 | |
| 
 | |
| 	put_inode(ino);
 | |
|   }
 | |
| 
 | |
|   return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				force_remove				     *
 | |
|  *===========================================================================*/
 | |
| PRIVATE int force_remove(path, dir)
 | |
| char *path;				/* path to file or directory */
 | |
| int dir;				/* TRUE iff directory */
 | |
| {
 | |
| /* Remove a file or directory. Wrapper around hgfs_unlink and hgfs_rmdir that
 | |
|  * makes the target temporarily writable if the operation fails with an access
 | |
|  * denied error. On Windows hosts, read-only files or directories cannot be
 | |
|  * removed (even though they can be renamed). In general, the HGFS server
 | |
|  * follows the behavior of the host file system, but this case just confuses
 | |
|  * the hell out of the MINIX userland..
 | |
|  */
 | |
|   struct hgfs_attr attr;
 | |
|   int r, r2;
 | |
| 
 | |
|   /* First try to remove the target. */
 | |
|   if (dir)
 | |
| 	r = hgfs_rmdir(path);
 | |
|   else
 | |
| 	r = hgfs_unlink(path);
 | |
| 
 | |
|   if (r != EACCES) return r;
 | |
| 
 | |
|   /* If this fails with an access error, retrieve the target's mode. */
 | |
|   attr.a_mask = HGFS_ATTR_MODE;
 | |
| 
 | |
|   r2 = hgfs_getattr(path, &attr);
 | |
| 
 | |
|   if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
 | |
| 
 | |
|   /* If the target is not writable, temporarily set it to writable. */
 | |
|   attr.a_mode |= S_IWUSR;
 | |
| 
 | |
|   r2 = hgfs_setattr(path, &attr);
 | |
| 
 | |
|   if (r2 != OK) return r;
 | |
| 
 | |
|   /* Then try the original operation again. */
 | |
|   if (dir)
 | |
| 	r = hgfs_rmdir(path);
 | |
|   else
 | |
| 	r = hgfs_unlink(path);
 | |
| 
 | |
|   if (r == OK) return r;
 | |
| 
 | |
|   /* If the operation still fails, unset the writable bit again. */
 | |
|   attr.a_mode &= ~S_IWUSR;
 | |
| 
 | |
|   hgfs_setattr(path, &attr);
 | |
| 
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_unlink				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int do_unlink()
 | |
| {
 | |
| /* Delete a file.
 | |
|  */
 | |
|   char path[PATH_MAX], name[NAME_MAX+1];
 | |
|   struct inode *parent, *ino;
 | |
|   int r;
 | |
| 
 | |
|   /* We cannot delete files on a read-only file system. */
 | |
|   if (state.read_only)
 | |
| 	return EROFS;
 | |
| 
 | |
|   /* Get the path string and possibly preexisting inode for the given path. */
 | |
|   if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   if (!strcmp(name, ".") || !strcmp(name, "..")) return EPERM;
 | |
| 
 | |
|   if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   /* Perform the unlink call. */
 | |
|   r = force_remove(path, FALSE /*dir*/);
 | |
| 
 | |
|   if (r != OK) {
 | |
| 	if (ino != NULL)
 | |
| 		put_inode(ino);
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   /* If a dentry existed for this name, it is gone now. */
 | |
|   if (ino != NULL) {
 | |
| 	del_dentry(ino);
 | |
| 
 | |
| 	put_inode(ino);
 | |
|   }
 | |
| 
 | |
|   return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_rmdir				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int do_rmdir()
 | |
| {
 | |
| /* Remove an empty directory.
 | |
|  */
 | |
|   char path[PATH_MAX], name[NAME_MAX+1];
 | |
|   struct inode *parent, *ino;
 | |
|   int r;
 | |
| 
 | |
|   /* We cannot remove directories on a read-only file system. */
 | |
|   if (state.read_only)
 | |
| 	return EROFS;
 | |
| 
 | |
|   /* Get the path string and possibly preexisting inode for the given path. */
 | |
|   if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   if (!strcmp(name, ".")) return EINVAL;
 | |
|   if (!strcmp(name, "..")) return ENOTEMPTY;
 | |
| 
 | |
|   if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   /* Perform the rmdir call. */
 | |
|   r = force_remove(path, TRUE /*dir*/);
 | |
| 
 | |
|   if (r != OK) {
 | |
| 	if (ino != NULL)
 | |
| 		put_inode(ino);
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   /* If a dentry existed for this name, it is gone now. */
 | |
|   if (ino != NULL) {
 | |
| 	del_dentry(ino);
 | |
| 
 | |
| 	put_inode(ino);
 | |
|   }
 | |
| 
 | |
|   return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_rename				     *
 | |
|  *===========================================================================*/
 | |
| PUBLIC int do_rename()
 | |
| {
 | |
| /* Rename a file or directory.
 | |
|  */
 | |
|   char old_path[PATH_MAX], new_path[PATH_MAX];
 | |
|   char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
 | |
|   struct inode *old_parent, *new_parent;
 | |
|   struct inode *old_ino, *new_ino;
 | |
|   int r;
 | |
| 
 | |
|   /* We cannot do rename on a read-only file system. */
 | |
|   if (state.read_only)
 | |
| 	return EROFS;
 | |
| 
 | |
|   /* Get path strings, names, directory inodes and possibly preexisting inodes
 | |
|    * for the old and new paths.
 | |
|    */
 | |
|   if ((r = get_name(m_in.REQ_REN_GRANT_OLD, m_in.REQ_REN_LEN_OLD,
 | |
| 	old_name)) != OK) return r;
 | |
| 
 | |
|   if ((r = get_name(m_in.REQ_REN_GRANT_NEW, m_in.REQ_REN_LEN_NEW,
 | |
| 	new_name)) != OK) return r;
 | |
| 
 | |
|   if (!strcmp(old_name, ".") || !strcmp(old_name, "..") ||
 | |
| 	!strcmp(new_name, ".") || !strcmp(new_name, "..")) return EINVAL;
 | |
| 
 | |
|   if ((old_parent = find_inode(m_in.REQ_REN_OLD_DIR)) == NULL ||
 | |
| 	(new_parent = find_inode(m_in.REQ_REN_NEW_DIR)) == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
 | |
| 	if (old_ino != NULL)
 | |
| 		put_inode(old_ino);
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   /* Perform the actual rename call. */
 | |
|   r = hgfs_rename(old_path, new_path);
 | |
| 
 | |
|   /* If we failed, or if we have nothing further to do: both inodes are
 | |
|    * NULL, or they both refer to the same file.
 | |
|    */
 | |
|   if (r != OK || old_ino == new_ino) {
 | |
| 	if (old_ino != NULL) put_inode(old_ino);
 | |
| 
 | |
| 	if (new_ino != NULL) put_inode(new_ino);
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   /* If the new dentry already existed, it has now been overwritten.
 | |
|    * Delete the associated inode if we had found one.
 | |
|    */
 | |
|   if (new_ino != NULL) {
 | |
| 	del_dentry(new_ino);
 | |
| 
 | |
| 	put_inode(new_ino);
 | |
|   }
 | |
| 
 | |
|   /* If the old dentry existed, rename it accordingly. */
 | |
|   if (old_ino != NULL) {
 | |
| 	del_dentry(old_ino);
 | |
| 
 | |
| 	add_dentry(new_parent, new_name, old_ino);
 | |
| 
 | |
| 	put_inode(old_ino);
 | |
|   }
 | |
| 
 | |
|   return OK;
 | |
| }
 |