106 lines · 2.8 KB · C
RawDownload
/* * vfs/make_path.c - compound VFS op: create a directory path, making any * missing parent components.  Layered on the vfs_mkdir/vfs_stat primitives. * * Moved out of util1.c as part of the VFS compound layer: filesystem mechanics * live in vfs/, so the operator-path resolution policy travels as an explicit * vfs_flags argument rather than ambient state. * * Copyright (C) 1998-2022 Andrew Tridgell, Martin Pool, Wayne Davison * Copyright (C) 2026 Wayne Davison, Andrew Tridgell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include "rsync.h"#include "vfs/vfs_internal.h" /* Creates a directory path, making missing components as needed.  mkp_flags is * MKP_DROP_NAME / MKP_SKIP_SLASH (path handling); vfs_flags is the resolution * policy passed straight to vfs_mkdir (VFS_OPERATOR_PATH for operator-supplied * --backup-dir trees, else 0).  Returns the number of dirs created, or a * negative value on error. */int vfs_make_path(char *fname, int mkp_flags, int vfs_flags){	char *end, *p;	int ret = 0; 	if (mkp_flags & MKP_SKIP_SLASH) {		while (*fname == '/')			fname++;	} 	while (*fname == '.' && fname[1] == '/')		fname += 2; 	if (mkp_flags & MKP_DROP_NAME) {		end = strrchr(fname, '/');		if (!end || end == fname)			return 0;		*end = '\0';	} else		end = fname + strlen(fname); 	/* Try to find an existing dir, starting from the deepest dir. */	for (p = end; ; ) {		if (dry_run) {			STRUCT_STAT st;			if (vfs_stat(VFS_AT_FDCWD, fname, &st, VFS_ALLOW_SYMLINK) == 0) {				if (S_ISDIR(st.st_mode))					errno = EEXIST;				else					errno = ENOTDIR;			}		} else if (vfs_mkdir(VFS_AT_FDCWD, fname, ACCESSPERMS, vfs_flags) == 0) {			ret++;			break;		} 		if (errno != ENOENT) {			STRUCT_STAT st;			if (errno != EEXIST || (vfs_stat(VFS_AT_FDCWD, fname, &st, VFS_ALLOW_SYMLINK) == 0 && !S_ISDIR(st.st_mode)))				ret = -ret - 1;			break;		}		while (1) {			if (p == fname) {				/* We got a relative path that doesn't exist, so assume that '.'				 * is there and just break out and create the whole thing. */				p = NULL;				goto double_break;			}			if (*--p == '/') {				if (p == fname) {					/* We reached the "/" dir, which we assume is there. */					goto double_break;				}				*p = '\0';				break;			}		}	}  double_break: 	/* Make all the dirs that we didn't find on the way here. */	while (p != end) {		if (p)			*p = '/';		else			p = fname;		p += strlen(p);		if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */			continue;		if (vfs_mkdir(VFS_AT_FDCWD, fname, ACCESSPERMS, vfs_flags) < 0)			ret = -ret - 1;		else			ret++;	} 	if (mkp_flags & MKP_DROP_NAME)		*end = '/'; 	return ret;}