pkgin_all: script to auto-install all packages
[minix.git] / lib / libsffs / link.c
blobbe877cc7f18f50a7fd9de9feb28e8cc7d769bb0b
1 /* This file contains directory entry related file system call handlers.
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
10 * Created:
11 * April 2009 (D.C. van Moolenbroek)
14 #include "inc.h"
16 #include <fcntl.h>
18 static int force_remove(char *path, int dir);
20 /*===========================================================================*
21 * do_create *
22 *===========================================================================*/
23 int do_create()
25 /* Create a new file.
27 char path[PATH_MAX], name[NAME_MAX+1];
28 struct inode *parent, *ino;
29 struct sffs_attr attr;
30 sffs_file_t handle;
31 int r;
33 /* We cannot create files on a read-only file system. */
34 if (state.s_read_only)
35 return EROFS;
37 /* Get path, name, parent inode and possibly inode for the given path. */
38 if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
39 return r;
41 if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
43 if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
44 return EINVAL;
46 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
47 return r;
49 /* Are we going to need a new inode upon success?
50 * Then make sure there is one available before trying anything.
52 if (ino == NULL || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
53 if (!have_free_inode()) {
54 if (ino != NULL)
55 put_inode(ino);
57 return ENFILE;
61 /* Perform the actual create call. */
62 r = sffs_table->t_open(path, O_CREAT | O_EXCL | O_RDWR, m_in.REQ_MODE,
63 &handle);
65 if (r != OK) {
66 /* Let's not try to be too clever with error codes here. If something
67 * is wrong with the directory, we'll find out later anyway.
70 if (ino != NULL)
71 put_inode(ino);
73 return r;
76 /* Get the created file's attributes. */
77 attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
78 r = sffs_table->t_getattr(path, &attr);
80 /* If this fails, or returns a directory, we have a problem. This
81 * scenario is in fact possible with race conditions.
82 * Simulate a close and return a somewhat appropriate error.
84 if (r != OK || S_ISDIR(attr.a_mode)) {
85 printf("%s: lost file after creation!\n", sffs_name);
87 sffs_table->t_close(handle);
89 if (ino != NULL) {
90 del_dentry(ino);
92 put_inode(ino);
95 return (r == OK) ? EEXIST : r;
98 /* We do assume that the underlying open(O_CREAT|O_EXCL) call did its job.
99 * If we previousy found an inode, get rid of it now. It's old.
101 if (ino != NULL) {
102 del_dentry(ino);
104 put_inode(ino);
107 /* Associate the open file handle with an inode, and reply with its details.
109 ino = get_free_inode();
111 assert(ino != NULL); /* we checked before whether we had a free one */
113 ino->i_file = handle;
114 ino->i_flags = I_HANDLE;
116 add_dentry(parent, name, ino);
118 m_out.RES_INODE_NR = INODE_NR(ino);
119 m_out.RES_MODE = get_mode(ino, attr.a_mode);
120 m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
121 m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
122 m_out.RES_UID = sffs_params->p_uid;
123 m_out.RES_GID = sffs_params->p_gid;
124 m_out.RES_DEV = NO_DEV;
126 return OK;
129 /*===========================================================================*
130 * do_mkdir *
131 *===========================================================================*/
132 int do_mkdir()
134 /* Make a new directory.
136 char path[PATH_MAX], name[NAME_MAX+1];
137 struct inode *parent, *ino;
138 int r;
140 /* We cannot create directories on a read-only file system. */
141 if (state.s_read_only)
142 return EROFS;
144 /* Get the path string and possibly an inode for the given path. */
145 if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
146 return r;
148 if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
150 if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
151 return EINVAL;
153 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
154 return r;
156 /* Perform the actual mkdir call. */
157 r = sffs_table->t_mkdir(path, m_in.REQ_MODE);
159 if (r != OK) {
160 if (ino != NULL)
161 put_inode(ino);
163 return r;
166 /* If we thought the new dentry already existed, it was apparently gone
167 * already. Delete it.
169 if (ino != NULL) {
170 del_dentry(ino);
172 put_inode(ino);
175 return OK;
178 /*===========================================================================*
179 * force_remove *
180 *===========================================================================*/
181 static int force_remove(path, dir)
182 char *path; /* path to file or directory */
183 int dir; /* TRUE iff directory */
185 /* Remove a file or directory. Wrapper around unlink and rmdir that makes the
186 * target temporarily writable if the operation fails with an access denied
187 * error. On Windows hosts, read-only files or directories cannot be removed
188 * (even though they can be renamed). In general, the SFFS library follows the
189 * behavior of the host file system, but this case just confuses the hell out
190 * of the MINIX userland..
192 struct sffs_attr attr;
193 int r, r2;
195 /* First try to remove the target. */
196 if (dir)
197 r = sffs_table->t_rmdir(path);
198 else
199 r = sffs_table->t_unlink(path);
201 if (r != EACCES) return r;
203 /* If this fails with an access error, retrieve the target's mode. */
204 attr.a_mask = SFFS_ATTR_MODE;
206 r2 = sffs_table->t_getattr(path, &attr);
208 if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
210 /* If the target is not writable, temporarily set it to writable. */
211 attr.a_mode |= S_IWUSR;
213 r2 = sffs_table->t_setattr(path, &attr);
215 if (r2 != OK) return r;
217 /* Then try the original operation again. */
218 if (dir)
219 r = sffs_table->t_rmdir(path);
220 else
221 r = sffs_table->t_unlink(path);
223 if (r == OK) return r;
225 /* If the operation still fails, unset the writable bit again. */
226 attr.a_mode &= ~S_IWUSR;
228 sffs_table->t_setattr(path, &attr);
230 return r;
233 /*===========================================================================*
234 * do_unlink *
235 *===========================================================================*/
236 int do_unlink()
238 /* Delete a file.
240 char path[PATH_MAX], name[NAME_MAX+1];
241 struct inode *parent, *ino;
242 int r;
244 /* We cannot delete files on a read-only file system. */
245 if (state.s_read_only)
246 return EROFS;
248 /* Get the path string and possibly preexisting inode for the given path. */
249 if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
250 return r;
252 if (!strcmp(name, ".") || !strcmp(name, "..")) return EPERM;
254 if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
255 return EINVAL;
257 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
258 return r;
260 /* Perform the unlink call. */
261 r = force_remove(path, FALSE /*dir*/);
263 if (r != OK) {
264 if (ino != NULL)
265 put_inode(ino);
267 return r;
270 /* If a dentry existed for this name, it is gone now. */
271 if (ino != NULL) {
272 del_dentry(ino);
274 put_inode(ino);
277 return OK;
280 /*===========================================================================*
281 * do_rmdir *
282 *===========================================================================*/
283 int do_rmdir()
285 /* Remove an empty directory.
287 char path[PATH_MAX], name[NAME_MAX+1];
288 struct inode *parent, *ino;
289 int r;
291 /* We cannot remove directories on a read-only file system. */
292 if (state.s_read_only)
293 return EROFS;
295 /* Get the path string and possibly preexisting inode for the given path. */
296 if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
297 return r;
299 if (!strcmp(name, ".")) return EINVAL;
300 if (!strcmp(name, "..")) return ENOTEMPTY;
302 if ((parent = find_inode(m_in.REQ_INODE_NR)) == NULL)
303 return EINVAL;
305 if ((r = verify_dentry(parent, name, path, &ino)) != OK)
306 return r;
308 /* Perform the rmdir call. */
309 r = force_remove(path, TRUE /*dir*/);
311 if (r != OK) {
312 if (ino != NULL)
313 put_inode(ino);
315 return r;
318 /* If a dentry existed for this name, it is gone now. */
319 if (ino != NULL) {
320 del_dentry(ino);
322 put_inode(ino);
325 return OK;
328 /*===========================================================================*
329 * do_rename *
330 *===========================================================================*/
331 int do_rename()
333 /* Rename a file or directory.
335 char old_path[PATH_MAX], new_path[PATH_MAX];
336 char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
337 struct inode *old_parent, *new_parent;
338 struct inode *old_ino, *new_ino;
339 int r;
341 /* We cannot do rename on a read-only file system. */
342 if (state.s_read_only)
343 return EROFS;
345 /* Get path strings, names, directory inodes and possibly preexisting inodes
346 * for the old and new paths.
348 if ((r = get_name(m_in.REQ_REN_GRANT_OLD, m_in.REQ_REN_LEN_OLD,
349 old_name)) != OK) return r;
351 if ((r = get_name(m_in.REQ_REN_GRANT_NEW, m_in.REQ_REN_LEN_NEW,
352 new_name)) != OK) return r;
354 if (!strcmp(old_name, ".") || !strcmp(old_name, "..") ||
355 !strcmp(new_name, ".") || !strcmp(new_name, "..")) return EINVAL;
357 if ((old_parent = find_inode(m_in.REQ_REN_OLD_DIR)) == NULL ||
358 (new_parent = find_inode(m_in.REQ_REN_NEW_DIR)) == NULL)
359 return EINVAL;
361 if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
362 return r;
364 if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
365 if (old_ino != NULL)
366 put_inode(old_ino);
368 return r;
371 /* Perform the actual rename call. */
372 r = sffs_table->t_rename(old_path, new_path);
374 /* If we failed, or if we have nothing further to do: both inodes are
375 * NULL, or they both refer to the same file.
377 if (r != OK || old_ino == new_ino) {
378 if (old_ino != NULL) put_inode(old_ino);
380 if (new_ino != NULL) put_inode(new_ino);
382 return r;
385 /* If the new dentry already existed, it has now been overwritten.
386 * Delete the associated inode if we had found one.
388 if (new_ino != NULL) {
389 del_dentry(new_ino);
391 put_inode(new_ino);
394 /* If the old dentry existed, rename it accordingly. */
395 if (old_ino != NULL) {
396 del_dentry(old_ino);
398 add_dentry(new_parent, new_name, old_ino);
400 put_inode(old_ino);
403 return OK;