fchmod02.c revision ec6edca7aa42b6affd989ef91b5897f96795e40f
1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/*
21 * Test Name: fchmod02
22 *
23 * Test Description:
24 *  Verify that, fchmod(2) will succeed to change the mode of a file/directory
25 *  set the sticky bit on it if invoked by root (uid = 0) process with
26 *  the following constraints,
27 *	- the process is not the owner of the file/directory.
28 *	- the effective group ID or one of the supplementary group ID's of the
29 *	  process is equal to the group ID of the file/directory.
30 *
31 * Expected Result:
32 *  fchmod() should return value 0 on success and succeeds to set sticky bit
33 *  on the specified file.
34 *
35 * Algorithm:
36 *  Setup:
37 *   Setup signal handling.
38 *   Create temporary directory.
39 *   Pause for SIGUSR1 if option specified.
40 *
41 *  Test:
42 *   Loop if the proper options are given.
43 *   Execute system call
44 *   Check return code, if system call failed (return=-1)
45 *   	Log the errno and Issue a FAIL message.
46 *   Otherwise,
47 *   	Verify the Functionality of system call
48 *      if successful,
49 *      	Issue Functionality-Pass message.
50 *      Otherwise,
51 *		Issue Functionality-Fail message.
52 *  Cleanup:
53 *   Print errno log and/or timing stats if options given
54 *   Delete the temporary directory created.
55 *
56 * Usage:  <for command-line>
57 *  fchmod02 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
58 *     where,  -c n : Run n copies concurrently.
59 *             -f   : Turn off functionality Testing.
60 *	       -i n : Execute test n times.
61 *	       -I x : Execute test for x seconds.
62 *	       -P x : Pause for x seconds between iterations.
63 *	       -t   : Turn on syscall timing.
64 *
65 * HISTORY
66 *	07/2001 Ported by Wayne Boyer
67 *
68 * RESTRICTIONS:
69 *  This test should be run by 'super-user' (root) only.
70 *
71 */
72
73#include <stdio.h>
74#include <sys/types.h>
75#include <sys/stat.h>
76#include <sys/fcntl.h>
77#include <errno.h>
78#include <string.h>
79#include <signal.h>
80#include <grp.h>
81#include <pwd.h>
82
83#include "test.h"
84#include "usctest.h"
85
86#define LTPUSER		"nobody"
87#define LTPGRP		"users"
88#define FILE_MODE 	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
89#define PERMS		01777	/*
90				 * Mode permissions of test file with sticky
91				 * bit set.
92				 */
93#define TESTFILE	"testfile"
94
95int fd;				/* file descriptor variable */
96char *TCID = "fchmod02";	/* Test program identifier.    */
97int TST_TOTAL = 1;		/* Total number of test cases. */
98
99void setup();			/* Main setup function for the test */
100void cleanup();			/* Main cleanup function for the test */
101
102int main(int ac, char **av)
103{
104	struct stat stat_buf;	/* stat(2) struct contents */
105	int lc;			/* loop counter */
106	char *msg;		/* message returned from parse_opts */
107
108	/* Parse standard options given to run the test. */
109	msg = parse_opts(ac, av, NULL, NULL);
110	if (msg != NULL) {
111		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
112
113	}
114
115	setup();
116
117	for (lc = 0; TEST_LOOPING(lc); lc++) {
118
119		Tst_count = 0;
120
121		/*
122		 * Call fchmod(2) with specified mode argument
123		 * (sticky-bit set) on testfile.
124		 */
125		TEST(fchmod(fd, PERMS));
126
127		if (TEST_RETURN == -1) {
128			tst_resm(TFAIL, "fchmod(%d, %#o) Failed, errno=%d : %s",
129				 fd, PERMS, TEST_ERRNO, strerror(TEST_ERRNO));
130			continue;
131		}
132		/*
133		 * Perform functional verification if test
134		 * executed without (-f) option.
135		 */
136		if (STD_FUNCTIONAL_TEST) {
137			/*
138			 * Get the testfile information using
139			 * fstat(2).
140			 */
141			if (fstat(fd, &stat_buf) < 0) {
142				tst_brkm(TFAIL, cleanup, "fstat(2) of %s "
143					 "failed, errno:%d", TESTFILE,
144					 TEST_ERRNO);
145			}
146
147			/* Check for expected mode permissions */
148			if ((stat_buf.st_mode & PERMS) == PERMS) {
149				tst_resm(TPASS, "Functionality of fchmod(%d, "
150					 "%#o) Successful", fd, PERMS);
151			} else {
152				tst_resm(TFAIL, "%s: Incorrect modes 0%03o, "
153					 "Expected 0%03o", TESTFILE,
154					 stat_buf.st_mode, PERMS);
155			}
156		} else {
157			tst_resm(TPASS, "call succeeded");
158		}
159	}
160
161	cleanup();
162
163	tst_exit();
164}
165
166/*
167 * void
168 * setup() - performs all ONE TIME setup for this test.
169 *  Create a temporary directory and change directory to it.
170 *  Create a test file under temporary directory.
171 *  Change the ownership of test file to that of "ltpuser1" user.
172 */
173void setup()
174{
175	struct passwd *ltpuser;	/* password struct for ltpuser1 */
176	struct group *ltpgroup;	/* group struct for ltpuser1 */
177	gid_t group1_gid;	/* user and process group id's */
178	uid_t user1_uid;
179
180	tst_sig(FORK, DEF_HANDLER, cleanup);
181
182	TEST_PAUSE;
183
184	/* Check that the test process id is super/root  */
185	if (geteuid() != 0) {
186		tst_brkm(TBROK, NULL, "Must be super/root for this test!");
187		tst_exit();
188	}
189
190	tst_tmpdir();
191
192	/* Get the uid of guest user - ltpuser1 */
193	if ((ltpuser = getpwnam(LTPUSER)) == NULL) {
194		tst_brkm(TBROK, cleanup, "%s not in /etc/passwd", LTPUSER);
195	}
196	user1_uid = ltpuser->pw_uid;
197
198	/* Get the group id of guest user - ltpuser1 */
199	if ((ltpgroup = getgrnam(LTPGRP)) == NULL) {
200		tst_brkm(TBROK, cleanup, "%s not in /etc/group", LTPGRP);
201	}
202	group1_gid = ltpgroup->gr_gid;
203
204	/*
205	 * Create a test file under temporary directory with specified
206	 * mode permissios and set the ownership of the test file to the
207	 * uid/gid of guest user user1.
208	 */
209	if ((fd = open(TESTFILE, O_RDWR | O_CREAT, FILE_MODE)) == -1) {
210		tst_brkm(TBROK, cleanup,
211			 "open(%s, O_RDWR|O_CREAT, %#o) Failed, errno=%d : %s",
212			 TESTFILE, FILE_MODE, errno, strerror(errno));
213	}
214
215	if (chown(TESTFILE, user1_uid, group1_gid) < 0) {
216		tst_brkm(TBROK, cleanup, "chown(2) of %s failed", TESTFILE);
217	}
218
219	/* Set the effective gid of the process to that of user */
220	if (setgid(group1_gid) < 0) {
221		tst_brkm(TBROK, cleanup, "setgid(2) to %d failed", group1_gid);
222	}
223}
224
225/*
226 * void
227 * cleanup() - performs all ONE TIME cleanup for this test at
228 *	       completion or premature exit.
229 *  Close the testfile created in the setup.
230 *  Remove the test directory and testfile created in the setup.
231 */
232void cleanup()
233{
234	/*
235	 * print timing stats if that option was specified.
236	 */
237	TEST_CLEANUP;
238
239	/* Close the testfile created in the setup() */
240	if (close(fd) == -1) {
241		tst_brkm(TBROK, NULL, "close(%s) Failed, errno=%d : %s",
242			 TESTFILE, errno, strerror(errno));
243	}
244
245	tst_rmdir();
246
247}
248