Add 5th lab

master
Yury Kurlykov 2020-05-29 02:43:15 +10:00
parent fca8d3a057
commit ea3de372a4
Signed by: t1meshift
GPG Key ID: B133F3167ABF94D8
10 changed files with 692 additions and 0 deletions

View File

@ -16,4 +16,5 @@ endfunction()
define_lab(lab2) define_lab(lab2)
define_lab(lab3) define_lab(lab3)
define_lab(lab4) define_lab(lab4)
define_lab(lab5)
define_lab(lab7) define_lab(lab7)

7
lab5/.execme Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
echo "TODO!"
exit 1

39
lab5/CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.16)
set(CMAKE_C_STANDARD 11)
# Lab name
set(LAB_NAME "lab5")
# Lab tasks
list(APPEND SOURCE_FILES
task1.c
task2.c
mytail.c
mystat.c
myls.c
task6.c
)
list(APPEND NON_COMPILABLE_SRC
.execme
)
### Here goes the template
project("${LAB_NAME}" C)
add_custom_target("${LAB_NAME}")
foreach (file IN LISTS SOURCE_FILES)
add_executable("${LAB_NAME}_${file}_run" "${file}")
add_dependencies("${LAB_NAME}" "${LAB_NAME}_${file}_run")
endforeach ()
foreach (file IN LISTS NON_COMPILABLE_SRC)
add_custom_command(
TARGET "${LAB_NAME}" POST_BUILD
DEPENDS "${file}"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/${file}"
"${CMAKE_CURRENT_BINARY_DIR}/${file}"
)
endforeach ()

3
lab5/README.md Normal file
View File

@ -0,0 +1,3 @@
# Лабораторная работа №5
## Задание 1

190
lab5/myls.c Normal file
View File

@ -0,0 +1,190 @@
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
/*
5. Разработать собственную версию (myls) команды ls, которая выводит список файлов в заданной директории.
С ключом -l она выводит информацию о каждом файле, включая собственника, группу, разрешения и т.д., получаемые из системного
вызова stat().
Формат: myls -l directory (или текущую директорию, если параметр не задан)
Использовать: stat(), opendir(), readdir(), getcwd(), ...
*/
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
char *path = NULL;
bool long_listing = false;
bool flag_end = false;
void cleanup_on_exit() {
free(path);
}
void print_entry(char *entry) {
if (long_listing) {
struct stat fstat;
if (lstat(entry, &fstat) == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", entry);
}
// Print permissions
char entry_type;
// setuid, setgid, sticky are not in count
const char* perm_sym[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
int perm = fstat.st_mode & 0777;
struct passwd *pw_data;
struct group *gp_data;
char user_name[33] = "";
char group_name[33] = "";
char last_mtime[256] = "";
switch (fstat.st_mode & S_IFMT) {
case S_IFREG:
entry_type = '-';
break;
case S_IFDIR:
entry_type = 'd';
break;
case S_IFLNK:
entry_type = 'l';
break;
case S_IFBLK:
entry_type = 'b';
break;
case S_IFSOCK:
entry_type = 's';
break;
case S_IFCHR:
entry_type = 'c';
break;
#ifdef S_IFIFO
case S_IFIFO:
entry_type = 'p';
break;
#endif /* S_IFIFO */
default:
entry_type = '?';
break;
}
if ((pw_data = getpwuid(fstat.st_uid)) == NULL) {
perror("Could not get owner username");
errno = 0;
snprintf(user_name, 33, "%d", fstat.st_uid);
} else {
snprintf(user_name, 33, "%s", pw_data->pw_name);
}
if ((gp_data = getgrgid(fstat.st_gid)) == NULL) {
perror("Could not get owner group name");
errno = 0;
snprintf(group_name, 33, "%d", fstat.st_gid);
} else {
snprintf(group_name, 33, "%s", gp_data->gr_name);
}
time_t mtime = fstat.st_mtime;
struct tm* tm_info = localtime(&mtime);
strftime(last_mtime, 256, "%Y-%b-%d %T %Z", tm_info);
printf("%c%s%s%s\t%4lu\t%s %s\t%6ld\t%s\t",
entry_type,
perm_sym[(perm & S_IRWXU) >> 6],
perm_sym[(perm & S_IRWXG) >> 3],
perm_sym[perm & S_IRWXO],
fstat.st_nlink,
user_name,
group_name,
fstat.st_size,
last_mtime);
}
printf("%s\n", entry);
}
int main(int argc, char *argv[]) {
atexit(cleanup_on_exit);
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-' && !flag_end) {
char *flag = argv[i] + 1;
if (STR_EQ(flag, "l")) {
long_listing = true;
}
else if (STR_EQ(flag, "-")) {
flag_end = true;
}
else {
FATAL("Usage: %s [-l] [--] [path]\n", argv[0]);
}
} else {
path = strdup(argv[i]);
break;
}
}
if (!path) {
/*
* As an extension to the POSIX.1-2001 standard, glibc's getcwd()
* allocates the buffer dynamically using malloc(3) if buf is NULL. In
* this case, the allocated buffer has the length size unless size is
* zero, when buf is allocated as big as necessary. The caller should
* free(3) the returned buffer.
*/
if ((path = getcwd(NULL, 0)) == NULL) {
perror("getcwd()");
FATAL("Couldn't get current working directory\n");
}
}
struct stat path_stat;
DIR *dir = NULL;
if (stat(path, &path_stat) == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", path);
}
bool is_dir = S_ISDIR(path_stat.st_mode);
if (is_dir) {
if ((dir = opendir(path)) == NULL) {
perror("opendir()");
FATAL("Couldn't open directory %s\n", path);
}
if (chdir(path) == -1) {
perror("chdir()");
FATAL("Could not chdir to directory %s\n", path);
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
print_entry(ent->d_name);
}
closedir(dir);
} else {
print_entry(path);
}
return 0;
}

