/******************************************************************************* * Copyright (c) 2011 Arapiki Solutions Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * "Peter Smith <psmith@arapiki.com>" - initial API and * implementation and/or initial documentation *******************************************************************************/ package com.buildml.scanner.legacy; import static org.junit.Assert.*; import java.io.File; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.buildml.model.IActionMgr; import com.buildml.model.IActionMgr.OperationType; import com.buildml.model.IBuildStore; import com.buildml.model.IFileMgr; import com.buildml.utils.os.SystemUtils; /** * Basic testing that the LegacyBuildScanner can produce a valid * BuildStore. There are many test cases, split over multiple * test case files, with this file testing C Functions that * manipulate or access file permissions. * * @author "Peter Smith <psmith@arapiki.com>" */ public class TestCFuncPerms { /* variables used in many test cases */ private IBuildStore bs = null; private IActionMgr actionMgr = null; private IFileMgr fileMgr = null; private int rootAction; private int action; private Integer fileModifies[]; /** temporary directory into which test cases can store files */ private File tmpDir; /*-------------------------------------------------------------------------------------*/ /** * Called before each test case starts. Creates a temporary directory in which the * test case can store temporary files. * @throws Exception */ @Before public void setUp() throws Exception { tmpDir = SystemUtils.createTempDir(); } /*-------------------------------------------------------------------------------------*/ /** * Called after each test case ends. Removes the temporary directory and its content. * @throws Exception */ @After public void tearDown() throws Exception { SystemUtils.deleteDirectory(tmpDir); } /*-------------------------------------------------------------------------------------*/ /** * Given the source code of a small C program, compile the program and scan it into a * BuildStore. We then retrieve the one (and only) action that was registered in the * BuildStore, along with the lists of files that were accessed (accessed, read, written, * and deleted). * @param programCode The body of the small C program to be compiled. * @param args The command line arguments to pass to the small C program. * @throws Exception Something went wrong when compiling/running the program. */ private void traceOneProgram(String programCode, String args[]) throws Exception { /* compile, run, and trace the program */ bs = BuildScannersCommonTestUtils.parseLegacyProgram(tmpDir, programCode, args); /* fetch references to sub objects */ actionMgr = bs.getActionMgr(); fileMgr = bs.getFileMgr(); /* find the root action */ rootAction = actionMgr.getRootAction("root"); /* there should only be one child action */ Integer childActions[] = actionMgr.getChildren(rootAction); assertEquals(1, childActions.length); /* this is the action ID of the one action */ action = childActions[0]; /* fetch the file access arrays */ fileModifies = actionMgr.getFilesAccessed(action, OperationType.OP_MODIFIED); } /*-------------------------------------------------------------------------------------*/ /** * Test the access() C function. * @throws Exception */ @Test public void testAccess() throws Exception { /* * Nothing to test, for now. This can be implemented when the access() function * is modified to support package-boundary checking. */ } /*-------------------------------------------------------------------------------------*/ /** * Test the chmod() C function. * @throws Exception */ @Test public void testChmod() throws Exception { /* * chmod() a file that exists. */ assertTrue(new File(tmpDir, "chmod-file").createNewFile()); traceOneProgram( "#include <sys/stat.h>\n" + "int main() {" + " chmod(\"" + tmpDir + "/chmod-file\", 0644);" + " return 0;" + "}", null); assertEquals(1, fileModifies.length); assertEquals(fileMgr.getPath(tmpDir + "/chmod-file"), fileModifies[0].intValue()); /* * chmod() a file that doesn't exist - nothing should be logged. */ traceOneProgram( "#include <sys/stat.h>\n" + "int main() {" + " chmod(\"" + tmpDir + "/no-file\", 0644);" + " return 0;" + "}", null); assertEquals(0, fileModifies.length); } /*-------------------------------------------------------------------------------------*/ /** * Test the chown() C function. * @throws Exception */ @Test public void testChown() throws Exception { /* * chmod() a file that exists (note that it's legal to change a file's * uid and gid to getuid() and getgid() respectively. This doesn't * require root elevation, so we can do it in a unit test. */ assertTrue(new File(tmpDir, "chown-file").createNewFile()); traceOneProgram( "#include <sys/stat.h>\n" + "int main() {" + " chown(\"" + tmpDir + "/chown-file\", getuid(), getgid());" + " return 0;" + "}", null); assertEquals(1, fileModifies.length); assertEquals(fileMgr.getPath(tmpDir + "/chown-file"), fileModifies[0].intValue()); /* * chmod() a file that doesn't exist - nothing should be logged. */ traceOneProgram( "#include <sys/stat.h>\n" + "int main() {" + " chown(\"" + tmpDir + "/no-file\", getuid(), getgid());" + " return 0;" + "}", null); assertEquals(0, fileModifies.length); } /*-------------------------------------------------------------------------------------*/ /** * Test the eaccess() C function. * @throws Exception */ @Test public void testEaccess() throws Exception { /* * Nothing to test, for now. This can be implemented when the eaccess() function * is modified to support package-boundary checking. */ } /*-------------------------------------------------------------------------------------*/ /** * Test the euidaccess() C function. * @throws Exception */ @Test public void testEuidaccess() throws Exception { /* * Nothing to test, for now. This can be implemented when the euidaccess() function * is modified to support package-boundary checking. */ } /*-------------------------------------------------------------------------------------*/ /** * Test the faccessat() C function. * @throws Exception */ @Test public void testFaccessat() throws Exception { /* * Nothing to test, for now. This can be implemented when the faccessat() function * is modified to support package-boundary checking. */ } /*-------------------------------------------------------------------------------------*/ /** * Test the fchmod() C function. * @throws Exception */ @Test public void testFchmod() throws Exception { /* * fchmod() a file that exists. */ assertTrue(new File(tmpDir, "fchmod-file").createNewFile()); traceOneProgram( "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main() {" + " chdir(\"" + tmpDir + "\");" + " int fd = open(\"fchmod-file\", O_RDONLY);" + " fchmod(fd, 0644);" + " return 0;" + "}", null); assertEquals(1, fileModifies.length); assertEquals(fileMgr.getPath(tmpDir + "/fchmod-file"), fileModifies[0].intValue()); /* * fchmod() with an invalid file descriptor. */ traceOneProgram( "#include <sys/stat.h>\n" + "int main() {" + " fchmod(-1, 0644);" + " return 0;" + "}", null); assertEquals(0, fileModifies.length); } /*-------------------------------------------------------------------------------------*/ /** * Test the fchmodat() C function. * @throws Exception */ @Test public void testFchmodat() throws Exception { /* * fchmodat() a file that exists. */ assertTrue(new File(tmpDir, "fchmodat-file").createNewFile()); traceOneProgram( "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main() {" + " int fd = open(\"" + tmpDir + "\", O_RDONLY);" + " fchmodat(fd, \"fchmodat-file\", 0644, 0);" + " return 0;" + "}", null); assertEquals(1, fileModifies.length); assertEquals(fileMgr.getPath(tmpDir + "/fchmodat-file"), fileModifies[0].intValue()); /* * fchmodat() with an invalid file. */ traceOneProgram( "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main() {" + " int fd = open(\"" + tmpDir + "\", O_RDONLY);" + " fchmodat(fd, \"bad-file\", 0644, 0);" + " return 0;" + "}", null); assertEquals(0, fileModifies.length); } /*-------------------------------------------------------------------------------------*/ /** * Test the fchown() C function. * @throws Exception */ @Test public void testFchown() throws Exception { /* * fchown() a file that exists (note that it's legal to change a file's * uid and gid to getuid() and getgid() respectively. This doesn't * require root elevation, so we can do it in a unit test. */ assertTrue(new File(tmpDir, "fchown-file").createNewFile()); traceOneProgram( "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main() {" + " int fd = open(\"" + tmpDir + "/fchown-file\", O_RDONLY);" + " fchown(fd, getuid(), getgid());" + " return 0;" + "}", null); assertEquals(1, fileModifies.length); assertEquals(fileMgr.getPath(tmpDir + "/fchown-file"), fileModifies[0].intValue()); /* * chmod() a file that doesn't exist - nothing should be logged. */ traceOneProgram( "#include <sys/stat.h>\n" + "int main() {" + " fchown(-1, getuid(), getgid());" + " return 0;" + "}", null); assertEquals(0, fileModifies.length); } /*-------------------------------------------------------------------------------------*/ /** * Test the fchownat() C function. * @throws Exception */ @Test public void testFchownat() throws Exception { /* * fchownat() a file that exists. */ assertTrue(new File(tmpDir, "fchownat-file").createNewFile()); traceOneProgram( "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main() {" + " int dirfd = open(\"" + tmpDir + "\", O_RDONLY);" + " fchownat(dirfd, \"fchownat-file\", getuid(), getgid(), 0);" + " return 0;" + "}", null); assertEquals(1, fileModifies.length); assertEquals(fileMgr.getPath(tmpDir + "/fchownat-file"), fileModifies[0].intValue()); /* * fchownat() with an invalid file. */ traceOneProgram( "#include <sys/stat.h>\n" + "#include <fcntl.h>\n" + "int main() {" + " int dirfd = open(\"" + tmpDir + "\", O_RDONLY);" + " fchownat(dirfd, \"bad-file\", getuid(), getgid(), 0);" + " return 0;" + "}", null); assertEquals(0, fileModifies.length); } /*-------------------------------------------------------------------------------------*/ /** * Test the lchown() C function. * @throws Exception */ @Test public void testLchown() throws Exception { /* * Not implemented for now, since symlinks aren't handled very well in BuildML. */ } /*-------------------------------------------------------------------------------------*/ }