vm: fix potential null deref
[minix.git] / servers / ext2 / open.c
bloba2856733083fb83bdf95867f20523fbbc5202846
1 /* Created (MFS based):
2 * February 2010 (Evgeniy Ivanov)
3 */
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>
15 static struct inode *new_node(struct inode *ldirp, char *string, mode_t
16 bits, block_t z0);
19 /*===========================================================================*
20 * fs_create *
21 *===========================================================================*/
22 int fs_create()
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];
31 /* Read request message */
32 omode = (mode_t) fs_m_in.REQ_MODE;
33 caller_uid = (uid_t) fs_m_in.REQ_UID;
34 caller_gid = (gid_t) fs_m_in.REQ_GID;
36 /* Try to make the file. */
38 /* Copy the last component (i.e., file name) */
39 len = fs_m_in.REQ_PATH_LEN; /* including trailing '\0' */
40 if (len > NAME_MAX + 1 || len > EXT2_NAME_MAX + 1)
41 return(ENAMETOOLONG);
43 err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_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));
48 /* Get last directory inode (i.e., directory that will hold the new inode) */
49 if ((ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL)
50 return(ENOENT);
52 /* Create a new inode by calling new_node(). */
53 rip = new_node(ldirp, lastc, omode, NO_BLOCK);
54 r = err_code;
56 /* If an error occurred, release inode. */
57 if (r != OK) {
58 put_inode(ldirp);
59 put_inode(rip);
60 return(r);
63 /* Reply message */
64 fs_m_out.RES_INODE_NR = rip->i_num;
65 fs_m_out.RES_MODE = rip->i_mode;
66 fs_m_out.RES_FILE_SIZE_LO = rip->i_size;
68 /* This values are needed for the execution */
69 fs_m_out.RES_UID = rip->i_uid;
70 fs_m_out.RES_GID = rip->i_gid;
72 /* Drop parent dir */
73 put_inode(ldirp);
75 return(OK);
79 /*===========================================================================*
80 * fs_mknod *
81 *===========================================================================*/
82 int fs_mknod()
84 struct inode *ip, *ldirp;
85 char lastc[NAME_MAX + 1];
86 phys_bytes len;
88 /* Copy the last component and set up caller's user and group id */
89 len = fs_m_in.REQ_PATH_LEN; /* including trailing '\0' */
90 if (len > NAME_MAX + 1 || len > EXT2_NAME_MAX + 1)
91 return(ENAMETOOLONG);
93 err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_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));
98 caller_uid = (uid_t) fs_m_in.REQ_UID;
99 caller_gid = (gid_t) fs_m_in.REQ_GID;
101 /* Get last directory inode */
102 if((ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL)
103 return(ENOENT);
105 /* Try to create the new node */
106 ip = new_node(ldirp, lastc, (mode_t) fs_m_in.REQ_MODE,
107 (block_t) fs_m_in.REQ_DEV);
109 put_inode(ip);
110 put_inode(ldirp);
111 return(err_code);
115 /*===========================================================================*
116 * fs_mkdir *
117 *===========================================================================*/
118 int fs_mkdir()
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;
126 /* Copy the last component and set up caller's user and group id */
127 len = fs_m_in.REQ_PATH_LEN; /* including trailing '\0' */
128 if (len > NAME_MAX + 1 || len > EXT2_NAME_MAX + 1)
129 return(ENAMETOOLONG);
131 err_code = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_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));
136 caller_uid = (uid_t) fs_m_in.REQ_UID;
137 caller_gid = (gid_t) fs_m_in.REQ_GID;
139 /* Get last directory inode */
140 if((ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL)
141 return(ENOENT);
143 /* Next make the inode. If that fails, return error code. */
144 rip = new_node(ldirp, lastc, (ino_t) fs_m_in.REQ_MODE, (block_t) 0);
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);
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 */
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 = (mode_t) fs_m_in.REQ_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);
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 ", rip->i_num);
175 rip->i_links_count--; /* undo the increment done in new_node() */
177 rip->i_dirt = IN_DIRTY; /* either way, i_links_count has changed */
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' */
185 /*===========================================================================*
186 * fs_slink *
187 *===========================================================================*/
188 int fs_slink()
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 */
198 caller_uid = (uid_t) fs_m_in.REQ_UID;
199 caller_gid = (gid_t) fs_m_in.REQ_GID;
201 /* Copy the link name's last component */
202 len = fs_m_in.REQ_PATH_LEN;
203 if (len > NAME_MAX || len > EXT2_NAME_MAX)
204 return(ENAMETOOLONG);
206 r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_GRANT,
207 (vir_bytes) 0, (vir_bytes) string, (size_t) len);
208 if (r != OK) return(r);
209 NUL(string, len, sizeof(string));
211 /* Temporarily open the dir. */
212 if( (ldirp = get_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL)
213 return(EINVAL);
215 /* Create the inode for the symlink. */
216 sip = new_node(ldirp, string, (mode_t) (I_SYMBOLIC_LINK | RWX_MODES),
217 (block_t) 0);
219 /* If we can then create fast symlink (store it in inode),
220 * Otherwise allocate a disk block for the contents of the symlink and
221 * copy contents of symlink (the name pointed to) into first disk block. */
222 if( (r = err_code) == OK) {
223 if ( (fs_m_in.REQ_MEM_SIZE + 1) > sip->i_sp->s_block_size) {
224 r = ENAMETOOLONG;
225 } else if ((fs_m_in.REQ_MEM_SIZE + 1) <= MAX_FAST_SYMLINK_LENGTH) {
226 r = sys_safecopyfrom(VFS_PROC_NR,
227 (cp_grant_id_t) fs_m_in.REQ_GRANT3,
228 (vir_bytes) 0, (vir_bytes) sip->i_block,
229 (vir_bytes) fs_m_in.REQ_MEM_SIZE);
230 sip->i_dirt = IN_DIRTY;
231 link_target_buf = (char*) sip->i_block;
232 } else {
233 if ((bp = new_block(sip, (off_t) 0)) != NULL) {
234 sys_safecopyfrom(VFS_PROC_NR,
235 (cp_grant_id_t) fs_m_in.REQ_GRANT3,
236 (vir_bytes) 0, (vir_bytes) b_data(bp),
237 (vir_bytes) fs_m_in.REQ_MEM_SIZE);
238 lmfs_markdirty(bp);
239 link_target_buf = b_data(bp);
240 } else {
241 r = err_code;
244 if (r == OK) {
245 assert(link_target_buf);
246 link_target_buf[fs_m_in.REQ_MEM_SIZE] = '\0';
247 sip->i_size = (off_t) strlen(link_target_buf);
248 if (sip->i_size != fs_m_in.REQ_MEM_SIZE) {
249 /* This can happen if the user provides a buffer
250 * with a \0 in it. This can cause a lot of trouble
251 * when the symlink is used later. We could just use
252 * the strlen() value, but we want to let the user
253 * know he did something wrong. ENAMETOOLONG doesn't
254 * exactly describe the error, but there is no
255 * ENAMETOOWRONG.
257 r = ENAMETOOLONG;
261 put_block(bp, DIRECTORY_BLOCK); /* put_block() accepts NULL. */
263 if(r != OK) {
264 sip->i_links_count = NO_LINK;
265 if (search_dir(ldirp, string, NULL, DELETE, IGN_PERM, 0) != OK)
266 panic("Symbolic link vanished");
270 /* put_inode() accepts NULL as a noop, so the below are safe. */
271 put_inode(sip);
272 put_inode(ldirp);
274 return(r);
277 /*===========================================================================*
278 * new_node *
279 *===========================================================================*/
280 static struct inode *new_node(struct inode *ldirp,
281 char *string, mode_t bits, block_t b0)
283 /* New_node() is called by fs_open(), fs_mknod(), and fs_mkdir().
284 * In all cases it allocates a new inode, makes a directory entry for it in
285 * the ldirp directory with string name, and initializes it.
286 * It returns a pointer to the inode if it can do this;
287 * otherwise it returns NULL. It always sets 'err_code'
288 * to an appropriate value (OK or an error code).
291 register struct inode *rip;
292 register int r;
294 if (ldirp->i_links_count == NO_LINK) { /* Dir does not actually exist */
295 err_code = ENOENT;
296 return(NULL);
299 /* Get final component of the path. */
300 rip = advance(ldirp, string, IGN_PERM);
302 if (S_ISDIR(bits) && (ldirp->i_links_count >= USHRT_MAX ||
303 ldirp->i_links_count >= LINK_MAX)) {
304 /* New entry is a directory, alas we can't give it a ".." */
305 put_inode(rip);
306 err_code = EMLINK;
307 return(NULL);
310 if ( rip == NULL && err_code == ENOENT) {
311 /* Last path component does not exist. Make new directory entry. */
312 if ( (rip = alloc_inode(ldirp, bits)) == NULL) {
313 /* Can't creat new inode: out of inodes. */
314 return(NULL);
317 /* Force inode to the disk before making directory entry to make
318 * the system more robust in the face of a crash: an inode with
319 * no directory entry is much better than the opposite.
321 rip->i_links_count++;
322 rip->i_block[0] = b0; /* major/minor device numbers */
323 rw_inode(rip, WRITING); /* force inode to disk now */
325 /* New inode acquired. Try to make directory entry. */
326 if ((r=search_dir(ldirp, string, &rip->i_num, ENTER, IGN_PERM,
327 rip->i_mode & I_TYPE)) != OK) {
328 rip->i_links_count--; /* pity, have to free disk inode */
329 rip->i_dirt = IN_DIRTY; /* dirty inodes are written out */
330 put_inode(rip); /* this call frees the inode */
331 err_code = r;
332 return(NULL);
335 } else if (err_code == EENTERMOUNT || err_code == ELEAVEMOUNT) {
336 r = EEXIST;
337 } else {
338 /* Either last component exists, or there is some problem. */
339 if (rip != NULL)
340 r = EEXIST;
341 else
342 r = err_code;
345 /* The caller has to return the directory inode (*ldirp). */
346 err_code = r;
347 return(rip);
351 /*===========================================================================*
352 * fs_inhibread *
353 *===========================================================================*/
354 int fs_inhibread()
356 struct inode *rip;
358 if((rip = find_inode(fs_dev, (ino_t) fs_m_in.REQ_INODE_NR)) == NULL)
359 return(EINVAL);
361 /* inhibit read ahead */
362 rip->i_seek = ISEEK;
364 return(OK);