79
lab5/mystat.c Normal file
View File

@ -0,0 +1,79 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
/*
4. Разработать собственную версию (mystat) команды stat, которая просто осуществляет системный вызов stat(), выводит
размер, число задействованных блоков, число ссылок и т.д. Отследить, как меняется число ссылок, когда изменяется
содержимое директории.
*/
#define MAX(a,b) ((a)>(b)?(a):(b))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
int main(int argc, char *argv[]) {
if (argc != 2) {
FATAL("Usage: %s <path>\n", argv[0]);
}
char *path = argv[1];
struct stat sb;
if (lstat(path, &sb) == -1) {
perror("lstat()");
FATAL("Couldn't stat %s\n", path);
}
printf("File: %s\n", path);
printf("Size: %ld\tBlocks: %ld\tIO Block: %ld\t", sb.st_size, sb.st_blocks, sb.st_blksize);
switch(sb.st_mode & S_IFMT) {
case S_IFBLK:
puts("block device");
break;
case S_IFCHR:
puts("character device");
break;
case S_IFDIR:
puts("directory");
break;
case S_IFIFO:
puts("FIFO/pipe");
break;
case S_IFLNK:
puts("symlink");
break;
case S_IFREG:
puts("regular file");
break;
case S_IFSOCK:
puts("socket");
break;
default:
puts("unknown?");
break;
}
printf("Device: [%04x:%04x]\t", major(sb.st_dev), minor(sb.st_dev));
printf("Inode: %lu\t", sb.st_ino);
printf("Links: %lu\n", sb.st_nlink);
printf("Mode: 0%o\tUid: %d\tGid: %d\n", sb.st_mode, sb.st_uid, sb.st_gid);
printf("Access (atime): %s", ctime(&sb.st_atime));
printf("Modify (mtime): %s", ctime(&sb.st_mtime));
printf("Change (ctime): %s", ctime(&sb.st_ctime));
return 0;
}

92
lab5/mytail.c Normal file
View File

