-
Notifications
You must be signed in to change notification settings - Fork 5
/
util_locking.c
137 lines (113 loc) · 4.32 KB
/
util_locking.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
This file is part of ESFS, a FUSE-based filesystem that supports snapshots.
ESFS is Copyright (C) 2013 Elod Csirmaz
<http://www.epcsirmaz.com/> <https://github.com/csirmaz>.
ESFS is based on Big Brother File System (fuse-tutorial)
Copyright (C) 2012 Joseph J. Pfeiffer, Jr., Ph.D. <[email protected]>,
and was forked from it on 21 August 2013.
Big Brother File System can be distributed under the terms of
the GNU GPLv3. See the file COPYING.
See also <http://www.cs.nmsu.edu/~pfeiffer/fuse-tutorial/>.
Big Brother File System was derived from function prototypes found in
/usr/include/fuse/fuse.h
Copyright (C) 2001-2007 Miklos Szeredi <[email protected]>
fuse.h is licensed under the LGPLv2.
ESFS 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.
ESFS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* NOTE: A Perl script is used to replace $ with esfs_ and $$ with ESFS_
* in this file. To write $, use \$.
*/
// This file contains low-level tools that use locking
/** Helper function for the implementation of rm -r
*
* Returns 0 or errno.
*/
int $_univ_rm(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
if(typeflag == FTW_DP || typeflag == FTW_D || typeflag == FTW_DNR) {
if(rmdir(fpath) != 0) { return errno; }
return 0;
}
if(unlink(fpath) != 0) { return errno; }
return 0;
}
/** Implementation of rm -r
*
* Returns 0 or -errno.
*/
// TODO 2 Make this atomic (and thread safe) by renaming the snapshot first
static inline int $recursive_remove(struct $fsdata_t *fsdata, const char *path)
{
int ret, lock;
if((lock = $mflock_lock(fsdata, $string2locklabel($$LOCKLABEL_RMDIR))) < 0) { return lock; }
ret = nftw(path, $_univ_rm, $$RECURSIVE_RM_FDS, FTW_DEPTH | FTW_PHYS);
if((lock = $mflock_unlock(fsdata, lock)) != 0) { return lock; }
if(ret >= 0) { return -ret; } // success or errno
return -ENOMEM; // generic error encountered by nftw
}
/** Implementation of mkdir -p
*
* Creates the directory at *the parent of* path and any parent directories as needed
*
* If firstcreated != NULL, it is set to be the top directory created
*
* Returns:
* * 0 if the directory already exists
* * 1 if a directory has been created
* * -errno on failure.
*/
// NOTE: dirname() and basename() are not thread safe
static int $mkpath(const char *path, char firstcreated[$$PATH_MAX], mode_t mode)
{
int slashpos;
int statret;
int mkpathret;
int mkdirret;
char prepath[$$PATH_MAX];
struct stat mystat;
// find previous slash
slashpos = strlen(path) - 1;
while(slashpos >= 0 && path[slashpos] != $$DIRSEPCH) { slashpos--; }
if(slashpos > 0) { // found one
strncpy(prepath, path, slashpos);
prepath[slashpos] = '\0';
} else { // no slash or slash is first character
return -EBADE;
}
// $dlogdbg("mkpath: %s <- %s\n", path, prepath);
if(lstat(prepath, &mystat) != 0) {
statret = errno;
if(statret == ENOENT) { // the parent node does not exist
mkpathret = $mkpath(prepath, firstcreated, mode);
// $dlogdbg("mkpath: recursion returned with %d\n", p);
if(mkpathret < 0) { return mkpathret; } // error
// attempt to create the directory
if(mkdir(prepath, mode) != 0) {
mkdirret = errno;
// Don't abort on EEXIST as another thread might be busy creating
// the same directories. Simply assume success.
if(mkdirret != EEXIST) {
return -mkdirret;
}
}
// save first directory created
if(mkpathret == 0 && firstcreated != NULL) { strcpy(firstcreated, prepath); }
return 1;
}
return -statret;
}
if(S_ISDIR(mystat.st_mode)) {
return 0; // node exists and is a directory
}
return -ENOTDIR; // found a node but it isn't a directory
}