1 /*
2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  *
26  * http://www.sgi.com
27  *
28  * For further information regarding this notice, see:
29  *
30  * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31  */
32 
33 /* $Id: rmobj.c,v 1.5 2009/07/20 10:59:32 vapier Exp $ */
34 
35 /**********************************************************
36  *
37  *    OS Testing - Silicon Graphics, Inc.
38  *
39  *    FUNCTION NAME     : rmobj()
40  *
41  *    FUNCTION TITLE    : Remove an object
42  *
43  *    SYNOPSIS:
44  *      int rmobj(char *obj, char **errmsg)
45  *
46  *    AUTHOR            : Kent Rogers
47  *
48  *    INITIAL RELEASE   : UNICOS 7.0
49  *
50  *    USER DESCRIPTION
51  *      This routine will remove the specified object.  If the specified
52  *      object is a directory, it will recursively remove the directory
53  *      and everything underneath it.  It assumes that it has privilege
54  *      to remove everything that it tries to remove.  If rmobj() encounters
55  *      any problems, and errmsg is not NULL, errmsg is set to point to a
56  *      string explaining the error.
57  *
58  *    DETAILED DESCRIPTION
59  *      Allocate space for the directory and its contents
60  *      Open the directory to get access to what is in it
61  *      Loop through the objects in the directory:
62  *        If the object is not "." or "..":
63  *          Determine the file type by calling lstat()
64  *          If the object is not a directory:
65  *            Remove the object with unlink()
66  *         Else:
67  *            Call rmobj(object) to remove the object's contents
68  *            Determine the link count on object by calling lstat()
69  *            If the link count >= 3:
70  *              Remove the directory with unlink()
71  *            Else
72  *               Remove the directory with rmdir()
73  *      Close the directory and free the pointers
74  *
75  *    RETURN VALUE
76  *      If there are any problems, rmobj() will set errmsg (if it was not
77  *      NULL) and return -1.  Otherwise it will return 0.
78  *
79  ************************************************************/
80 #define _GNU_SOURCE
81 #include <errno.h>		/* for errno */
82 #include <stdio.h>		/* for NULL */
83 #include <stdlib.h>		/* for malloc() */
84 #include <string.h>		/* for string function */
85 #include <limits.h>		/* for PATH_MAX */
86 #include <sys/types.h>		/* for opendir(), readdir(), closedir(), stat() */
87 #include <sys/stat.h>		/* for [l]stat() */
88 #include <fcntl.h>
89 #include <dirent.h>		/* for opendir(), readdir(), closedir() */
90 #include <unistd.h>		/* for rmdir(), unlink() */
91 #include "rmobj.h"
92 
93 #define SYSERR strerror(errno)
94 
rmobj(char * obj,char ** errmsg)95 int rmobj(char *obj, char **errmsg)
96 {
97 	int ret_val = 0;	/* return value from this routine */
98 	DIR *dir;		/* pointer to a directory */
99 	struct dirent *dir_ent;	/* pointer to directory entries */
100 	char dirobj[PATH_MAX];	/* object inside directory to modify */
101 	struct stat statbuf;	/* used to hold stat information */
102 	static char err_msg[1024];	/* error message */
103 	int fd;
104 
105 	/* Determine the file type */
106 
107 	fd = open(obj, O_DIRECTORY | O_NOFOLLOW);
108 	if (fd != -1) {
109 		close(fd);
110 		/* object is a directory */
111 
112 		/* Do NOT perform the request if the directory is "/" */
113 		if (!strcmp(obj, "/")) {
114 			if (errmsg != NULL) {
115 				sprintf(err_msg, "Cannot remove /");
116 				*errmsg = err_msg;
117 			}
118 			return -1;
119 		}
120 
121 		/* Open the directory to get access to what is in it */
122 		if ((dir = opendir(obj)) == NULL) {
123 			if (rmdir(obj) != 0) {
124 				if (errmsg != NULL) {
125 					sprintf(err_msg,
126 						"rmdir(%s) failed; errno=%d: %s",
127 						obj, errno, SYSERR);
128 					*errmsg = err_msg;
129 				}
130 				return -1;
131 			} else {
132 				return 0;
133 			}
134 		}
135 
136 		/* Loop through the entries in the directory, removing each one */
137 		for (dir_ent = (struct dirent *)readdir(dir);
138 		     dir_ent != NULL; dir_ent = (struct dirent *)readdir(dir)) {
139 
140 			/* Don't remove "." or ".." */
141 			if (!strcmp(dir_ent->d_name, ".")
142 			    || !strcmp(dir_ent->d_name, ".."))
143 				continue;
144 
145 			/* Recursively call this routine to remove the current entry */
146 			sprintf(dirobj, "%s/%s", obj, dir_ent->d_name);
147 			if (rmobj(dirobj, errmsg) != 0)
148 				ret_val = -1;
149 		}
150 
151 		/* Close the directory */
152 		closedir(dir);
153 
154 		/* If there were problems removing an entry, don't attempt to
155 		   remove the directory itself */
156 		if (ret_val == -1)
157 			return -1;
158 
159 		/* Get the link count, now that all the entries have been removed */
160 		if (lstat(obj, &statbuf) < 0) {
161 			if (errmsg != NULL) {
162 				sprintf(err_msg,
163 					"lstat(%s) failed; errno=%d: %s", obj,
164 					errno, SYSERR);
165 				*errmsg = err_msg;
166 			}
167 			return -1;
168 		}
169 
170 		/* Remove the directory itself */
171 		if (statbuf.st_nlink >= 3) {
172 			/* The directory is linked; unlink() must be used */
173 			if (unlink(obj) < 0) {
174 				if (errmsg != NULL) {
175 					sprintf(err_msg,
176 						"unlink(%s) failed; errno=%d: %s",
177 						obj, errno, SYSERR);
178 					*errmsg = err_msg;
179 				}
180 				return -1;
181 			}
182 		} else {
183 			/* The directory is not linked; remove() can be used */
184 			if (remove(obj) < 0) {
185 				if (errmsg != NULL) {
186 					sprintf(err_msg,
187 						"remove(%s) failed; errno=%d: %s",
188 						obj, errno, SYSERR);
189 					*errmsg = err_msg;
190 				}
191 				return -1;
192 			}
193 		}
194 	} else {
195 		/* object is not a directory; just use unlink() */
196 		if (unlink(obj) < 0) {
197 			if (errmsg != NULL) {
198 				sprintf(err_msg,
199 					"unlink(%s) failed; errno=%d: %s", obj,
200 					errno, SYSERR);
201 				*errmsg = err_msg;
202 			}
203 			return -1;
204 		}
205 	}			/* if obj is a directory */
206 
207 	/*
208 	 * Everything must have went ok.
209 	 */
210 	return 0;
211 }				/* rmobj() */
212