xref: /minix3/minix/fs/ext2/open.c (revision 0314acfb2d68447dfa1b0b33aa4c25b1cbfa85d3)
1433d6423SLionel Sambuc /* Created (MFS based):
2433d6423SLionel Sambuc  *   February 2010 (Evgeniy Ivanov)
3433d6423SLionel Sambuc  */
4433d6423SLionel Sambuc 
5433d6423SLionel Sambuc #include "fs.h"
6433d6423SLionel Sambuc #include <sys/stat.h>
7433d6423SLionel Sambuc #include <string.h>
8433d6423SLionel Sambuc #include <assert.h>
9433d6423SLionel Sambuc #include "buf.h"
10433d6423SLionel Sambuc #include "inode.h"
11433d6423SLionel Sambuc #include "super.h"
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc static struct inode *new_node(struct inode *ldirp, char *string, mode_t
14970d95ecSDavid van Moolenbroek 	bits, uid_t uid, gid_t gid, block_t z0);
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc /*===========================================================================*
18433d6423SLionel Sambuc  *				fs_create				     *
19433d6423SLionel Sambuc  *===========================================================================*/
fs_create(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid,struct fsdriver_node * node)20970d95ecSDavid van Moolenbroek int fs_create(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid,
21970d95ecSDavid van Moolenbroek 	struct fsdriver_node *node)
22433d6423SLionel Sambuc {
23433d6423SLionel Sambuc   int r;
24433d6423SLionel Sambuc   struct inode *ldirp;
25433d6423SLionel Sambuc   struct inode *rip;
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc   /* Try to make the file. */
28433d6423SLionel Sambuc 
29433d6423SLionel Sambuc   /* Get last directory inode (i.e., directory that will hold the new inode) */
30970d95ecSDavid van Moolenbroek   if ((ldirp = get_inode(fs_dev, dir_nr)) == NULL)
31433d6423SLionel Sambuc 	  return(ENOENT);
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc   /* Create a new inode by calling new_node(). */
34970d95ecSDavid van Moolenbroek   rip = new_node(ldirp, name, mode, uid, gid, NO_BLOCK);
35433d6423SLionel Sambuc   r = err_code;
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc   /* If an error occurred, release inode. */
38433d6423SLionel Sambuc   if (r != OK) {
39433d6423SLionel Sambuc 	  put_inode(ldirp);
40433d6423SLionel Sambuc 	  put_inode(rip);
41433d6423SLionel Sambuc 	  return(r);
42433d6423SLionel Sambuc   }
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc   /* Reply message */
45970d95ecSDavid van Moolenbroek   node->fn_ino_nr = rip->i_num;
46970d95ecSDavid van Moolenbroek   node->fn_mode = rip->i_mode;
47970d95ecSDavid van Moolenbroek   node->fn_size = rip->i_size;
48970d95ecSDavid van Moolenbroek   node->fn_uid = rip->i_uid;
49970d95ecSDavid van Moolenbroek   node->fn_gid = rip->i_gid;
50970d95ecSDavid van Moolenbroek   node->fn_dev = NO_DEV;
51433d6423SLionel Sambuc 
52433d6423SLionel Sambuc   /* Drop parent dir */
53433d6423SLionel Sambuc   put_inode(ldirp);
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc   return(OK);
56433d6423SLionel Sambuc }
57433d6423SLionel Sambuc 
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc /*===========================================================================*
60433d6423SLionel Sambuc  *				fs_mknod				     *
61433d6423SLionel Sambuc  *===========================================================================*/
fs_mknod(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid,dev_t dev)62970d95ecSDavid van Moolenbroek int fs_mknod(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid,
63970d95ecSDavid van Moolenbroek 	dev_t dev)
64433d6423SLionel Sambuc {
65433d6423SLionel Sambuc   struct inode *ip, *ldirp;
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc   /* Get last directory inode */
68970d95ecSDavid van Moolenbroek   if((ldirp = get_inode(fs_dev, dir_nr)) == NULL)
69433d6423SLionel Sambuc 	  return(ENOENT);
70433d6423SLionel Sambuc 
71433d6423SLionel Sambuc   /* Try to create the new node */
72970d95ecSDavid van Moolenbroek   ip = new_node(ldirp, name, mode, uid, gid, (block_t) dev);
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc   put_inode(ip);
75433d6423SLionel Sambuc   put_inode(ldirp);
76433d6423SLionel Sambuc   return(err_code);
77433d6423SLionel Sambuc }
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 
80433d6423SLionel Sambuc /*===========================================================================*
81433d6423SLionel Sambuc  *				fs_mkdir				     *
82433d6423SLionel Sambuc  *===========================================================================*/
fs_mkdir(ino_t dir_nr,char * name,mode_t mode,uid_t uid,gid_t gid)83970d95ecSDavid van Moolenbroek int fs_mkdir(ino_t dir_nr, char *name, mode_t mode, uid_t uid, gid_t gid)
84433d6423SLionel Sambuc {
85433d6423SLionel Sambuc   int r1, r2;			/* status codes */
86433d6423SLionel Sambuc   ino_t dot, dotdot;		/* inode numbers for . and .. */
87433d6423SLionel Sambuc   struct inode *rip, *ldirp;
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc   /* Get last directory inode */
90970d95ecSDavid van Moolenbroek   if((ldirp = get_inode(fs_dev, dir_nr)) == NULL)
91433d6423SLionel Sambuc       return(ENOENT);
92433d6423SLionel Sambuc 
93433d6423SLionel Sambuc   /* Next make the inode. If that fails, return error code. */
94970d95ecSDavid van Moolenbroek   rip = new_node(ldirp, name, mode, uid, gid, (block_t) 0);
95433d6423SLionel Sambuc 
96433d6423SLionel Sambuc   if(rip == NULL || err_code == EEXIST) {
97433d6423SLionel Sambuc 	  put_inode(rip);		/* can't make dir: it already exists */
98433d6423SLionel Sambuc 	  put_inode(ldirp);
99433d6423SLionel Sambuc 	  return(err_code);
100433d6423SLionel Sambuc   }
101433d6423SLionel Sambuc 
102433d6423SLionel Sambuc   /* Get the inode numbers for . and .. to enter in the directory. */
103433d6423SLionel Sambuc   dotdot = ldirp->i_num;	/* parent's inode number */
104433d6423SLionel Sambuc   dot = rip->i_num;		/* inode number of the new dir itself */
105433d6423SLionel Sambuc 
106433d6423SLionel Sambuc   /* Now make dir entries for . and .. unless the disk is completely full. */
107433d6423SLionel Sambuc   /* enter . in the new dir*/
108970d95ecSDavid van Moolenbroek   r1 = search_dir(rip, ".", &dot, ENTER, I_DIRECTORY);
109433d6423SLionel Sambuc   /* enter .. in the new dir */
110970d95ecSDavid van Moolenbroek   r2 = search_dir(rip, "..", &dotdot, ENTER, I_DIRECTORY);
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc   /* If both . and .. were successfully entered, increment the link counts. */
113433d6423SLionel Sambuc   if (r1 == OK && r2 == OK) {
114433d6423SLionel Sambuc 	  /* Normal case.  It was possible to enter . and .. in the new dir. */
115433d6423SLionel Sambuc 	  rip->i_links_count++;	/* this accounts for . */
116433d6423SLionel Sambuc 	  ldirp->i_links_count++;	/* this accounts for .. */
117433d6423SLionel Sambuc 	  ldirp->i_dirt = IN_DIRTY;	/* mark parent's inode as dirty */
118433d6423SLionel Sambuc   } else {
119433d6423SLionel Sambuc 	  /* It was not possible to enter . or .. probably disk was full -
120433d6423SLionel Sambuc 	   * links counts haven't been touched. */
121970d95ecSDavid van Moolenbroek 	  if (search_dir(ldirp, name, NULL, DELETE, 0) != OK)
122433d6423SLionel Sambuc 		  panic("Dir disappeared: %d ", (int) rip->i_num);
123433d6423SLionel Sambuc 	  rip->i_links_count--;	/* undo the increment done in new_node() */
124433d6423SLionel Sambuc   }
125433d6423SLionel Sambuc   rip->i_dirt = IN_DIRTY;	/* either way, i_links_count has changed */
126433d6423SLionel Sambuc 
127433d6423SLionel Sambuc   put_inode(ldirp);		/* return the inode of the parent dir */
128433d6423SLionel Sambuc   put_inode(rip);		/* return the inode of the newly made dir */
129433d6423SLionel Sambuc   return(err_code);		/* new_node() always sets 'err_code' */
130433d6423SLionel Sambuc }
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc 
133433d6423SLionel Sambuc /*===========================================================================*
134433d6423SLionel Sambuc  *                             fs_slink 				     *
135433d6423SLionel Sambuc  *===========================================================================*/
fs_slink(ino_t dir_nr,char * name,uid_t uid,gid_t gid,struct fsdriver_data * data,size_t bytes)136970d95ecSDavid van Moolenbroek int fs_slink(ino_t dir_nr, char *name, uid_t uid, gid_t gid,
137970d95ecSDavid van Moolenbroek 	struct fsdriver_data *data, size_t bytes)
138433d6423SLionel Sambuc {
139433d6423SLionel Sambuc   struct inode *sip;           /* inode containing symbolic link */
140433d6423SLionel Sambuc   struct inode *ldirp;         /* directory containing link */
141433d6423SLionel Sambuc   register int r;              /* error code */
142433d6423SLionel Sambuc   char* link_target_buf = NULL;       /* either sip->i_block or bp->b_data */
143433d6423SLionel Sambuc   struct buf *bp = NULL;    /* disk buffer for link */
144433d6423SLionel Sambuc 
145433d6423SLionel Sambuc   /* Temporarily open the dir. */
146970d95ecSDavid van Moolenbroek   if( (ldirp = get_inode(fs_dev, dir_nr)) == NULL)
147433d6423SLionel Sambuc 	  return(EINVAL);
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc   /* Create the inode for the symlink. */
150970d95ecSDavid van Moolenbroek   sip = new_node(ldirp, name, (I_SYMBOLIC_LINK | RWX_MODES), uid, gid, 0);
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc   /* If we can then create fast symlink (store it in inode),
153433d6423SLionel Sambuc    * Otherwise allocate a disk block for the contents of the symlink and
154433d6423SLionel Sambuc    * copy contents of symlink (the name pointed to) into first disk block. */
155433d6423SLionel Sambuc   if( (r = err_code) == OK) {
156970d95ecSDavid van Moolenbroek 	if (bytes + 1 > sip->i_sp->s_block_size) {
157433d6423SLionel Sambuc 		r = ENAMETOOLONG;
158970d95ecSDavid van Moolenbroek 	} else if ((bytes + 1) <= MAX_FAST_SYMLINK_LENGTH) {
159970d95ecSDavid van Moolenbroek 		r = fsdriver_copyin(data, 0, (char *) sip->i_block, bytes);
160433d6423SLionel Sambuc 		sip->i_dirt = IN_DIRTY;
161433d6423SLionel Sambuc 		link_target_buf = (char*) sip->i_block;
162433d6423SLionel Sambuc         } else {
163433d6423SLionel Sambuc 		if ((bp = new_block(sip, (off_t) 0)) != NULL) {
164970d95ecSDavid van Moolenbroek 			r = fsdriver_copyin(data, 0, b_data(bp), bytes);
165433d6423SLionel Sambuc 			lmfs_markdirty(bp);
166433d6423SLionel Sambuc 			link_target_buf = b_data(bp);
167433d6423SLionel Sambuc 		} else {
168433d6423SLionel Sambuc 			r = err_code;
169433d6423SLionel Sambuc 		}
170433d6423SLionel Sambuc 	}
171433d6423SLionel Sambuc 	if (r == OK) {
172433d6423SLionel Sambuc 		assert(link_target_buf);
173970d95ecSDavid van Moolenbroek 		link_target_buf[bytes] = '\0';
174433d6423SLionel Sambuc 		sip->i_size = (off_t) strlen(link_target_buf);
175970d95ecSDavid van Moolenbroek 		if (sip->i_size != bytes) {
176433d6423SLionel Sambuc 			  /* This can happen if the user provides a buffer
177433d6423SLionel Sambuc 			   * with a \0 in it. This can cause a lot of trouble
178433d6423SLionel Sambuc 			   * when the symlink is used later. We could just use
179433d6423SLionel Sambuc 			   * the strlen() value, but we want to let the user
180433d6423SLionel Sambuc 			   * know he did something wrong. ENAMETOOLONG doesn't
181433d6423SLionel Sambuc 			   * exactly describe the error, but there is no
182433d6423SLionel Sambuc 			   * ENAMETOOWRONG.
183433d6423SLionel Sambuc 			   */
184433d6423SLionel Sambuc 			  r = ENAMETOOLONG;
185433d6423SLionel Sambuc 		  }
186433d6423SLionel Sambuc 	}
187433d6423SLionel Sambuc 
188*0314acfbSDavid van Moolenbroek 	put_block(bp); /* put_block() accepts NULL. */
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc 	if(r != OK) {
191433d6423SLionel Sambuc 		sip->i_links_count = NO_LINK;
192970d95ecSDavid van Moolenbroek 		if (search_dir(ldirp, name, NULL, DELETE, 0) != OK)
193433d6423SLionel Sambuc 			panic("Symbolic link vanished");
194433d6423SLionel Sambuc 	}
195433d6423SLionel Sambuc   }
196433d6423SLionel Sambuc 
197433d6423SLionel Sambuc   /* put_inode() accepts NULL as a noop, so the below are safe. */
198433d6423SLionel Sambuc   put_inode(sip);
199433d6423SLionel Sambuc   put_inode(ldirp);
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc   return(r);
202433d6423SLionel Sambuc }
203433d6423SLionel Sambuc 
204433d6423SLionel Sambuc /*===========================================================================*
205433d6423SLionel Sambuc  *				new_node				     *
206433d6423SLionel Sambuc  *===========================================================================*/
new_node(struct inode * ldirp,char * string,mode_t bits,uid_t uid,gid_t gid,block_t b0)207433d6423SLionel Sambuc static struct inode *new_node(struct inode *ldirp,
208970d95ecSDavid van Moolenbroek 	char *string, mode_t bits, uid_t uid, gid_t gid, block_t b0)
209433d6423SLionel Sambuc {
210433d6423SLionel Sambuc /* New_node() is called by fs_open(), fs_mknod(), and fs_mkdir().
211433d6423SLionel Sambuc  * In all cases it allocates a new inode, makes a directory entry for it in
212433d6423SLionel Sambuc  * the ldirp directory with string name, and initializes it.
213433d6423SLionel Sambuc  * It returns a pointer to the inode if it can do this;
214433d6423SLionel Sambuc  * otherwise it returns NULL.  It always sets 'err_code'
215433d6423SLionel Sambuc  * to an appropriate value (OK or an error code).
216433d6423SLionel Sambuc  */
217433d6423SLionel Sambuc 
218433d6423SLionel Sambuc   register struct inode *rip;
219433d6423SLionel Sambuc   register int r;
220433d6423SLionel Sambuc 
221433d6423SLionel Sambuc   if (ldirp->i_links_count == NO_LINK) { /* Dir does not actually exist */
222433d6423SLionel Sambuc 	err_code = ENOENT;
223433d6423SLionel Sambuc 	return(NULL);
224433d6423SLionel Sambuc   }
225433d6423SLionel Sambuc 
226433d6423SLionel Sambuc   if (S_ISDIR(bits) && (ldirp->i_links_count >= USHRT_MAX ||
227433d6423SLionel Sambuc 			ldirp->i_links_count >= LINK_MAX)) {
228433d6423SLionel Sambuc         /* New entry is a directory, alas we can't give it a ".." */
229433d6423SLionel Sambuc         err_code = EMLINK;
230433d6423SLionel Sambuc         return(NULL);
231433d6423SLionel Sambuc   }
232433d6423SLionel Sambuc 
233970d95ecSDavid van Moolenbroek   /* Get final component of the path. */
234970d95ecSDavid van Moolenbroek   rip = advance(ldirp, string);
235970d95ecSDavid van Moolenbroek 
236433d6423SLionel Sambuc   if ( rip == NULL && err_code == ENOENT) {
237433d6423SLionel Sambuc 	/* Last path component does not exist.  Make new directory entry. */
238970d95ecSDavid van Moolenbroek 	if ( (rip = alloc_inode(ldirp, bits, uid, gid)) == NULL) {
239433d6423SLionel Sambuc 		/* Can't creat new inode: out of inodes. */
240433d6423SLionel Sambuc 		return(NULL);
241433d6423SLionel Sambuc 	}
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 	/* Force inode to the disk before making directory entry to make
244433d6423SLionel Sambuc 	 * the system more robust in the face of a crash: an inode with
245433d6423SLionel Sambuc 	 * no directory entry is much better than the opposite.
246433d6423SLionel Sambuc 	 */
247433d6423SLionel Sambuc 	rip->i_links_count++;
248433d6423SLionel Sambuc 	rip->i_block[0] = b0;		/* major/minor device numbers */
249433d6423SLionel Sambuc 	rw_inode(rip, WRITING);		/* force inode to disk now */
250433d6423SLionel Sambuc 
251433d6423SLionel Sambuc 	/* New inode acquired.  Try to make directory entry. */
252970d95ecSDavid van Moolenbroek 	if ((r=search_dir(ldirp, string, &rip->i_num, ENTER,
253433d6423SLionel Sambuc 			  rip->i_mode & I_TYPE)) != OK) {
254433d6423SLionel Sambuc 		rip->i_links_count--;	/* pity, have to free disk inode */
255433d6423SLionel Sambuc 		rip->i_dirt = IN_DIRTY;	/* dirty inodes are written out */
256433d6423SLionel Sambuc 		put_inode(rip);	/* this call frees the inode */
257433d6423SLionel Sambuc 		err_code = r;
258433d6423SLionel Sambuc 		return(NULL);
259433d6423SLionel Sambuc 	}
260433d6423SLionel Sambuc 
261433d6423SLionel Sambuc   } else {
262433d6423SLionel Sambuc 	/* Either last component exists, or there is some problem. */
263433d6423SLionel Sambuc 	if (rip != NULL)
264433d6423SLionel Sambuc 		r = EEXIST;
265433d6423SLionel Sambuc 	else
266433d6423SLionel Sambuc 		r = err_code;
267433d6423SLionel Sambuc   }
268433d6423SLionel Sambuc 
269433d6423SLionel Sambuc   /* The caller has to return the directory inode (*ldirp).  */
270433d6423SLionel Sambuc   err_code = r;
271433d6423SLionel Sambuc   return(rip);
272433d6423SLionel Sambuc }
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc /*===========================================================================*
276970d95ecSDavid van Moolenbroek  *				fs_seek					     *
277433d6423SLionel Sambuc  *===========================================================================*/
fs_seek(ino_t ino_nr)278970d95ecSDavid van Moolenbroek void fs_seek(ino_t ino_nr)
279433d6423SLionel Sambuc {
280433d6423SLionel Sambuc   struct inode *rip;
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc   /* inhibit read ahead */
283970d95ecSDavid van Moolenbroek   if ((rip = find_inode(fs_dev, ino_nr)) != NULL)
284433d6423SLionel Sambuc 	rip->i_seek = ISEEK;
285433d6423SLionel Sambuc }
286