Laboratory 1 · Related unit: Unit 2
Linux Fundamentals
A complete tour of the Linux environment used throughout the course: file-system navigation, permissions, users and groups, shell scripting, and the compiler toolchain from source to executable. This laboratory assumes the course VM is set up and the toolchain from Lesson 2.5 is installed.
01 Objectives
By the end of this laboratory you will be able to
- Navigate the Linux file system and manage files and directories using the shell.
- Inspect and modify file permissions and ownership.
- Inspect users and groups on a running system.
- Write a small shell script that performs a real task and handles errors.
- Compile a multi-file C program using the GCC / Make / Git / GDB workflow.
- Produce a lab report documenting each step and the reasoning behind it.
02 Background
Every subsequent laboratory in this course assumes you can work fluently in the Linux shell. Lab 1 is where that fluency is built. The exercises here are deliberately small — the goal is not complexity, but familiarity with the tools you will rely on for the rest of the course.
The laboratory has four parts. Part A explores the file system and the shell. Part B examines permissions and users. Part C writes a small shell script and runs it. Part D builds a multi-file C program using the full toolchain and answers questions about the intermediate stages. Each part produces artifacts that go into the lab report.
Complete Lesson 1.0 and Lesson 2.5 first
This lab assumes you have completed the course VM setup (Lesson 1.0) and the development environment setup (Lesson 2.5). If gcc --version does not produce output, or ~/comp3150/ does not exist, return to those lessons before continuing.
03 Setup
Create your lab workspace and initialize a Git repository:
$ mkdir -p ~/comp3150/labs/lab-1
$ cd ~/comp3150/labs/lab-1
$ git init
Initialized empty Git repository in /home/alice/comp3150/labs/lab-1/.git/
$ cat > .gitignore << 'EOF'
*.o
lab1
build/
EOF
$ git add .gitignore
$ git commit -m "Initialise Lab 1 workspace"
Before you begin, take a snapshot of the VM named before-lab-1. If anything goes wrong during the lab, you can revert to this snapshot and start over.
04 Part A — File-system navigation
Complete the following steps in your lab directory. For each, record the command you used and the observed output in your lab report.
-
Establish your workspace. Print the current working directory, then create a nested directory structure:
Confirm the structure withmkdir -p src docs tests cd src touch main.c util.c cd ../docs touch notes.txt cd ../tests touch test_cases.txt cd ..find . -print. -
Practice navigation. From the lab directory:
- Change into
srcusing a relative path. - Change back to the lab directory using
... - Change into
srcusing an absolute path. - Change into
docs, thencd .., thencd -(this returns to the previous directory).
pwdoutputs. - Change into
-
Explore the system tree. Run the following and record one line of meaningful output from each:
For the last command, how many files are inls /etc | head -5 ls /usr/bin | wc -l ls -la ~ | head -10 ls -l /boot/boot? Which user owns them? -
Inspect a file's metadata. Write a short text file and inspect it with
stat:
Identify each of the following fields in the output and explain what it means:echo "Laboratory 1" > docs/notes.txt stat docs/notes.txtSize,Blocks,Access,Modify,Change. Add this analysis to your lab report. -
Use a pipeline. Count how many processes on the system are running as the root user:
Then count how many distinct users have at least one running process:ps -ef | awk '$1 == "root"' | wc -l
Record both numbers and explain what they tell you about the workload on your VM.ps -ef | awk 'NR>1 {print $1}' | sort -u | wc -l
05 Part B — Permissions, users, and groups
-
Inspect your own identity. Run each of the following and interpret the output:
What is your UID? What is your primary GID? How many supplementary groups do you belong to?id whoami groups echo $HOME echo $PATH -
Create and inspect permissions. Create a test file and observe its default mode:
The mode is determined by the umask. Runtouch perm-test.txt ls -l perm-test.txtumaskand record the value. Then verify the formula from Lesson 2.4:666 & ~umaskshould equal the observed mode. -
Modify permissions with symbolic notation. Apply each of the following and record the mode after each change:
chmod u+x perm-test.txt ls -l perm-test.txt chmod g-w perm-test.txt ls -l perm-test.txt chmod o-r perm-test.txt ls -l perm-test.txt chmod u=rw,go=r perm-test.txt ls -l perm-test.txt -
Modify permissions with octal notation. Apply each of the following and record the resulting mode:
Afterchmod 644 perm-test.txt chmod 600 perm-test.txt chmod 000 perm-test.txt chmod 755 perm-test.txtchmod 000, try to read the file withcat perm-test.txt. What happens? Can you restore access? -
Set the umask and observe its effect. Run:
For each new file and directory, verify that the mode matches the formula from Lesson 2.4. Record the observed modes and your calculations in your lab report.umask 077 touch private.txt mkdir private-dir ls -l private.txt ls -ld private-dir umask 022 touch public.txt mkdir public-dir ls -l public.txt ls -ld public-dir -
Inspect a system user account. Look up the entry for your own account in the account database:
Identify each field in thegrep "^$(whoami):" /etc/passwd grep "^$(whoami):" /etc/group/etc/passwdline. What is your login shell? What is your home directory? -
Examine setuid binaries. List every setuid binary on the system:
For three of them, look up what the program does and explain in one sentence why it needs elevated privileges. Add this analysis to your lab report.sudo find / -perm -4000 -type f 2>/dev/null
06 Part C — Shell scripting
Write a shell script called sysinfo.sh in your lab directory. The script reports a summary of the current system's state. It must satisfy the following requirements:
- Accept an optional command-line argument. If the argument is
--verbose, the script prints additional detail. If any other argument is supplied, the script prints an error and exits with a non-zero status. - Print a header line that includes the current date and the current user.
- Print the hostname, kernel version, and CPU architecture.
- Print the number of processes currently running (you may use
ps -e --no-headers | wc -l). - Print the total and available memory (you may use
free -h). - Print the total and free disk space on the root file system (you may use
df -h /). - If the
--verboseflag is supplied, also print the current umask and the contents of thePATHenvironment variable.
The script must handle the following errors gracefully:
- If a command it depends on is missing, it must print a clear error and exit with a non-zero status.
- Any temporary files it creates must be cleaned up on exit, including on error.
When you have written the script:
- Make it executable:
chmod +x sysinfo.sh. - Run it without arguments. Capture the output.
- Run it with
--verbose. Capture the output. - Run it with an invalid argument. Capture the output and the exit code (use
echo $?after the run). - Run
shellcheck sysinfo.shif available in your VM. Note any warnings. - Commit the script to your Git repository with a descriptive message.
07 Part D — Toolchain
Build a small multi-file C program using the full toolchain from Lesson 2.5. The program will compute factorials and print a table.
-
Write the source files. Create three files:
/* factorial.h */ #ifndef FACTORIAL_H #define FACTORIAL_H long factorial(int n); #endif/* factorial.c */ #include "factorial.h" long factorial(int n) { long result = 1; for (int i = 1; i <= n; i++) result *= i; return result; }/* main.c */ #include <stdio.h> #include <stdlib.h> #include "factorial.h" int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s N\n", argv[0]); return 1; } int n = atoi(argv[1]); for (int i = 1; i <= n; i++) printf("%2d! = %ld\n", i, factorial(i)); return 0; } -
Inspect the compilation pipeline. Run each of the following and record the size of the resulting intermediate file:
Describe in your lab report what each stage did.gcc -E main.c -o main.i gcc -S main.c -o main.s gcc -c main.c -o main.o wc -l main.i main.s ls -lh main.i main.s main.o -
Write a Makefile. Create a Makefile that builds the program from the two object files. The default target should link the executable; there should also be a
cleantarget. Use variables for the compiler and flags. -
Build and test. Run
maketo build the program. Run./lab1 10and confirm the output shows 1! through 10!. Then modify onlyfactorial.c(add a comment line) and runmakeagain. Did it recompilemain.c? Explain. -
Compile with warnings. Rebuild with
-Wall -Wextra -Werroradded to the flags. Does the build still succeed? If it fails, fix the warnings. -
Debug with GDB. Introduce a deliberate bug into
factorial.c— for example, change the loop condition toi < ninstead ofi <= n. Rebuild and run./lab1 5. The output will be wrong. Then use GDB to identify the bug:
Describe the exact point at which the divergence occurred. Fix the bug and verify the corrected output.gdb ./lab1 (gdb) break factorial (gdb) run 5 (gdb) print n (gdb) print i (gdb) continue (gdb) print result (gdb) quit - Commit your work. Add all source files and the Makefile to Git and commit with a descriptive message.
08 Deliverables
Submit the following as your Lab 1 deliverables. Include them in a single archive or in the Git repository as instructed.
-
Source files:
factorial.h,factorial.c,main.c,Makefile, andsysinfo.sh. -
Lab report (Markdown, PDF, or plain text) with the following sections:
- Setup. Describe the VM configuration and any issues encountered.
- Part A. The recorded outputs from every step. Include a short paragraph explaining the significance of one observation.
- Part B. The recorded permission changes with the octal modes after each. The setuid analysis for three binaries. A short discussion of why setuid is a security concern.
- Part C. The complete script, the captured output for all three invocations, and the exit code for the invalid invocation.
- Part D. The intermediate file sizes, the description of each compilation stage, the effect of the incremental rebuild, the GDB session with the fix, and an explanation of the bug's root cause.
- Reflection. A 200–300 word reflection on what you learned that you did not already know before the lab.
09 Grading rubric
| Criterion | Weight | What it evaluates |
|---|---|---|
| Part A — file-system navigation | 15% | Accurate completion of all steps, correct interpretation of stat fields, well-formed pipeline observation. |
| Part B — permissions and users | 20% | Correct permission changes with octal verification, umask calculation, setuid analysis, understanding of the security implications. |
| Part C — shell script | 25% | Script satisfies all requirements, handles argument parsing and errors correctly, cleans up temporary files, is readable and idiomatic. |
| Part D — toolchain | 25% | Program builds and runs, Makefile correct, incremental rebuild behaviour demonstrated, GDB session identifies the bug, correct explanation of root cause. |
| Report quality | 15% | Clear structure, correct use of technical terminology, evidence of reasoning rather than mechanical transcription, reflection demonstrates learning. |
| Total | 100% |
10 Summary
- Lab 1 established your Linux workspace and verified that the development toolchain is complete.
- You practised file-system navigation, permission modification, and umask behaviour — the skills assumed by every subsequent lab.
- You wrote a shell script that handles errors and cleans up after itself — the shape of the automation scripts you will write throughout the course.
- You built a multi-file C program with Make and debugged it with GDB — the workflow that every programming assignment in this course will follow.