xref: /minix3/minix/fs/ext2/open.c (revision 89c9de7d091f384bd4337bd6775fb15c93b8e8c6)
1 /* Created (MFS based):
2  *   February 2010 (Evgeniy Ivanov)
3  */
4 
5 #include "fs.h"
6 #include <sys/stat.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <minix/com.h>
10 #include "buf.h"
11 #include "inode.h"
12 #include "super.h"
13 #include <minix/vfsif.h>
14 
15 static struct inode *new_node(struct inode *ldirp, char *string, mode_t
16 	bits, block_t z0);
17 
18 
19 /*===========================================================================*
20  *				fs_create				     *
21  *===========================================================================*/
22 int fs_create()
23 {
24   phys_bytes len;
25   int r;
26   struct inode *ldirp;
27   struct inode *rip;
28   mode_t omode;
29   char lastc[NAME_MAX + 1];
30 
31   /* Read request message */
32   omode = fs_m_in.m_vfs_fs_create.mode;
33   caller_uid = fs_m_in.m_vfs_fs_create.uid;
34   caller_gid = fs_m_in.m_vfs_fs_create.gid;
35 
36   /* Try to make the file. */
37 
38   /* Copy the last component (i.e., file name) */
39   len = fs_m_in.m_vfs_fs_create.path_len; /* including trailing '\0' */
40   if (len > NAME_MAX + 1 || len > EXT2_NAME_MAX + 1)
41 	return(ENAMETOOLONG);
42 
43   err_code = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_create.grant,
44 			      (vir_bytes) 0, (vir_bytes) lastc, (size_t) len);
45   if (err_code != OK) return err_code;
46   NUL(lastc, len, sizeof(lastc));
47 
48   /* Get last directory inode (i.e., directory that will hold the new inode) */
49   if ((ldirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_create.inode)) == NULL)
50 	  return(ENOENT);
51 
52   /* Create a new inode by calling new_node(). */
53   rip = new_node(ldirp, lastc, omode, NO_BLOCK);
54   r = err_code;
55 
56   /* If an error occurred, release inode. */
57   if (r != OK) {
58 	  put_inode(ldirp);
59 	  put_inode(rip);
60 	  return(r);
61   }
62 
63   /* Reply message */
64   fs_m_out.m_fs_vfs_create.inode = rip->i_num;
65   fs_m_out.m_fs_vfs_create.mode = rip->i_mode;
66   fs_m_out.m_fs_vfs_create.file_size = rip->i_size;
67 
68   /* This values are needed for the execution */
69   fs_m_out.m_fs_vfs_create.uid = rip->i_uid;
70   fs_m_out.m_fs_vfs_create.gid = rip->i_gid;
71 
72   /* Drop parent dir */
73   put_inode(ldirp);
74 
75   return(OK);
76 }
77 
78 
79 /*===========================================================================*
80  *				fs_mknod				     *
81  *===========================================================================*/
82 int fs_mknod()
83 {
84   struct inode *ip, *ldirp;
85   char lastc[NAME_MAX + 1];
86   phys_bytes len;
87 
88   /* Copy the last component and set up caller's user and group id */
89   len = fs_m_in.m_vfs_fs_mknod.path_len; /* including trailing '\0' */
90   if (len > NAME_MAX + 1 || len > EXT2_NAME_MAX + 1)
91 	return(ENAMETOOLONG);
92 
93   err_code = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_mknod.grant,
94                              (vir_bytes) 0, (vir_bytes) lastc, (size_t) len);
95   if (err_code != OK) return err_code;
96   NUL(lastc, len, sizeof(lastc));
97 
98   caller_uid = fs_m_in.m_vfs_fs_mknod.uid;
99   caller_gid = fs_m_in.m_vfs_fs_mknod.gid;
100 
101   /* Get last directory inode */
102   if((ldirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_mknod.inode)) == NULL)
103 	  return(ENOENT);
104 
105   /* Try to create the new node */
106   ip = new_node(ldirp, lastc, fs_m_in.m_vfs_fs_mknod.mode,
107 		(block_t) fs_m_in.m_vfs_fs_mknod.device);
108 
109   put_inode(ip);
110   put_inode(ldirp);
111   return(err_code);
112 }
113 
114 
115 /*===========================================================================*
116  *				fs_mkdir				     *
117  *===========================================================================*/
118 int fs_mkdir()
119 {
120   int r1, r2;			/* status codes */
121   ino_t dot, dotdot;		/* inode numbers for . and .. */
122   struct inode *rip, *ldirp;
123   char lastc[NAME_MAX + 1];         /* last component */
124   phys_bytes len;
125 
126   /* Copy the last component and set up caller's user and group id */
127   len = fs_m_in.m_vfs_fs_mkdir.path_len; /* including trailing '\0' */
128   if (len > NAME_MAX + 1 || len > EXT2_NAME_MAX + 1)
129 	return(ENAMETOOLONG);
130 
131   err_code = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_mkdir.grant,
132 			      (vir_bytes) 0, (vir_bytes) lastc, (phys_bytes) len);
133   if(err_code != OK) return(err_code);
134   NUL(lastc, len, sizeof(lastc));
135 
136   caller_uid = fs_m_in.m_vfs_fs_mkdir.uid;
137   caller_gid = fs_m_in.m_vfs_fs_mkdir.gid;
138 
139   /* Get last directory inode */
140   if((ldirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_mkdir.inode)) == NULL)
141       return(ENOENT);
142 
143   /* Next make the inode. If that fails, return error code. */
144   rip = new_node(ldirp, lastc, fs_m_in.m_vfs_fs_mkdir.mode, (block_t) 0);
145 
146   if(rip == NULL || err_code == EEXIST) {
147 	  put_inode(rip);		/* can't make dir: it already exists */
148 	  put_inode(ldirp);
149 	  return(err_code);
150   }
151 
152   /* Get the inode numbers for . and .. to enter in the directory. */
153   dotdot = ldirp->i_num;	/* parent's inode number */
154   dot = rip->i_num;		/* inode number of the new dir itself */
155 
156   /* Now make dir entries for . and .. unless the disk is completely full. */
157   /* Use dot1 and dot2, so the mode of the directory isn't important. */
158   rip->i_mode = fs_m_in.m_vfs_fs_mkdir.mode;	/* set mode */
159   /* enter . in the new dir*/
160   r1 = search_dir(rip, dot1, &dot, ENTER, IGN_PERM, I_DIRECTORY);
161   /* enter .. in the new dir */
162   r2 = search_dir(rip, dot2, &dotdot, ENTER, IGN_PERM, I_DIRECTORY);
163 
164   /* If both . and .. were successfully entered, increment the link counts. */
165   if (r1 == OK && r2 == OK) {
166 	  /* Normal case.  It was possible to enter . and .. in the new dir. */
167 	  rip->i_links_count++;	/* this accounts for . */
168 	  ldirp->i_links_count++;	/* this accounts for .. */
169 	  ldirp->i_dirt = IN_DIRTY;	/* mark parent's inode as dirty */
170   } else {
171 	  /* It was not possible to enter . or .. probably disk was full -
172 	   * links counts haven't been touched. */
173 	  if (search_dir(ldirp, lastc, NULL, DELETE, IGN_PERM, 0) != OK)
174 		  panic("Dir disappeared: %d ", (int) rip->i_num);
175 	  rip->i_links_count--;	/* undo the increment done in new_node() */
176   }
177   rip->i_dirt = IN_DIRTY;		/* either way, i_links_count has changed */
178 
179   put_inode(ldirp);		/* return the inode of the parent dir */
180   put_inode(rip);		/* return the inode of the newly made dir */
181   return(err_code);		/* new_node() always sets 'err_code' */
182 }
183 
184 
185 /*===========================================================================*
186  *                             fs_slink 				     *
187  *===========================================================================*/
188 int fs_slink()
189 {
190   phys_bytes len;
191   struct inode *sip;           /* inode containing symbolic link */
192   struct inode *ldirp;         /* directory containing link */
193   register int r;              /* error code */
194   char string[NAME_MAX];       /* last component of the new dir's path name */
195   char* link_target_buf = NULL;       /* either sip->i_block or bp->b_data */
196   struct buf *bp = NULL;    /* disk buffer for link */
197 
198   caller_uid = fs_m_in.m_vfs_fs_slink.uid;
199   caller_gid = fs_m_in.m_vfs_fs_slink.gid;
200 
201   /* Copy the link name's last component */
202   len = fs_m_in.m_vfs_fs_slink.path_len;
203   if (len > NAME_MAX || len > EXT2_NAME_MAX)
204 	return(ENAMETOOLONG);
205 
206   r = sys_safecopyfrom(VFS_PROC_NR, fs_m_in.m_vfs_fs_slink.grant_path,
207 		       (vir_bytes) 0, (vir_bytes) string, (size_t) len);
208   if (r != OK) return(r);
209   NUL(string, len, sizeof(string));
210 
211   /* Temporarily open the dir. */
212   if( (ldirp = get_inode(fs_dev, fs_m_in.m_vfs_fs_slink.inode)) == NULL)
213 	  return(EINVAL);
214 
215   /* Create the inode for the symlink. */
216   sip = new_node(ldirp, string, (I_SYMBOLIC_LINK | RWX_MODES), 0);
217 
218   /* If we can then create fast symlink (store it in inode),
219    * Otherwise allocate a disk block for the contents of the symlink and
220    * copy contents of symlink (the name pointed to) into first disk block. */
221   if( (r = err_code) == OK) {
222 	if ( (fs_m_in.m_vfs_fs_slink.mem_size + 1) > sip->i_sp->s_block_size) {
223 		r = ENAMETOOLONG;
224 	} else if ((fs_m_in.m_vfs_fs_slink.mem_size + 1) <= MAX_FAST_SYMLINK_LENGTH) {
225 		r = sys_safecopyfrom(VFS_PROC_NR,
226 				     fs_m_in.m_vfs_fs_slink.grant_target,
227 				     (vir_bytes) 0, (vir_bytes) sip->i_block,
228                                      (vir_bytes) fs_m_in.m_vfs_fs_slink.mem_size);
229 		sip->i_dirt = IN_DIRTY;
230 		link_target_buf = (char*) sip->i_block;
231         } else {
232 		if ((bp = new_block(sip, (off_t) 0)) != NULL) {
233 			sys_safecopyfrom(VFS_PROC_NR,
234 					 fs_m_in.m_vfs_fs_slink.grant_target,
235 					 (vir_bytes) 0, (vir_bytes) b_data(bp),
236 					 (vir_bytes) fs_m_in.m_vfs_fs_slink.mem_size);
237 			lmfs_markdirty(bp);
238 			link_target_buf = b_data(bp);
239 		} else {
240 			r = err_code;
241 		}
242 	}
243 	if (r == OK) {
244 		assert(link_target_buf);
245 		link_target_buf[fs_m_in.m_vfs_fs_slink.mem_size] = '\0';
246 		sip->i_size = (off_t) strlen(link_target_buf);
247 		if (sip->i_size != fs_m_in.m_vfs_fs_slink.mem_size) {
248 			  /* This can happen if the user provides a buffer
249 			   * with a \0 in it. This can cause a lot of trouble
250 			   * when the symlink is used later. We could just use
251 			   * the strlen() value, but we want to let the user
252 			   * know he did something wrong. ENAMETOOLONG doesn't
253 			   * exactly describe the error, but there is no
254 			   * ENAMETOOWRONG.
255 			   */
256 			  r = ENAMETOOLONG;
257 		  }
258 	}
259 
260 	put_block(bp, DIRECTORY_BLOCK); /* put_block() accepts NULL. */
261 
262 	if(r != OK) {
263 		sip->i_links_count = NO_LINK;
264 		if (search_dir(ldirp, string, NULL, DELETE, IGN_PERM, 0) != OK)
265 			panic("Symbolic link vanished");
266 	}
267   }
268 
269   /* put_inode() accepts NULL as a noop, so the below are safe. */
270   put_inode(sip);
271   put_inode(ldirp);
272 
273   return(r);
274 }
275 
276 /*===========================================================================*
277  *				new_node				     *
278  *===========================================================================*/
279 static struct inode *new_node(struct inode *ldirp,
280 	char *string, mode_t bits, block_t b0)
281 {
282 /* New_node() is called by fs_open(), fs_mknod(), and fs_mkdir().
283  * In all cases it allocates a new inode, makes a directory entry for it in
284  * the ldirp directory with string name, and initializes it.
285  * It returns a pointer to the inode if it can do this;
286  * otherwise it returns NULL.  It always sets 'err_code'
287  * to an appropriate value (OK or an error code).
288  */
289 
290   register struct inode *rip;
291   register int r;
292 
293   if (ldirp->i_links_count == NO_LINK) { /* Dir does not actually exist */
294 	err_code = ENOENT;
295 	return(NULL);
296   }
297 
298   /* Get final component of the path. */
299   rip = advance(ldirp, string, IGN_PERM);
300 
301   if (S_ISDIR(bits) && (ldirp->i_links_count >= USHRT_MAX ||
302 			ldirp->i_links_count >= LINK_MAX)) {
303         /* New entry is a directory, alas we can't give it a ".." */
304         put_inode(rip);
305         err_code = EMLINK;
306         return(NULL);
307   }
308 
309   if ( rip == NULL && err_code == ENOENT) {
310 	/* Last path component does not exist.  Make new directory entry. */
311 	if ( (rip = alloc_inode(ldirp, bits)) == NULL) {
312 		/* Can't creat new inode: out of inodes. */
313 		return(NULL);
314 	}
315 
316 	/* Force inode to the disk before making directory entry to make
317 	 * the system more robust in the face of a crash: an inode with
318 	 * no directory entry is much better than the opposite.
319 	 */
320 	rip->i_links_count++;
321 	rip->i_block[0] = b0;		/* major/minor device numbers */
322 	rw_inode(rip, WRITING);		/* force inode to disk now */
323 
324 	/* New inode acquired.  Try to make directory entry. */
325 	if ((r=search_dir(ldirp, string, &rip->i_num, ENTER, IGN_PERM,
326 			  rip->i_mode & I_TYPE)) != OK) {
327 		rip->i_links_count--;	/* pity, have to free disk inode */
328 		rip->i_dirt = IN_DIRTY;	/* dirty inodes are written out */
329 		put_inode(rip);	/* this call frees the inode */
330 		err_code = r;
331 		return(NULL);
332 	}
333 
334   } else if (err_code == EENTERMOUNT || err_code == ELEAVEMOUNT) {
335 	r = EEXIST;
336   } else {
337 	/* Either last component exists, or there is some problem. */
338 	if (rip != NULL)
339 		r = EEXIST;
340 	else
341 		r = err_code;
342   }
343 
344   /* The caller has to return the directory inode (*ldirp).  */
345   err_code = r;
346   return(rip);
347 }
348 
349 
350 /*===========================================================================*
351  *				fs_inhibread				     *
352  *===========================================================================*/
353 int fs_inhibread()
354 {
355   struct inode *rip;
356 
357   if((rip = find_inode(fs_dev, fs_m_in.m_vfs_fs_inhibread.inode)) == NULL)
358 	  return(EINVAL);
359 
360   /* inhibit read ahead */
361   rip->i_seek = ISEEK;
362 
363   return(OK);
364 }
365