This repository has been archived by the owner on May 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprivate.c
145 lines (119 loc) · 4.08 KB
/
private.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
137
138
139
140
141
142
143
144
145
#include "private.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include "disk.h"
char* get_real_path(struct ufs_disk* disk, const char* path)
{
char* real_path = calloc(strlen(disk->mountpoint) + strlen(path) + 1, sizeof(char));
strcpy(real_path, disk->mountpoint);
strcat(real_path, path);
return real_path;
}
static uint64_t get_disk_free_space(const char* mountpoint)
{
uint64_t free_space = 0;
struct statvfs stbuf;
if (statvfs(mountpoint, &stbuf) == 0)
free_space = stbuf.f_bavail * stbuf.f_frsize;
return free_space;
}
static struct ufs_disk* select_disk_by_free_space(struct ufs_disk* disks[])
{
struct ufs_disk* selected_disk = disks[0];
uint64_t max_free_space = get_disk_free_space(selected_disk->mountpoint);
for (struct ufs_disk** disk_iter = disks + 1; *disk_iter; ++disk_iter) {
uint64_t free_space = get_disk_free_space((*disk_iter)->mountpoint);
if (free_space > max_free_space) {
max_free_space = free_space;
selected_disk = *disk_iter;
}
}
return selected_disk;
}
static struct ufs_disk* select_disk(struct unionfs* fs, const char* path)
{
/* find all disks which contain parent path */
struct ufs_disk** parent_disks = calloc(fs->disks_count + 1, sizeof(struct ufs_disk*));
struct ufs_disk** parent_disk_iter = parent_disks;
for (struct ufs_disk* disk = fs->all_disks; disk != fs->all_disks + fs->disks_count; ++disk) {
char* real_path = get_real_path(disk, path);
if (access(dirname(real_path), F_OK) == 0)
*parent_disk_iter++ = disk;
free(real_path);
}
if (parent_disk_iter - parent_disks == 1) {
struct ufs_disk* selected_disk = parent_disks[0];
free(parent_disks);
return selected_disk;
}
/* filter out read-only disks */
parent_disk_iter = parent_disks;
while (*parent_disk_iter) {
struct ufs_disk* disk = *parent_disk_iter;
if (disk->mount_flags & ST_RDONLY || disk->custom_flags & UFS_DISK_NO_SHARED_WRITES) {
struct ufs_disk** citer = parent_disk_iter;
struct ufs_disk** niter = citer + 1;
while (*niter != 0) {
*citer = *niter;
citer++;
niter++;
}
*citer = 0;
} else {
parent_disk_iter++;
}
}
/* consider "nothing found" as "found everywhere", this will lead to exact error later */
if (parent_disk_iter == parent_disks)
for (struct ufs_disk* disk = fs->all_disks; disk != fs->all_disks + fs->disks_count; ++disk)
*parent_disk_iter++ = disk;
assert(parent_disk_iter != parent_disks);
assert(parent_disk_iter - parent_disks <= (intptr_t)fs->disks_count);
struct ufs_disk* selected_disk = NULL;
time_t now = time(NULL);
/* if last disk selection happened long time ago - select new disk by free space */
if (now - fs->last_disk_selection < fs->config->disk_cache_timeout) {
assert(fs->last_used_disk);
/* if last used disk in list with disks contain parent path - select next disk */
parent_disk_iter = parent_disks;
while (*parent_disk_iter) {
if (*parent_disk_iter == fs->last_used_disk)
break;
++parent_disk_iter;
}
if (*parent_disk_iter) {
++parent_disk_iter;
if (!*parent_disk_iter)
parent_disk_iter = parent_disks;
selected_disk = *parent_disk_iter;
}
}
if (!selected_disk)
selected_disk = select_disk_by_free_space(parent_disks);
fs->last_used_disk = selected_disk;
fs->last_disk_selection = now;
free(parent_disks);
return selected_disk;
}
char* new_real_path(struct unionfs* fs, const char* path)
{
struct ufs_disk* disk = select_disk(fs, path);
return get_real_path(disk, path);
}
static struct ufs_disk* get_disk_by_device_id(struct unionfs* fs, dev_t id)
{
for (struct ufs_disk* disk = fs->all_disks; disk != fs->all_disks + fs->disks_count; ++disk)
if (disk->device_id == id)
return disk;
return NULL;
}
ino_t calc_ino(struct unionfs* fs, dev_t dev_id, ino_t orig_ino)
{
struct ufs_disk* disk = get_disk_by_device_id(fs, dev_id);
return disk ? disk->inode_offset + orig_ino : orig_ino;
}