@ -0,0 +1,92 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdbool.h>
/*
3. Разработать собственную версию (mytail) команды tail.
Формат: mytail -n file
Она читает блок из конца файла, просматривает его с конца до заданного количества строк n и печатает эти строки
в соответствующем порядке.
Использовать: stat(), lseek(), open(), read(), close(), ...
*/
#define MAX(a,b) ((a)>(b)?(a):(b))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
int main(int argc, char *argv[]) {
const int BUFF_SIZE = 4096;
long n = 10;
char *filename = NULL;
// Parse arguments (skip 0th arg)
for (int i = 1; i < argc; ++i) {
if ((argv[i][0] == '-') && (i < argc - 1)) {
// -n flag (e.g. -20)
n = strtol(argv[i], NULL, 10) * -1;
if (n < 1 || errno == ERANGE) {
FATAL("Usage: %s [-<number_of_lines>] <file>\n", argv[0]);
}
} else {
filename = argv[i];
break;
}
}
if (filename == NULL) FATAL("No file name specified.\n");
struct stat sb;
int fd;
if (stat(filename, &sb) == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", filename);
}
if ((fd = open(filename, O_RDONLY)) == -1) {
perror("open()");
FATAL("Couldn't open %s\n", filename);
}
char *buf = calloc(BUFF_SIZE, 1);
int lf_cnt = 0;
size_t read_sz = 0;
off_t file_offset = MAX(sb.st_size - BUFF_SIZE, 0);
bool seek_stop = false;
lseek(fd, -BUFF_SIZE, SEEK_END);
do {
int data_sz = read(fd, buf, BUFF_SIZE);
for (int i = data_sz - 1; i >= 0; --i) {
if (buf[i] == '\n') {
if (++lf_cnt >= n) {
file_offset += i + 1;
goto print_lines;
}
}
}
file_offset -= BUFF_SIZE;
lseek(fd, -BUFF_SIZE, SEEK_CUR);
} while ((lf_cnt <= n) && (file_offset > 0));
print_lines:
lseek(fd, file_offset, SEEK_SET);
do {
read_sz = read(fd, buf, BUFF_SIZE);
write(STDOUT_FILENO, buf, read_sz);
} while (read_sz == BUFF_SIZE);
free(buf);
close(fd);
return 0;
}

66
lab5/task1.c Normal file
View File

@ -0,0 +1,66 @@
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#define BENCHMARK(name, payload) \
do {\
struct timeval t0, t1;\
gettimeofday(&t0, NULL);\
payload \
gettimeofday(&t1, NULL);\
struct timeval result;\
timersub(&t1, &t0, &result);\
printf(name ": %ld.%06lds\n", result.tv_sec, result.tv_usec);\
} while (0)
/*
1. Используя creat(), write(), fflush(), close(), gettimeofday(), разработать программу, которая
открывает файл,
записывает туда 300 KB,
очищает все записи,
закрывает и
удаляет файл,
а также измеряет и выводит время, затраченное на каждое действие.
*/
int main() {
int f = -1;
BENCHMARK("creat() call",{
f = creat("task1.txt", S_IWUSR);
});
if (f == -1) {
perror("creat()");
return 1;
}
int bytes_written = -1;
const size_t data_size = 307200; // bytes, 300KiB
char *random_data = malloc(data_size);
BENCHMARK("write 300KiB", {
bytes_written = write(f, random_data, data_size);
});
free(random_data);
if (bytes_written == -1) {
perror("write()");
return 1;
}
BENCHMARK("fsync() call", {
fsync(f);
});
BENCHMARK("close() call", {
close(f);
});
return 0;
}

87
lab5/task2.c Normal file
View File

