blob: 46d3b6738c569e5d1b40d832a1ef58923cad81c9 [file] [log] [blame]
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Test directory operations
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <eprintf.h>
#include <mystdlib.h>
#include <puny.h>
#include "fcalls.h"
#include "util.h"
enum { T_NIL = 0,
T_FILE = 1,
T_DIR = 2 };
typedef struct file_s {
int type;
char *name;
long tell;
} file_s;
typedef struct dir_s {
char *name;
int max; /* Max number of slots for files */
int next; /* Next file slot */
file_s *file; /* Slots for files that are in directory */
} dir_s;
typedef bool (*dir_f)(struct dirent *entry, void *user);
static file_s NilFile = { 0, NULL, 0 };
static dir_s NilDir = { NULL, 0, 0, NULL };
static bool ignore (char *name)
{
return (strcmp(name, ".") == 0) ||
(strcmp(name, "..") == 0);
}
static inline bool CheckEmpty(dir_s *d) { return d->next == 0; }
static void AddFile (dir_s *d, int type, char *name)
{
if (d->max == d->next) {
int more = (d->max + 1) * 2;
file_s *f = erealloc(d->file, more * sizeof(file_s));
d->max = more;
d->file = f;
}
d->file[d->next].type = type;
d->file[d->next].name = name;
++d->next;
}
static file_s *FindFile (dir_s *d, char *name)
{
int i;
for (i = 0; i < d->next; i++) {
if (strcmp(d->file[i].name, name) == 0) {
return &d->file[i];
}
}
return NULL;
}
static void RemoveFile (dir_s *d, char *name)
{
int i;
for (i = 0; i < d->next; i++) {
if (strcmp(d->file[i].name, name) == 0) {
free(d->file[i].name);
--d->next;
d->file[i] = d->file[d->next];
d->file[d->next] = NilFile;
return;
}
}
}
static void AddChildren (dir_s *dir, int num_children)
{
char *name;
int i;
chdir(dir->name);
for (i = 0; i < num_children; i++) {
name = RndName();
mkdir(name, 0777);
AddFile(dir, T_DIR, name);
}
chdir("..");
}
static void DeleteChildren (dir_s *dir)
{
chdir(dir->name);
while (!CheckEmpty(dir)) {
rmdir(dir->file[0].name);
RemoveFile(dir, dir->file[0].name);
}
chdir("..");
}
void for_each_file (dir_s *dir, dir_f fn, void *user)
{
DIR *d;
struct dirent entry;
struct dirent *result;
d = opendir(dir->name);
for (;;) {
readdir_r(d, &entry, &result);
if (result == NULL) break;
if (!fn(&entry, user)) break;
}
closedir(d);
}
static bool AuditChild (struct dirent *entry, void *user)
{
dir_s *dir = user;
if (ignore(entry->d_name)) return TRUE;
if (!FindFile(dir, entry->d_name)) {
PrError("Didn't find %s in %s", entry->d_name, dir->name);
return FALSE;
}
return TRUE;
}
static void AuditDir (dir_s *dir)
{
for_each_file(dir, AuditChild, dir);
}
static void SaveTell (DIR *d, struct dirent *entry, dir_s *dir)
{
file_s *f;
if (ignore(entry->d_name)) return;
if ((f = FindFile(dir, entry->d_name)) == NULL) {
PrError("Didn't find %s in %s", entry->d_name, dir->name);
return;
}
f->tell = telldir(d);
}
void SeekTest (void)
{
int num_children = 10;
dir_s dir = NilDir;
DIR *d;
struct dirent *entry;
int i;
dir.name = RndName();
mkdir(dir.name, 0777);
AddChildren(&dir, num_children);
d = opendir(dir.name);
while ((entry = readdir(d)) != NULL) {
SaveTell(d, entry, &dir);
}
for (i = 0; i < 27; i++) {
int k = urand(num_children);
file_s *file = &dir.file[k];
seekdir(d, file->tell);
entry = readdir(d);
if (strcmp(entry->d_name, file->name) != 0) {
PrError("Seekdir failed. Looking for %s found %s",
entry->d_name, file->name);
}
}
closedir(d);
DeleteChildren(&dir);
rmdir(dir.name);
free(dir.name);
}
enum { NAME_SIZE = 17 };
typedef struct DirEntry_s DirEntry_s;
struct DirEntry_s {
int type;
char *name;
DirEntry_s *sibling;
DirEntry_s *child;
};
typedef bool (*walk_f)(DirEntry_s *dir);
static DirEntry_s *CreateDirEntry (int type)
{
int fd;
DirEntry_s *direntry;
direntry = ezalloc(sizeof(*direntry));
direntry->name = RndName();
direntry->type = type;
switch (type) {
case T_FILE:
fd = creat(direntry->name, 0700);
close(fd);
break;
case T_DIR:
mkdir(direntry->name, 0700);
break;
default:
PrError("bad type %d", type);
break;
}
return direntry;
}
static void Indent (int indent)
{
while (indent--) {
printf(" ");
}
}
static void PrintTree (DirEntry_s *direntry, int indent)
{
if (!direntry) return;
if (Option.print) {
Indent(indent);
printf("%s %s\n", direntry->type == T_DIR ? "D" : "F",
direntry->name);
}
PrintTree(direntry->child, indent + 1);
PrintTree(direntry->sibling, indent);
}
static bool WalkTree (DirEntry_s *direntry, walk_f f)
{
bool continue_walking;
if (!direntry) return TRUE;
if (!WalkTree(direntry->sibling, f)) return FALSE;
if (direntry->type == T_DIR) {
chdir(direntry->name);
continue_walking = WalkTree(direntry->child, f);
chdir("..");
if (!continue_walking) return FALSE;
}
return f(direntry);
}
static bool DeleteDirEntry (DirEntry_s *direntry)
{
switch (direntry->type) {
case T_FILE:
unlink(direntry->name);
break;
case T_DIR:
rmdir(direntry->name);
break;
default:
PrError("bad type %d", direntry->type);
break;
}
return TRUE;
}
static void DeleteTree (DirEntry_s *direntry)
{
WalkTree(direntry, DeleteDirEntry);
}
static DirEntry_s *CreateTree (int width, int depth, int level)
{
int i;
DirEntry_s *direntry;
DirEntry_s *child;
int type = random_percent(level * 15) ? T_FILE : T_DIR;
direntry = CreateDirEntry(type);
if ((depth == 0) || (type != T_DIR)) {
return direntry;
}
chdir(direntry->name);
for (i = 0; i < width; i++) {
child = CreateTree(width, depth - 1, level + 1);
child->sibling = direntry->child;
direntry->child = child;
}
chdir("..");
return direntry;
}
static void TreeTest (void)
{
DirEntry_s *root;
root = CreateTree(4, 9, 0);
PrintTree(root, 0);
DeleteTree(root);
}
static void Simple (void)
{
int num_children = 20;
dir_s dir = NilDir;
dir.name = RndName();
mkdir(dir.name, 0777);
AddChildren(&dir, num_children);
AuditDir(&dir);
DeleteChildren(&dir);
rmdir(dir.name);
free(dir.name);
}
void DirTest (void)
{
Simple();
TreeTest();
// SeekTest();
}