1 /* This file contains directory entry related file system call handlers.
2 *
3 * The entry points into this file are:
4 * do_create perform the CREATE file system call
5 * do_mkdir perform the MKDIR file system call
6 * do_unlink perform the UNLINK file system call
7 * do_rmdir perform the RMDIR file system call
8 * do_rename perform the RENAME file system call
9 *
10 * Created:
11 * April 2009 (D.C. van Moolenbroek)
12 */
13
14 #include "inc.h"
15
16 #include <fcntl.h>
17
18 /*===========================================================================*
19 * do_create *
20 *===========================================================================*/
do_create(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid,struct fsdriver_node * node)21 int do_create(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid,
22 struct fsdriver_node *node)
23 {
24 /* Create a new file.
25 */
26 char path[PATH_MAX];
27 struct inode *parent, *ino;
28 struct sffs_attr attr;
29 sffs_file_t handle;
30 int r;
31
32 /* We cannot create files on a read-only file system. */
33 if (read_only)
34 return EROFS;
35
36 if ((parent = find_inode(dir_nr)) == NULL)
37 return EINVAL;
38
39 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
40 return r;
41
42 /* Are we going to need a new inode upon success?
43 * Then make sure there is one available before trying anything.
44 */
45 if (ino == NULL || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
46 if (!have_free_inode()) {
47 if (ino != NULL)
48 put_inode(ino);
49
50 return ENFILE;
51 }
52 }
53
54 /* Perform the actual create call. */
55 r = sffs_table->t_open(path, O_CREAT | O_EXCL | O_RDWR, mode, &handle);
56
57 if (r != OK) {
58 /* Let's not try to be too clever with error codes here. If something
59 * is wrong with the directory, we'll find out later anyway.
60 */
61
62 if (ino != NULL)
63 put_inode(ino);
64
65 return r;
66 }
67
68 /* Get the created file's attributes. */
69 attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
70 r = sffs_table->t_getattr(path, &attr);
71
72 /* If this fails, or returns a directory, we have a problem. This
73 * scenario is in fact possible with race conditions.
74 * Simulate a close and return a somewhat appropriate error.
75 */
76 if (r != OK || S_ISDIR(attr.a_mode)) {
77 printf("%s: lost file after creation!\n", sffs_name);
78
79 sffs_table->t_close(handle);
80
81 if (ino != NULL) {
82 del_dentry(ino);
83
84 put_inode(ino);
85 }
86
87 return (r == OK) ? EEXIST : r;
88 }
89
90 /* We do assume that the underlying open(O_CREAT|O_EXCL) call did its job.
91 * If we previousy found an inode, get rid of it now. It's old.
92 */
93 if (ino != NULL) {
94 del_dentry(ino);
95
96 put_inode(ino);
97 }
98
99 /* Associate the open file handle with an inode, and reply with its details.
100 */
101 ino = get_free_inode();
102
103 assert(ino != NULL); /* we checked before whether we had a free one */
104
105 ino->i_file = handle;
106 ino->i_flags = I_HANDLE;
107
108 add_dentry(parent, name, ino);
109
110 node->fn_ino_nr = INODE_NR(ino);
111 node->fn_mode = get_mode(ino, attr.a_mode);
112 node->fn_size = attr.a_size;
113 node->fn_uid = sffs_params->p_uid;
114 node->fn_gid = sffs_params->p_gid;
115 node->fn_dev = NO_DEV;
116
117 return OK;
118 }
119
120 /*===========================================================================*
121 * do_mkdir *
122 *===========================================================================*/
do_mkdir(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid)123 int do_mkdir(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid)
124 {
125 /* Make a new directory.
126 */
127 char path[PATH_MAX];
128 struct inode *parent, *ino;
129 int r;
130
131 /* We cannot create directories on a read-only file system. */
132 if (read_only)
133 return EROFS;
134
135 if ((parent = find_inode(dir_nr)) == NULL)
136 return EINVAL;
137
138 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
139 return r;
140
141 /* Perform the actual mkdir call. */
142 r = sffs_table->t_mkdir(path, mode);
143
144 if (r != OK) {
145 if (ino != NULL)
146 put_inode(ino);
147
148 return r;
149 }
150
151 /* If we thought the new dentry already existed, it was apparently gone
152 * already. Delete it.
153 */
154 if (ino != NULL) {
155 del_dentry(ino);
156
157 put_inode(ino);
158 }
159
160 return OK;
161 }
162
163 /*===========================================================================*
164 * force_remove *
165 *===========================================================================*/
force_remove(char * path,int dir)166 static int force_remove(
167 char *path, /* path to file or directory */
168 int dir /* TRUE iff directory */
169 )
170 {
171 /* Remove a file or directory. Wrapper around unlink and rmdir that makes the
172 * target temporarily writable if the operation fails with an access denied
173 * error. On Windows hosts, read-only files or directories cannot be removed
174 * (even though they can be renamed). In general, the SFFS library follows the
175 * behavior of the host file system, but this case just confuses the hell out
176 * of the MINIX userland..
177 */
178 struct sffs_attr attr;
179 int r, r2;
180
181 /* First try to remove the target. */
182 if (dir)
183 r = sffs_table->t_rmdir(path);
184 else
185 r = sffs_table->t_unlink(path);
186
187 if (r != EACCES) return r;
188
189 /* If this fails with an access error, retrieve the target's mode. */
190 attr.a_mask = SFFS_ATTR_MODE;
191
192 r2 = sffs_table->t_getattr(path, &attr);
193
194 if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
195
196 /* If the target is not writable, temporarily set it to writable. */
197 attr.a_mode |= S_IWUSR;
198
199 r2 = sffs_table->t_setattr(path, &attr);
200
201 if (r2 != OK) return r;
202
203 /* Then try the original operation again. */
204 if (dir)
205 r = sffs_table->t_rmdir(path);
206 else
207 r = sffs_table->t_unlink(path);
208
209 if (r == OK) return r;
210
211 /* If the operation still fails, unset the writable bit again. */
212 attr.a_mode &= ~S_IWUSR;
213
214 sffs_table->t_setattr(path, &attr);
215
216 return r;
217 }
218
219 /*===========================================================================*
220 * do_unlink *
221 *===========================================================================*/
do_unlink(ino_t dir_nr,char * name,int call)222 int do_unlink(ino_t dir_nr, char *name, int call)
223 {
224 /* Delete a file.
225 */
226 char path[PATH_MAX];
227 struct inode *parent, *ino;
228 int r;
229
230 /* We cannot delete files on a read-only file system. */
231 if (read_only)
232 return EROFS;
233
234 if ((parent = find_inode(dir_nr)) == NULL)
235 return EINVAL;
236
237 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
238 return r;
239
240 /* Perform the unlink call. */
241 r = force_remove(path, FALSE /*dir*/);
242
243 if (r != OK) {
244 if (ino != NULL)
245 put_inode(ino);
246
247 return r;
248 }
249
250 /* If a dentry existed for this name, it is gone now. */
251 if (ino != NULL) {
252 del_dentry(ino);
253
254 put_inode(ino);
255 }
256
257 return OK;
258 }
259
260 /*===========================================================================*
261 * do_rmdir *
262 *===========================================================================*/
do_rmdir(ino_t dir_nr,char * name,int call)263 int do_rmdir(ino_t dir_nr, char *name, int call)
264 {
265 /* Remove an empty directory.
266 */
267 char path[PATH_MAX];
268 struct inode *parent, *ino;
269 int r;
270
271 /* We cannot remove directories on a read-only file system. */
272 if (read_only)
273 return EROFS;
274
275 if ((parent = find_inode(dir_nr)) == NULL)
276 return EINVAL;
277
278 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
279 return r;
280
281 /* Perform the rmdir call. */
282 r = force_remove(path, TRUE /*dir*/);
283
284 if (r != OK) {
285 if (ino != NULL)
286 put_inode(ino);
287
288 return r;
289 }
290
291 /* If a dentry existed for this name, it is gone now. */
292 if (ino != NULL) {
293 del_dentry(ino);
294
295 put_inode(ino);
296 }
297
298 return OK;
299 }
300
301 /*===========================================================================*
302 * do_rename *
303 *===========================================================================*/
do_rename(ino_t old_dir_nr,char * old_name,ino_t new_dir_nr,char * new_name)304 int do_rename(ino_t old_dir_nr, char *old_name, ino_t new_dir_nr,
305 char *new_name)
306 {
307 /* Rename a file or directory.
308 */
309 char old_path[PATH_MAX], new_path[PATH_MAX];
310 struct inode *old_parent, *new_parent;
311 struct inode *old_ino, *new_ino;
312 int r;
313
314 /* We cannot do rename on a read-only file system. */
315 if (read_only)
316 return EROFS;
317
318 /* Get possibly preexisting inodes for the old and new paths. */
319 if ((old_parent = find_inode(old_dir_nr)) == NULL ||
320 (new_parent = find_inode(new_dir_nr)) == NULL)
321 return EINVAL;
322
323 if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
324 return r;
325
326 if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
327 if (old_ino != NULL)
328 put_inode(old_ino);
329
330 return r;
331 }
332
333 /* Perform the actual rename call. */
334 r = sffs_table->t_rename(old_path, new_path);
335
336 /* If we failed, or if we have nothing further to do: both inodes are
337 * NULL, or they both refer to the same file.
338 */
339 if (r != OK || old_ino == new_ino) {
340 if (old_ino != NULL) put_inode(old_ino);
341
342 if (new_ino != NULL) put_inode(new_ino);
343
344 return r;
345 }
346
347 /* If the new dentry already existed, it has now been overwritten.
348 * Delete the associated inode if we had found one.
349 */
350 if (new_ino != NULL) {
351 del_dentry(new_ino);
352
353 put_inode(new_ino);
354 }
355
356 /* If the old dentry existed, rename it accordingly. */
357 if (old_ino != NULL) {
358 del_dentry(old_ino);
359
360 add_dentry(new_parent, new_name, old_ino);
361
362 put_inode(old_ino);
363 }
364
365 return OK;
366 }
367