@ -0,0 +1,87 @@
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#define BENCHMARK(name, payload) \
do {\
struct timeval __t0, __t1;\
gettimeofday(&__t0, NULL);\
payload \
gettimeofday(&__t1, NULL);\
struct timeval __result;\
timersub(&__t1, &__t0, &__result);\
printf(name ": %ld.%06lds\n", __result.tv_sec, __result.tv_usec);\
} while (0)
/*
2. Разработать программу, которая замеряет время для 300,000 однобайтовых записей с использованием
a) напрямую POSIX: creat(), write(), close().
b) с использованием библиотеки stdio (напр., fopen(), fwrite(), and fclose()).
Сравнить и объяснить результаты.
*/
int main() {
BENCHMARK("POSIX API", {
int f = -1;
BENCHMARK("creat() call",{
f = creat("task2_posix.txt", 0644);
});
if (f == -1) {
perror("creat()");
return 1;
}
int bytes_written = -1;
const size_t data_size = 300000;
char *random_data = malloc(data_size);
BENCHMARK("write() call", {
bytes_written = write(f, random_data, data_size);
});
free(random_data);
if (bytes_written == -1) {
perror("write()");
return 1;
}
BENCHMARK("close() call", {
close(f);
});
});
BENCHMARK("stdio.h API",{
FILE *f = NULL;
BENCHMARK("fopen() call", {
f = fopen("task2_stdio.txt", "w");
});
if (f == NULL) {
perror("fopen()");
return 1;
}
int bytes_written = -1;
const size_t data_size = 300000;
char *random_data = malloc(data_size);
BENCHMARK("fwrite() call", {
bytes_written = fwrite(random_data, 1, 300000, f);
});
free(random_data);
if (bytes_written != data_size) {
perror("fwrite()");
return 1;
}
BENCHMARK("fclose() call",{
fclose(f);
});
});
return 0;
}

128
lab5/task6.c Normal file
View File

@ -0,0 +1,128 @@
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/*
6. Разработать программу, которая выводит имена каждого файла и директории, начиная с заданной точки в дереве каталогов.
a) Без аргументов: сначала текущая директория и ее содержимое, затем поддиректории и т.д. (пока не закончится дерево,
root в качестве CWD).
b) С одним аргументом (который есть имя директории): все поддерево, начиная с заданной директории.
c) А также еще один какой-либо интересный вариант (см. опции для find).
*/
#define USAGE_STRING "Usage: %s [-d <max_depth>] [-L] [--] [path]\n"
#define MAX(a,b) ((a)>(b)?(a):(b))
#define STR_EQ(a, b) (!strcmp((a),(b)))
#define FATAL(fmt, ...) do {\
fprintf(stderr,fmt,##__VA_ARGS__);\
exit(1);\
} while (0)
#define WARNING(fmt, ...) fprintf(stderr,fmt,##__VA_ARGS__)
bool flag_end = false;
bool follow_symlinks = false;
long max_depth = LONG_MAX;
void traverse_path(char *path, long depth) {
struct stat pstat;
DIR *dir = NULL;
struct dirent *ent;
int stat_status;
printf("%s\n", path);
if (depth > max_depth)
return;
if (follow_symlinks) {
stat_status = stat(path, &pstat);
} else {
stat_status = lstat(path, &pstat);
}
if (stat_status == -1) {
perror("stat()");
FATAL("Couldn't stat %s\n", path);
}
if (!S_ISDIR(pstat.st_mode))
return;
if ((dir = opendir(path)) == NULL) {
perror("opendir()");
FATAL("Couldn't open directory %s\n", path);
}
while ((ent = readdir(dir)) != NULL) {
if (STR_EQ(ent->d_name, ".") || STR_EQ(ent->d_name, "..")) {
continue;
}
char path_next[PATH_MAX];
strcpy(path_next, path);
if (path_next[strlen(path_next) - 1] != '/') {
strcat(path_next, "/");
}
strcat(path_next, ent->d_name);
traverse_path(path_next, depth + 1);
}
closedir(dir);
}
int main(int argc, char *argv[]) {
char *path = ".";
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-' && !flag_end) {
char *flag = argv[i] + 1;
if (STR_EQ(flag, "d")) {
if (i + 1 < argc) {
max_depth = strtol(argv[++i], NULL, 10);
if (errno == ERANGE) {
WARNING("Max depth level is out of range, %ld is set\n", max_depth);
errno = 0;
}
if (max_depth < 0) {
FATAL("Max depth must be non-negative\n");
}
} else {
FATAL(USAGE_STRING, argv[0]);
}
}
else if (STR_EQ(flag, "L")) {
follow_symlinks = true;
}
else if (STR_EQ(flag, "-")) {
flag_end = true;
}
else {
FATAL(USAGE_STRING, argv[0]);
}
} else {
path = argv[i];
break;
}
}
traverse_path(path, 1);
}