xref: /minix3/minix/lib/libsffs/link.c (revision 289b04677a1b234d09a045a727f5e614a6c8d716)
1433d6423SLionel Sambuc /* This file contains directory entry related file system call handlers.
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * The entry points into this file are:
4433d6423SLionel Sambuc  *   do_create		perform the CREATE file system call
5433d6423SLionel Sambuc  *   do_mkdir		perform the MKDIR file system call
6433d6423SLionel Sambuc  *   do_unlink		perform the UNLINK file system call
7433d6423SLionel Sambuc  *   do_rmdir		perform the RMDIR file system call
8433d6423SLionel Sambuc  *   do_rename		perform the RENAME file system call
9433d6423SLionel Sambuc  *
10433d6423SLionel Sambuc  * Created:
11433d6423SLionel Sambuc  *   April 2009 (D.C. van Moolenbroek)
12433d6423SLionel Sambuc  */
13433d6423SLionel Sambuc 
14433d6423SLionel Sambuc #include "inc.h"
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc #include <fcntl.h>
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc /*===========================================================================*
19433d6423SLionel Sambuc  *				do_create				     *
20433d6423SLionel Sambuc  *===========================================================================*/
do_create(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid,struct fsdriver_node * node)21a99c939dSDavid van Moolenbroek int do_create(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid,
22a99c939dSDavid van Moolenbroek 	struct fsdriver_node *node)
23433d6423SLionel Sambuc {
24433d6423SLionel Sambuc /* Create a new file.
25433d6423SLionel Sambuc  */
26a99c939dSDavid van Moolenbroek   char path[PATH_MAX];
27433d6423SLionel Sambuc   struct inode *parent, *ino;
28433d6423SLionel Sambuc   struct sffs_attr attr;
29433d6423SLionel Sambuc   sffs_file_t handle;
30433d6423SLionel Sambuc   int r;
31433d6423SLionel Sambuc 
32433d6423SLionel Sambuc   /* We cannot create files on a read-only file system. */
33*289b0467SDavid van Moolenbroek   if (read_only)
34433d6423SLionel Sambuc 	return EROFS;
35433d6423SLionel Sambuc 
36a99c939dSDavid van Moolenbroek   if ((parent = find_inode(dir_nr)) == NULL)
37433d6423SLionel Sambuc 	return EINVAL;
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
40433d6423SLionel Sambuc 	return r;
41433d6423SLionel Sambuc 
42433d6423SLionel Sambuc   /* Are we going to need a new inode upon success?
43433d6423SLionel Sambuc    * Then make sure there is one available before trying anything.
44433d6423SLionel Sambuc    */
45433d6423SLionel Sambuc   if (ino == NULL || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
46433d6423SLionel Sambuc 	if (!have_free_inode()) {
47433d6423SLionel Sambuc 		if (ino != NULL)
48433d6423SLionel Sambuc 			put_inode(ino);
49433d6423SLionel Sambuc 
50433d6423SLionel Sambuc 		return ENFILE;
51433d6423SLionel Sambuc 	}
52433d6423SLionel Sambuc   }
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc   /* Perform the actual create call. */
55a99c939dSDavid van Moolenbroek   r = sffs_table->t_open(path, O_CREAT | O_EXCL | O_RDWR, mode, &handle);
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc   if (r != OK) {
58433d6423SLionel Sambuc 	/* Let's not try to be too clever with error codes here. If something
59433d6423SLionel Sambuc 	 * is wrong with the directory, we'll find out later anyway.
60433d6423SLionel Sambuc 	 */
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc 	if (ino != NULL)
63433d6423SLionel Sambuc 		put_inode(ino);
64433d6423SLionel Sambuc 
65433d6423SLionel Sambuc 	return r;
66433d6423SLionel Sambuc   }
67433d6423SLionel Sambuc 
68433d6423SLionel Sambuc   /* Get the created file's attributes. */
69433d6423SLionel Sambuc   attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
70433d6423SLionel Sambuc   r = sffs_table->t_getattr(path, &attr);
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc   /* If this fails, or returns a directory, we have a problem. This
73433d6423SLionel Sambuc    * scenario is in fact possible with race conditions.
74433d6423SLionel Sambuc    * Simulate a close and return a somewhat appropriate error.
75433d6423SLionel Sambuc    */
76433d6423SLionel Sambuc   if (r != OK || S_ISDIR(attr.a_mode)) {
77433d6423SLionel Sambuc 	printf("%s: lost file after creation!\n", sffs_name);
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 	sffs_table->t_close(handle);
80433d6423SLionel Sambuc 
81433d6423SLionel Sambuc 	if (ino != NULL) {
82433d6423SLionel Sambuc 		del_dentry(ino);
83433d6423SLionel Sambuc 
84433d6423SLionel Sambuc 		put_inode(ino);
85433d6423SLionel Sambuc 	}
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc 	return (r == OK) ? EEXIST : r;
88433d6423SLionel Sambuc   }
89433d6423SLionel Sambuc 
90433d6423SLionel Sambuc   /* We do assume that the underlying open(O_CREAT|O_EXCL) call did its job.
91433d6423SLionel Sambuc    * If we previousy found an inode, get rid of it now. It's old.
92433d6423SLionel Sambuc    */
93433d6423SLionel Sambuc   if (ino != NULL) {
94433d6423SLionel Sambuc 	del_dentry(ino);
95433d6423SLionel Sambuc 
96433d6423SLionel Sambuc 	put_inode(ino);
97433d6423SLionel Sambuc   }
98433d6423SLionel Sambuc 
99433d6423SLionel Sambuc   /* Associate the open file handle with an inode, and reply with its details.
100433d6423SLionel Sambuc    */
101433d6423SLionel Sambuc   ino = get_free_inode();
102433d6423SLionel Sambuc 
103433d6423SLionel Sambuc   assert(ino != NULL); /* we checked before whether we had a free one */
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc   ino->i_file = handle;
106433d6423SLionel Sambuc   ino->i_flags = I_HANDLE;
107433d6423SLionel Sambuc 
108433d6423SLionel Sambuc   add_dentry(parent, name, ino);
109433d6423SLionel Sambuc 
110a99c939dSDavid van Moolenbroek   node->fn_ino_nr = INODE_NR(ino);
111a99c939dSDavid van Moolenbroek   node->fn_mode = get_mode(ino, attr.a_mode);
112a99c939dSDavid van Moolenbroek   node->fn_size = attr.a_size;
113a99c939dSDavid van Moolenbroek   node->fn_uid = sffs_params->p_uid;
114a99c939dSDavid van Moolenbroek   node->fn_gid = sffs_params->p_gid;
115a99c939dSDavid van Moolenbroek   node->fn_dev = NO_DEV;
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc   return OK;
118433d6423SLionel Sambuc }
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc /*===========================================================================*
121433d6423SLionel Sambuc  *				do_mkdir				     *
122433d6423SLionel Sambuc  *===========================================================================*/
do_mkdir(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid)123a99c939dSDavid van Moolenbroek int do_mkdir(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid)
124433d6423SLionel Sambuc {
125433d6423SLionel Sambuc /* Make a new directory.
126433d6423SLionel Sambuc  */
127a99c939dSDavid van Moolenbroek   char path[PATH_MAX];
128433d6423SLionel Sambuc   struct inode *parent, *ino;
129433d6423SLionel Sambuc   int r;
130433d6423SLionel Sambuc 
131433d6423SLionel Sambuc   /* We cannot create directories on a read-only file system. */
132*289b0467SDavid van Moolenbroek   if (read_only)
133433d6423SLionel Sambuc 	return EROFS;
134433d6423SLionel Sambuc 
135a99c939dSDavid van Moolenbroek   if ((parent = find_inode(dir_nr)) == NULL)
136433d6423SLionel Sambuc 	return EINVAL;
137433d6423SLionel Sambuc 
138433d6423SLionel Sambuc   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
139433d6423SLionel Sambuc 	return r;
140433d6423SLionel Sambuc 
141433d6423SLionel Sambuc   /* Perform the actual mkdir call. */
142a99c939dSDavid van Moolenbroek   r = sffs_table->t_mkdir(path, mode);
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc   if (r != OK) {
145433d6423SLionel Sambuc 	if (ino != NULL)
146433d6423SLionel Sambuc 		put_inode(ino);
147433d6423SLionel Sambuc 
148433d6423SLionel Sambuc 	return r;
149433d6423SLionel Sambuc   }
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc   /* If we thought the new dentry already existed, it was apparently gone
152433d6423SLionel Sambuc    * already. Delete it.
153433d6423SLionel Sambuc    */
154433d6423SLionel Sambuc   if (ino != NULL) {
155433d6423SLionel Sambuc 	del_dentry(ino);
156433d6423SLionel Sambuc 
157433d6423SLionel Sambuc 	put_inode(ino);
158433d6423SLionel Sambuc   }
159433d6423SLionel Sambuc 
160433d6423SLionel Sambuc   return OK;
161433d6423SLionel Sambuc }
162433d6423SLionel Sambuc 
163433d6423SLionel Sambuc /*===========================================================================*
164433d6423SLionel Sambuc  *				force_remove				     *
165433d6423SLionel Sambuc  *===========================================================================*/
force_remove(char * path,int dir)166433d6423SLionel Sambuc static int force_remove(
167433d6423SLionel Sambuc 	char *path,	/* path to file or directory */
168433d6423SLionel Sambuc 	int dir	   	/* TRUE iff directory */
169433d6423SLionel Sambuc )
170433d6423SLionel Sambuc {
171433d6423SLionel Sambuc /* Remove a file or directory. Wrapper around unlink and rmdir that makes the
172433d6423SLionel Sambuc  * target temporarily writable if the operation fails with an access denied
173433d6423SLionel Sambuc  * error. On Windows hosts, read-only files or directories cannot be removed
174433d6423SLionel Sambuc  * (even though they can be renamed). In general, the SFFS library follows the
175433d6423SLionel Sambuc  * behavior of the host file system, but this case just confuses the hell out
176433d6423SLionel Sambuc  * of the MINIX userland..
177433d6423SLionel Sambuc  */
178433d6423SLionel Sambuc   struct sffs_attr attr;
179433d6423SLionel Sambuc   int r, r2;
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc   /* First try to remove the target. */
182433d6423SLionel Sambuc   if (dir)
183433d6423SLionel Sambuc 	r = sffs_table->t_rmdir(path);
184433d6423SLionel Sambuc   else
185433d6423SLionel Sambuc 	r = sffs_table->t_unlink(path);
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc   if (r != EACCES) return r;
188433d6423SLionel Sambuc 
189433d6423SLionel Sambuc   /* If this fails with an access error, retrieve the target's mode. */
190433d6423SLionel Sambuc   attr.a_mask = SFFS_ATTR_MODE;
191433d6423SLionel Sambuc 
192433d6423SLionel Sambuc   r2 = sffs_table->t_getattr(path, &attr);
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc   if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc   /* If the target is not writable, temporarily set it to writable. */
197433d6423SLionel Sambuc   attr.a_mode |= S_IWUSR;
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc   r2 = sffs_table->t_setattr(path, &attr);
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc   if (r2 != OK) return r;
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc   /* Then try the original operation again. */
204433d6423SLionel Sambuc   if (dir)
205433d6423SLionel Sambuc 	r = sffs_table->t_rmdir(path);
206433d6423SLionel Sambuc   else
207433d6423SLionel Sambuc 	r = sffs_table->t_unlink(path);
208433d6423SLionel Sambuc 
209433d6423SLionel Sambuc   if (r == OK) return r;
210433d6423SLionel Sambuc 
211433d6423SLionel Sambuc   /* If the operation still fails, unset the writable bit again. */
212433d6423SLionel Sambuc   attr.a_mode &= ~S_IWUSR;
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc   sffs_table->t_setattr(path, &attr);
215433d6423SLionel Sambuc 
216433d6423SLionel Sambuc   return r;
217433d6423SLionel Sambuc }
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc /*===========================================================================*
220433d6423SLionel Sambuc  *				do_unlink				     *
221433d6423SLionel Sambuc  *===========================================================================*/
do_unlink(ino_t dir_nr,char * name,int call)222a99c939dSDavid van Moolenbroek int do_unlink(ino_t dir_nr, char *name, int call)
223433d6423SLionel Sambuc {
224433d6423SLionel Sambuc /* Delete a file.
225433d6423SLionel Sambuc  */
226a99c939dSDavid van Moolenbroek   char path[PATH_MAX];
227433d6423SLionel Sambuc   struct inode *parent, *ino;
228433d6423SLionel Sambuc   int r;
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc   /* We cannot delete files on a read-only file system. */
231*289b0467SDavid van Moolenbroek   if (read_only)
232433d6423SLionel Sambuc 	return EROFS;
233433d6423SLionel Sambuc 
234a99c939dSDavid van Moolenbroek   if ((parent = find_inode(dir_nr)) == NULL)
235433d6423SLionel Sambuc 	return EINVAL;
236433d6423SLionel Sambuc 
237433d6423SLionel Sambuc   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
238433d6423SLionel Sambuc 	return r;
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc   /* Perform the unlink call. */
241433d6423SLionel Sambuc   r = force_remove(path, FALSE /*dir*/);
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc   if (r != OK) {
244433d6423SLionel Sambuc 	if (ino != NULL)
245433d6423SLionel Sambuc 		put_inode(ino);
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc 	return r;
248433d6423SLionel Sambuc   }
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc   /* If a dentry existed for this name, it is gone now. */
251433d6423SLionel Sambuc   if (ino != NULL) {
252433d6423SLionel Sambuc 	del_dentry(ino);
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc 	put_inode(ino);
255433d6423SLionel Sambuc   }
256433d6423SLionel Sambuc 
257433d6423SLionel Sambuc   return OK;
258433d6423SLionel Sambuc }
259433d6423SLionel Sambuc 
260433d6423SLionel Sambuc /*===========================================================================*
261433d6423SLionel Sambuc  *				do_rmdir				     *
262433d6423SLionel Sambuc  *===========================================================================*/
do_rmdir(ino_t dir_nr,char * name,int call)263a99c939dSDavid van Moolenbroek int do_rmdir(ino_t dir_nr, char *name, int call)
264433d6423SLionel Sambuc {
265433d6423SLionel Sambuc /* Remove an empty directory.
266433d6423SLionel Sambuc  */
267a99c939dSDavid van Moolenbroek   char path[PATH_MAX];
268433d6423SLionel Sambuc   struct inode *parent, *ino;
269433d6423SLionel Sambuc   int r;
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc   /* We cannot remove directories on a read-only file system. */
272*289b0467SDavid van Moolenbroek   if (read_only)
273433d6423SLionel Sambuc 	return EROFS;
274433d6423SLionel Sambuc 
275a99c939dSDavid van Moolenbroek   if ((parent = find_inode(dir_nr)) == NULL)
276433d6423SLionel Sambuc 	return EINVAL;
277433d6423SLionel Sambuc 
278433d6423SLionel Sambuc   if ((r = verify_dentry(parent, name, path, &ino)) != OK)
279433d6423SLionel Sambuc 	return r;
280433d6423SLionel Sambuc 
281433d6423SLionel Sambuc   /* Perform the rmdir call. */
282433d6423SLionel Sambuc   r = force_remove(path, TRUE /*dir*/);
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc   if (r != OK) {
285433d6423SLionel Sambuc 	if (ino != NULL)
286433d6423SLionel Sambuc 		put_inode(ino);
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 	return r;
289433d6423SLionel Sambuc   }
290433d6423SLionel Sambuc 
291433d6423SLionel Sambuc   /* If a dentry existed for this name, it is gone now. */
292433d6423SLionel Sambuc   if (ino != NULL) {
293433d6423SLionel Sambuc 	del_dentry(ino);
294433d6423SLionel Sambuc 
295433d6423SLionel Sambuc 	put_inode(ino);
296433d6423SLionel Sambuc   }
297433d6423SLionel Sambuc 
298433d6423SLionel Sambuc   return OK;
299433d6423SLionel Sambuc }
300433d6423SLionel Sambuc 
301433d6423SLionel Sambuc /*===========================================================================*
302433d6423SLionel Sambuc  *				do_rename				     *
303433d6423SLionel Sambuc  *===========================================================================*/
do_rename(ino_t old_dir_nr,char * old_name,ino_t new_dir_nr,char * new_name)304a99c939dSDavid van Moolenbroek int do_rename(ino_t old_dir_nr, char *old_name, ino_t new_dir_nr,
305a99c939dSDavid van Moolenbroek 	char *new_name)
306433d6423SLionel Sambuc {
307433d6423SLionel Sambuc /* Rename a file or directory.
308433d6423SLionel Sambuc  */
309433d6423SLionel Sambuc   char old_path[PATH_MAX], new_path[PATH_MAX];
310433d6423SLionel Sambuc   struct inode *old_parent, *new_parent;
311433d6423SLionel Sambuc   struct inode *old_ino, *new_ino;
312433d6423SLionel Sambuc   int r;
313433d6423SLionel Sambuc 
314433d6423SLionel Sambuc   /* We cannot do rename on a read-only file system. */
315*289b0467SDavid van Moolenbroek   if (read_only)
316433d6423SLionel Sambuc 	return EROFS;
317433d6423SLionel Sambuc 
318a99c939dSDavid van Moolenbroek   /* Get possibly preexisting inodes for the old and new paths. */
319a99c939dSDavid van Moolenbroek   if ((old_parent = find_inode(old_dir_nr)) == NULL ||
320a99c939dSDavid van Moolenbroek 	(new_parent = find_inode(new_dir_nr)) == NULL)
321433d6423SLionel Sambuc 	return EINVAL;
322433d6423SLionel Sambuc 
323433d6423SLionel Sambuc   if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
324433d6423SLionel Sambuc 	return r;
325433d6423SLionel Sambuc 
326433d6423SLionel Sambuc   if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
327433d6423SLionel Sambuc 	if (old_ino != NULL)
328433d6423SLionel Sambuc 		put_inode(old_ino);
329433d6423SLionel Sambuc 
330433d6423SLionel Sambuc 	return r;
331433d6423SLionel Sambuc   }
332433d6423SLionel Sambuc 
333433d6423SLionel Sambuc   /* Perform the actual rename call. */
334433d6423SLionel Sambuc   r = sffs_table->t_rename(old_path, new_path);
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc   /* If we failed, or if we have nothing further to do: both inodes are
337433d6423SLionel Sambuc    * NULL, or they both refer to the same file.
338433d6423SLionel Sambuc    */
339433d6423SLionel Sambuc   if (r != OK || old_ino == new_ino) {
340433d6423SLionel Sambuc 	if (old_ino != NULL) put_inode(old_ino);
341433d6423SLionel Sambuc 
342433d6423SLionel Sambuc 	if (new_ino != NULL) put_inode(new_ino);
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc 	return r;
345433d6423SLionel Sambuc   }
346433d6423SLionel Sambuc 
347433d6423SLionel Sambuc   /* If the new dentry already existed, it has now been overwritten.
348433d6423SLionel Sambuc    * Delete the associated inode if we had found one.
349433d6423SLionel Sambuc    */
350433d6423SLionel Sambuc   if (new_ino != NULL) {
351433d6423SLionel Sambuc 	del_dentry(new_ino);
352433d6423SLionel Sambuc 
353433d6423SLionel Sambuc 	put_inode(new_ino);
354433d6423SLionel Sambuc   }
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc   /* If the old dentry existed, rename it accordingly. */
357433d6423SLionel Sambuc   if (old_ino != NULL) {
358433d6423SLionel Sambuc 	del_dentry(old_ino);
359433d6423SLionel Sambuc 
360433d6423SLionel Sambuc 	add_dentry(new_parent, new_name, old_ino);
361433d6423SLionel Sambuc 
362433d6423SLionel Sambuc 	put_inode(old_ino);
363433d6423SLionel Sambuc   }
364433d6423SLionel Sambuc 
365433d6423SLionel Sambuc   return OK;
366433d6423SLionel Sambuc }
367