baseline

yet another open-source distributed versioning control system
Log | Files | Refs

dircache-simple.c (16328B)


      1 /*
      2  * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/stat.h>   /* stat(3) */
     18 
     19 #include <stdio.h>
     20 #include <stdlib.h>	/* malloc(2) */
     21 #include <string.h>	/* str*(2), mem*(2) */
     22 #include <unistd.h>	/* close(2), rmdir(2), unlink(2) */
     23 
     24 #include <fcntl.h>	/* O_* macros */
     25 #include <fts.h>	/* fts_*(3) */
     26 
     27 #include "defaults.h"
     28 #include "config.h"
     29 #include "objdb.h"
     30 #include "dircache.h"
     31 #include "objects.h"
     32 #include "helper.h"
     33 
     34 int dircache_simple_get_ops(struct dircache_ops **);
     35 static int simple_open(struct dircache_ctx **, struct objdb_ctx *, struct objdb_ops *, const char *, const char *);
     36 static int simple_close(struct dircache_ctx *);
     37 static int simple_init(struct dircache_ctx *);
     38 static int simple_insert(struct dircache_ctx *, const char *);
     39 static int simple_commit(struct dircache_ctx *, const char *);
     40 static int simple_branch_get(struct dircache_ctx *, char **);
     41 static int simple_branch_set(struct dircache_ctx *, const char *);
     42 static int simple_workdir_get(struct dircache_ctx *, char **);
     43 static int simple_workdir_set(struct dircache_ctx *, const char *);
     44 
     45 static struct dircache_ops simple_ops = {
     46 	.name = "simple",
     47 	.version = "1.0",
     48 	.open = simple_open,
     49 	.close = simple_close,
     50 	.init = simple_init,
     51 	.insert = simple_insert,
     52 	.remove = NULL,
     53 	.commit = simple_commit,
     54 	.branch_get = simple_branch_get,
     55 	.branch_set = simple_branch_set,
     56 	.workdir_get = simple_workdir_get,
     57 	.workdir_set = simple_workdir_set,
     58 	.fsck = NULL,
     59 	.compress = NULL,
     60 	.dedup = NULL
     61 };
     62 
     63 int
     64 dircache_simple_get_ops(struct dircache_ops **ops)
     65 {
     66 	if (ops == NULL)
     67 		return EXIT_FAILURE;
     68 	*ops = (struct dircache_ops *)malloc(sizeof(struct dircache_ops));
     69 	memcpy(*ops, &simple_ops, sizeof(struct dircache_ops));
     70 	return EXIT_SUCCESS;
     71 }
     72 
     73 /* FIXME */
     74 static char *
     75 get_dircache_path(struct dircache_ctx *ctx)
     76 {
     77         char *path = NULL;
     78 
     79         if (ctx == NULL)
     80                 return NULL;
     81         asprintf(&path, "%s/%s", ctx->repo_baselinepath, BASELINE_DIRCACHE);
     82         return path;
     83 }
     84 
     85 static const char *
     86 strmismatch(const char *first, const char *second)
     87 {
     88 	int i;
     89 	size_t minlen;
     90 
     91 	minlen = strlen(first) < strlen(second) ? strlen(first) : strlen(second);
     92 	for (i=0 ; i<minlen ; i++) {
     93 		if (first[i] == second[i])
     94 			continue;
     95 		break;
     96 	}
     97 #ifdef DEUG
     98 	printf("[DEBUG] mismatch at i = %d\n", i);
     99 #endif
    100 	return &first[i];
    101 }
    102 
    103 static const char*
    104 dir_diff(const char *first, const char *second)
    105 {
    106 	const char *mis = strmismatch(first, second);
    107 	if (*mis == '/')
    108 		mis++;
    109 	return mis;
    110 }
    111 
    112 static int
    113 mkdirp(const char *parent, const char *dir)
    114 {
    115 	char *o, *start, *end;
    116 	char *path = NULL;
    117 	int retval;
    118 	struct stat s;
    119 
    120 	o = start = end = strdup(dir);
    121 	if (*o == '/') {
    122 		start++;
    123 		end++;
    124 	}
    125 	while (1) {
    126 		if (*end == '\0')
    127 			break;
    128 		if (*end == '/') {
    129 			*end = '\0';
    130 			/* OpenBSD's asprintf(3) uses realloc(), hence no need to free */
    131 			asprintf(&path, "%s/%s", parent, start);
    132 #ifdef DEBUG
    133 			printf("[DEBUG] create %s\n", path);
    134 #endif
    135 			if (stat(path, &s) == 0) {
    136 				if (!S_ISDIR(s.st_mode)) {
    137 					retval = EXIT_FAILURE;
    138 					goto ret;
    139 				}
    140 			}
    141 			else {
    142 				if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
    143 					return EXIT_FAILURE;
    144 			}
    145 			*end = '/';
    146 		}
    147 		end++;
    148 	}
    149 ret:
    150 	free(path);
    151 	free(o);
    152 	return EXIT_SUCCESS;
    153 }
    154 
    155 static int
    156 simple_open(struct dircache_ctx **dc_ctx, struct objdb_ctx *db_ctx, struct objdb_ops *db_ops, const char *rootpath, const char *baselinepath)
    157 {
    158 	if (dc_ctx == NULL || db_ctx == NULL)
    159 		return EXIT_FAILURE;
    160 	*dc_ctx = (struct dircache_ctx *)malloc(sizeof(struct dircache_ctx));
    161 	(*dc_ctx)->db_ctx = db_ctx;
    162 	(*dc_ctx)->db_ops = db_ops;
    163 	(*dc_ctx)->repo_rootpath = strdup(rootpath);
    164 	(*dc_ctx)->repo_baselinepath = strdup(baselinepath);
    165 	return EXIT_SUCCESS;
    166 }
    167 
    168 static int
    169 simple_close(struct dircache_ctx *dc_ctx)
    170 {
    171 	/* TODO */
    172 	return EXIT_SUCCESS;
    173 }
    174 
    175 static int
    176 simple_init(struct dircache_ctx *dc_ctx)
    177 {
    178 	char *branch_name = NULL, *branch_path;
    179 	char *dc_path;
    180 	char *workdir_fname;
    181 	int exist, fd, retval;
    182 	struct stat s;
    183 	FILE *branch_fp;
    184 
    185 	if (dc_ctx == NULL)
    186 		return EXIT_FAILURE;
    187 	dc_path = get_dircache_path(dc_ctx);
    188 	asprintf(&branch_path, "%s/branch", dc_ctx->repo_baselinepath);
    189 	/* check if '.baseline/dircache' dir exists */
    190 	if (stat(dc_path, &s) == -1) {
    191 		/* create '.baseline/dircache' dir */
    192 		if (mkdir(dc_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
    193 			return EXIT_FAILURE;
    194 		}
    195 	}
    196 	/* check if '.baseline/branch' file exists */
    197 	/* TODO: a lot of that code should be moved to is_init() func or cmd-init file */
    198 	/* may be already initialized ... should just return failure? */
    199 	if(stat(branch_path, &s) == 0) {
    200 		if (!S_ISREG(s.st_mode)) {
    201 			retval = EXIT_FAILURE;
    202 			goto ret;
    203 		}
    204 		if (!(s.st_mode & S_IRUSR) || !(s.st_mode & S_IWUSR)) {
    205 			retval = EXIT_FAILURE;
    206 			goto ret;
    207 		}
    208 	}
    209 	else {
    210 		asprintf(&branch_name, "%s", DEFAULT_BRANCH);
    211 		/* this piece of code does not belong here */
    212 		if (dc_ctx->db_ops->branch_if_exists(dc_ctx->db_ctx, branch_name, &exist) == EXIT_FAILURE) {
    213 			retval = EXIT_FAILURE;
    214 			free(branch_name);
    215 			goto ret;
    216 		}
    217 		if (!exist)
    218 			dc_ctx->db_ops->branch_create(dc_ctx->db_ctx, branch_name);
    219 		/* create '.baseline/branch' file */
    220 		if ((branch_fp = fopen(branch_path, "w")) == NULL) {
    221 			retval = EXIT_FAILURE;
    222 			goto ret;
    223 		}
    224 		fprintf(branch_fp, "%s", branch_name);
    225 		fclose(branch_fp);
    226 		free(branch_name);
    227 	}
    228 	/* create '.baseline/workdir' file */
    229 	asprintf(&workdir_fname, "%s/workdir", dc_ctx->repo_baselinepath);
    230 	if ((fd = open(workdir_fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
    231 		free(workdir_fname);
    232 		retval = EXIT_FAILURE;
    233 		goto ret;
    234 	}
    235 	close(fd);
    236 ret:
    237 	free(dc_path);
    238 	free(branch_path);
    239 	return EXIT_SUCCESS;
    240 }
    241 
    242 static int
    243 simple_insert(struct dircache_ctx *dc_ctx, const char *path)
    244 {
    245 	char *objid, *paths[2], *cache_path;
    246 	char *dc_path, *p;
    247 	struct stat s, fs;
    248 	FILE *fp;
    249 	FTS *dir;
    250 	FTSENT *entry;
    251 	struct file *file;
    252 
    253 	dc_path = get_dircache_path(dc_ctx);
    254 	if (stat(path, &s) == -1)
    255 		return EXIT_FAILURE;
    256 	if (S_ISDIR(s.st_mode)) {
    257 		/* dirs are cached for now, and should be inserted at commit time */
    258 		paths[0] = (char *)path;
    259 		paths[1] = NULL;
    260 		if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
    261 			return EXIT_FAILURE;
    262 		while ((entry = fts_read(dir)) != NULL) {
    263 			/* skip directories starting with '.', other than our top-level directory */
    264 			if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) {
    265 				fts_set(dir, entry, FTS_SKIP);
    266 				continue;
    267 			}
    268 			if (entry->fts_info & FTS_D) {
    269 #ifdef DEBUG
    270 				printf("DS: %s\n", entry->fts_path);
    271 #endif
    272 				/* FIXME: path check is required */
    273 				p = dir_diff(entry->fts_path, dc_ctx->repo_rootpath);
    274 				if (*p != '\0') {
    275 					asprintf(&cache_path, "%s/%s", dc_path, p);
    276 					if (mkdir(cache_path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
    277 						free(cache_path);
    278 						return EXIT_FAILURE;
    279 					}
    280 #ifdef DEBUG
    281 					printf("[DEBUG] dircache: created dir %s\n", cache_path);
    282 #endif
    283 					free(cache_path);
    284 				}
    285 			}
    286 			if(entry->fts_info & FTS_F) {
    287 #ifdef DEBUG
    288 				printf("F: %s\n", entry->fts_path);
    289 #endif
    290 				if (stat(entry->fts_path, &fs) == -1)
    291 					return EXIT_FAILURE;
    292 				/* TODO: baseline_file_new2() */
    293 				file = baseline_file_new();
    294 				file->loc = LOC_FS;
    295 				if ((file->fd = open(entry->fts_path, O_RDONLY, 0)) == -1)
    296 					return EXIT_FAILURE;
    297 				dc_ctx->db_ops->insert_file(dc_ctx->db_ctx, file);
    298 				close(file->fd);
    299 				objid = strdup(file->id);
    300 				baseline_file_free(file);
    301 #ifdef DEBUG
    302 				printf("\t ID = %s\n", objid);
    303 #endif
    304 				/* FIXME: path check is required */
    305 				asprintf(&cache_path, "%s/%s", dc_path, dir_diff(entry->fts_path, dc_ctx->repo_rootpath));
    306 				if ((fp = fopen(cache_path, "w")) == NULL) {
    307 					free(cache_path);
    308 					return EXIT_FAILURE;
    309 				}
    310 #ifdef DEBUG
    311 				printf("[DEBUG] dircache: created file %s\n", cache_path);
    312 #endif
    313 				fprintf(fp, "F %s %06o\n", objid, fs.st_mode);
    314 				fclose(fp);
    315 				free(cache_path);
    316 			}
    317 		}
    318 		fts_close(dir);
    319 	}
    320 	else {
    321 		/* it's a file, why the hell would we wait, insert it immediately */
    322 		if (stat(path, &fs) == -1)
    323 			return EXIT_FAILURE;
    324 		/* TODO: baseline_file_new2() */
    325 		file = baseline_file_new();
    326 		file->loc = LOC_FS;
    327 		if ((file->fd = open(path, O_RDONLY, 0)) == -1)
    328 			return EXIT_FAILURE;
    329 		dc_ctx->db_ops->insert_file(dc_ctx->db_ctx, file);
    330 		close(file->fd);
    331 		objid = strdup(file->id);
    332 		baseline_file_free(file);
    333 
    334 		if (mkdirp(dc_path, dir_diff(path, dc_ctx->repo_rootpath)) == EXIT_FAILURE)
    335 			return EXIT_FAILURE;
    336 		asprintf(&cache_path, "%s/%s", dc_path, dir_diff(path, dc_ctx->repo_rootpath));
    337 		if ((fp = fopen(cache_path, "w")) == NULL) {
    338 			free(cache_path);
    339 			return EXIT_FAILURE;
    340 		}
    341 		fprintf(fp, "F %s %06o\n", objid, fs.st_mode);
    342 		fclose(fp);
    343 		free(objid);
    344 		free(cache_path);
    345 	}
    346 	return EXIT_SUCCESS;
    347 }
    348 
    349 static int
    350 gen_dindex(struct dircache_ctx *dc_ctx, char **didx)
    351 {
    352 	char *dircache_path, *didx_path = NULL;
    353 	char *paths[2];
    354 	int didx_fd;
    355 	struct stat s;
    356 	FILE *didx_fp;
    357 	FTS *ftsp;
    358 	FTSENT *entry;
    359 
    360 	dircache_path = get_dircache_path(dc_ctx);
    361 	if (stat(dircache_path, &s) == -1)
    362 		return EXIT_FAILURE;
    363 	if (!S_ISDIR(s.st_mode))
    364 		return EXIT_FAILURE; 
    365 	/* create dir-only index */
    366 	asprintf(&didx_path, "%s/dindex.XXXXXXX", dc_ctx->repo_baselinepath);
    367 	if ((didx_fd = mkstemp(didx_path)) == -1) {
    368 		return EXIT_FAILURE;
    369 	}
    370 	if ((didx_fp = fdopen(didx_fd, "w")) == NULL) {
    371 		unlink(didx_path);
    372 		close(didx_fd);
    373 		return EXIT_FAILURE;
    374 	}
    375 	paths[0] = (char *)dircache_path;
    376 	paths[1] = NULL;
    377 	if ((ftsp = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
    378 		return EXIT_FAILURE;
    379 	while ((entry = fts_read(ftsp)) != NULL) {
    380 		/* skip directories starting with '.', other than our top-level directory */
    381 		if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) {
    382 			fts_set(ftsp, entry, FTS_SKIP);
    383 			continue;
    384 		}
    385 		if (entry->fts_info & FTS_DP) {
    386 			/* FIXME: spaces! */
    387 			fprintf(didx_fp, "%s\n", entry->fts_path);
    388 		}
    389 	}
    390 	fts_close(ftsp);
    391 	fclose(didx_fp);
    392 	free(dircache_path);
    393 	*didx = didx_path;
    394 	return EXIT_SUCCESS;
    395 }
    396 
    397 static int
    398 simple_commit(struct dircache_ctx *dc_ctx, const char *msgfile)
    399 {
    400 	char *cur_branch, *cur_head;
    401 	char *objid, *path, *paths[2], tmp_objid[1024];
    402 	char *dircache_path, *didx_path;
    403 	struct stat s;
    404 	FILE *didx_fp, *fp;
    405 	FTS *dir;
    406 	FTSENT *entry;
    407 	unsigned int mode;
    408 	size_t size;
    409 	ssize_t len;
    410 	char type;
    411 	struct dir *ndir;
    412 	struct dirent *ent;
    413 	struct commit *com;
    414 
    415 	dircache_path = get_dircache_path(dc_ctx);
    416 	if (stat(dircache_path, &s) == -1)
    417 		return EXIT_FAILURE;
    418 	if (!S_ISDIR(s.st_mode))
    419 		return EXIT_FAILURE; 
    420 	/* get current branch */
    421 	if (simple_branch_get(dc_ctx, &cur_branch) == EXIT_FAILURE)
    422 		return EXIT_FAILURE;
    423 	/* get current branch's head (commit's parent) */
    424 	if (dc_ctx->db_ops->branch_get_head(dc_ctx->db_ctx, cur_branch, &cur_head) == EXIT_FAILURE)
    425 		return EXIT_FAILURE;
    426 
    427 	/* FIXME: empty dirache directory */
    428 	gen_dindex(dc_ctx, &didx_path);
    429 	if ((didx_fp = fopen(didx_path, "r")) == NULL)
    430 		return EXIT_FAILURE;
    431 	objid = NULL;
    432 	size = 0;
    433 	path = NULL;	/* if not set to NULL, realloc() will get pissed. And, it can waste your day! */
    434 	while ((len = getline(&path, &size, didx_fp)) != -1) {
    435 		if (path[len-1] == '\n') {
    436 			path[len-1] = 0;
    437 			--len;
    438 		}
    439 		ndir = baseline_dir_new();
    440 
    441 		paths[0] = (char *)path;
    442 		paths[1] = NULL;
    443 		if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL)
    444 			return EXIT_FAILURE;
    445 		while ((entry = fts_read(dir)) != NULL) {
    446 			if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) {
    447 				fts_set(dir, entry, FTS_SKIP);
    448 				continue;
    449 			}
    450 			if (entry->fts_info & FTS_F) {
    451 #ifdef DEBUG
    452 				printf("\t FILE: %s\n", entry->fts_path);
    453 #endif
    454 				if ((fp = fopen(entry->fts_path, "r")) == NULL)
    455 					return EXIT_FAILURE;
    456 				fscanf(fp, "%c %s %o", &type, tmp_objid, &mode);
    457 				if (type == 'F') {
    458 					ent = (struct dirent *)calloc(1, sizeof(struct dirent));
    459 					ent->id = strdup(tmp_objid);
    460 					ent->name = strdup(entry->fts_name);
    461 					ent->mode = mode;
    462 					ent->type = T_FILE;
    463 					baseline_dir_append(ndir, ent);
    464 					printf("[+] F %06o\t%s\n", mode, entry->fts_name);
    465 				}
    466 				else if (type == 'D') {
    467 					ent = (struct dirent *)calloc(1, sizeof(struct dirent));
    468 					ent->id = strdup(tmp_objid);
    469 					ent->name = strdup(entry->fts_name);
    470 					ent->mode = mode;
    471 					ent->type = T_DIR;
    472 					baseline_dir_append(ndir, ent);
    473 					/* FIXME: dir mode are copied from dircache, hence not preserved */
    474 					printf("[+] D %06o\t%s\n", mode, entry->fts_name);
    475 				}
    476 				fclose(fp);
    477 				unlink(entry->fts_path);
    478 			}
    479 			else {
    480 				if (entry->fts_level >= 1) {
    481 					fts_set(dir, entry, FTS_SKIP);
    482 				}
    483 			}
    484 		}
    485 		fts_close(dir);
    486 		dc_ctx->db_ops->insert_dir(dc_ctx->db_ctx, ndir);
    487 		free(objid);
    488 		objid = strdup(ndir->id);
    489 		baseline_dir_free(ndir);
    490 #ifdef DEBUG
    491 		printf("dir \'%s\' commited with ID = %s\n", path, objid);
    492 #endif
    493 		if (stat(path, &s) == -1)
    494 			return EXIT_FAILURE;
    495 		/* FIXME: do not remove '.baseline/dircache' dir */
    496 		if (rmdir(path) == -1)
    497 			return EXIT_FAILURE;
    498 		if ((fp = fopen(path, "w")) == NULL)
    499 			return EXIT_FAILURE;
    500 		fprintf(fp, "%c %s %06o", 'D', objid, s.st_mode);
    501 		fclose(fp);
    502 	}
    503 	fclose(didx_fp);
    504 	unlink(didx_path);
    505 	/* temp */
    506 	unlink(dircache_path);
    507 	if (mkdir(dircache_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) {
    508 		return EXIT_FAILURE;
    509 	}
    510 	/* generate commit */
    511 	if ((com = baseline_helper_commit_build(dc_ctx, objid, cur_head, msgfile)) == NULL)
    512 		return EXIT_FAILURE;
    513 	/* insert the commit into the db */
    514 	dc_ctx->db_ops->insert_commit(dc_ctx->db_ctx, com);
    515 	printf("commit id: %s\n", com->id);
    516 	unlink(path);
    517 	/* update the branch's head */
    518 	dc_ctx->db_ops->branch_set_head(dc_ctx->db_ctx, cur_branch, com->id);
    519 	/* update the working dir */
    520 	simple_workdir_set(dc_ctx, com->id);
    521 	free(cur_branch);
    522 	free(cur_head);
    523 	free(path);
    524 	free(objid);
    525 	baseline_commit_free(com);
    526 	return EXIT_SUCCESS;
    527 }
    528 
    529 static int
    530 simple_branch_get(struct dircache_ctx *dc_ctx, char **branch_name)
    531 {
    532 	char *fname;
    533 	size_t size = 0;
    534 	FILE *fp;
    535 
    536 	if (dc_ctx == NULL || branch_name == NULL)
    537 		return EXIT_FAILURE;
    538 	asprintf(&fname, "%s/branch", dc_ctx->repo_baselinepath);
    539 	*branch_name = NULL;
    540 	if ((fp = fopen(fname, "r")) == NULL)
    541 		return EXIT_FAILURE;
    542 	if (getline(branch_name, &size, fp) == -1) {
    543 		free(*branch_name);
    544 		return EXIT_FAILURE;
    545 	}
    546 	fclose(fp);
    547 	free(fname);
    548 	return EXIT_SUCCESS;
    549 }
    550 
    551 static int
    552 simple_branch_set(struct dircache_ctx *dc_ctx, const char *branch_name)
    553 {
    554 	char *fname;
    555 	FILE *fp;
    556 
    557 	if (dc_ctx == NULL || branch_name == NULL)
    558 		return EXIT_FAILURE;
    559 	asprintf(&fname, "%s/branch", dc_ctx->repo_baselinepath);
    560 	if ((fp = fopen(fname, "w")) == NULL)
    561 		return EXIT_FAILURE;
    562 	fprintf(fp, "%s", branch_name);
    563 	fclose(fp);
    564 	free(fname);
    565 	return EXIT_SUCCESS;
    566 }
    567 
    568 static int
    569 simple_workdir_get(struct dircache_ctx *dc_ctx, char **commit_id)
    570 {
    571 	char *fname;
    572 	size_t size = 0;
    573 	FILE *fp;
    574 
    575 	if (dc_ctx == NULL || commit_id == NULL)
    576 		return EXIT_FAILURE;
    577 	asprintf(&fname, "%s/workdir", dc_ctx->repo_baselinepath);
    578 	if ((fp = fopen(fname, "r")) == NULL)
    579 		return EXIT_FAILURE;
    580 	*commit_id = NULL;
    581 	if (getline(commit_id, &size, fp) == -1) {
    582 		/* assuming the file is empty */
    583 		/* FIXME: check for errors */
    584 		*commit_id = NULL;
    585 	}
    586 	fclose(fp);
    587 	free(fname);
    588 	return EXIT_SUCCESS;
    589 }
    590 
    591 static int
    592 simple_workdir_set(struct dircache_ctx *dc_ctx, const char *commit_id)
    593 {
    594 	char *fname;
    595 	FILE *fp;
    596 
    597 	if (dc_ctx == NULL || commit_id == NULL)
    598 		return EXIT_FAILURE;
    599 	asprintf(&fname, "%s/workdir", dc_ctx->repo_baselinepath);
    600 	if ((fp = fopen(fname, "w")) == NULL)
    601 		return EXIT_FAILURE;
    602 	fprintf(fp, "%s", commit_id);
    603 	fclose(fp);
    604 	free(fname);
    605 	return EXIT_SUCCESS;
    606 }
    607