File: dir.c

package info (click to toggle)
apfsprogs 0.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,108 kB
  • sloc: ansic: 16,034; makefile: 175; sh: 57
file content (232 lines) | stat: -rw-r--r-- 6,682 bytes parent folder | download | duplicates (3)
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * Copyright (C) 2019 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
 */

#include <sys/stat.h> /* The macros for the inode mode */
#include <sys/types.h>
#include <unistd.h>
#include <apfs/checksum.h>
#include <apfs/raw.h>
#include <apfs/types.h>
#include "dir.h"
#include "mkapfs.h"

/**
 * set_key_header - Set the cnid and type on a catalog key
 * @ino:	inode number
 * @type:	record type
 * @key:	key header to set
 */
static void set_key_header(u64 ino, u64 type, struct apfs_key_header *key)
{
	u64 obj_id_and_type;

	obj_id_and_type = type << APFS_OBJ_TYPE_SHIFT;
	obj_id_and_type |= ino;
	key->obj_id_and_type = cpu_to_le64(obj_id_and_type);
}

/**
 * make_unhashed_dentry_key - Make an unhashed dentry key
 * @ino:	inode number for the parent
 * @name:	directory name
 * @key:	key space to use
 *
 * Returns the length of the key.
 */
static int make_unhashed_dentry_key(u64 ino, char *name,
				    struct apfs_drec_key *key)
{
	u32 len = strlen(name) + 1; /* The null termination is counted */

	set_key_header(ino, APFS_TYPE_DIR_REC, &key->hdr);
	strcpy((char *)key->name, name);
	key->name_len = cpu_to_le16(len);
	return sizeof(*key) + len;
}

/**
 * make_hashed_dentry_key - Make a hashed dentry key
 * @ino:	inode number for the parent
 * @name:	directory name
 * @key:	key space to use
 *
 * Returns the length of the key.
 */
static int make_hashed_dentry_key(u64 ino, char *name,
				  struct apfs_drec_hashed_key *key)
{
	u32 len;
	u32 hash = 0xFFFFFFFF;
	u32 utf32;

	set_key_header(ino, APFS_TYPE_DIR_REC, &key->hdr);
	strcpy((char *)key->name, name);

	len = strlen(name) + 1; /* The null termination is counted */

	/* Special directories don't have unicode characters in their names */
	for (utf32 = *name; utf32; utf32 = *++name)
		hash = crc32c(hash, &utf32, sizeof(utf32));
	hash = (hash & 0x3FFFFF) << 10;

	key->name_len_and_hash = cpu_to_le32(hash | len);
	return sizeof(*key) + len;
}

/**
 * make_special_dentry_key - Make the dentry key for a special directory
 * @ino:	inode number for the parent
 * @name:	directory name
 * @key:	key space to use
 *
 * Returns the length of the key.
 */
static int make_special_dentry_key(u64 ino, char *name, void *key)
{
	if (param->norm_sensitive)
		return make_unhashed_dentry_key(ino, name, key);
	else
		return make_hashed_dentry_key(ino, name, key);
}

/**
 * make_special_dentry_val - Make the dentry value for a special directory
 * @ino:	inode number for the directory
 * @val_end:	end of the value space to use
 *
 * Returns the length of the value.
 */
static int make_special_dentry_val(u64 ino, struct apfs_drec_val *val_end)
{
	struct apfs_drec_val *val = (void *)val_end - sizeof(*val);

	val->file_id = cpu_to_le64(ino);
	val->date_added = cpu_to_le64(get_timestamp());
	val->flags = cpu_to_le16(S_IFDIR >> 12);

	return sizeof(*val);
}

/**
 * make_special_inode_key - Make the inode key for a special directory
 * @ino:	inode number for the directory
 * @key:	key space to use
 *
 * Returns the length of the key.
 */
static int make_special_inode_key(u64 ino, struct apfs_inode_key *key)
{
	set_key_header(ino, APFS_TYPE_INODE, &key->hdr);
	return sizeof(*key);
}

/**
 * make_special_inode_val - Make the inode value for a special directory
 * @ino:	inode number for the directory
 * @name:	directory name
 * @val_end:	end of the value space to use
 *
 * Returns the length of the value.
 */
static int make_special_inode_val(u64 ino, char *name,
				  struct apfs_inode_val *val_end)
{
	struct apfs_inode_val *val = (void *)val_end - sizeof(*val);
	struct apfs_xf_blob *xblob;
	struct apfs_x_field *name_xfield;
	int namelen, padded_namelen, i_len;

	namelen = strlen(name) + 1;
	padded_namelen = ROUND_UP(namelen, 8);
	i_len = sizeof(*val) + sizeof(*xblob) +
		sizeof(*name_xfield) + padded_namelen;
	val = (void *)val_end - i_len;

	val->parent_id = cpu_to_le64(APFS_ROOT_DIR_PARENT);
	val->private_id = cpu_to_le64(ino);

	/* Should this be the exact same as the dentry timetamp? */
	val->create_time = val->mod_time = val->change_time =
			   val->access_time = cpu_to_le64(get_timestamp());

	val->default_protection_class =
				cpu_to_le32(APFS_PROTECTION_CLASS_DIR_NONE);

	/* TODO: allow the user to override these fields */
	val->owner = cpu_to_le32(geteuid());
	val->group = cpu_to_le32(getegid());
	val->mode = cpu_to_le16(0755 | S_IFDIR);

	xblob = (struct apfs_xf_blob *)val->xfields;
	xblob->xf_num_exts = cpu_to_le16(1); /* Just the primary name */
	xblob->xf_used_data = cpu_to_le16(padded_namelen);

	name_xfield = (struct apfs_x_field *)xblob->xf_data;
	name_xfield->x_type = APFS_INO_EXT_TYPE_NAME;
	name_xfield->x_flags = APFS_XF_DO_NOT_COPY;
	name_xfield->x_size = cpu_to_le16(namelen);

	strcpy((char *)val_end - padded_namelen, name);
	return i_len;
}

/**
 * make_special_dir_inode - Make inode record for an empty special dir
 * @ino:		inode number for the directory
 * @name:		directory name
 * @next_toc:		next available toc entry (updated on return)
 * @key_area:		start of the key area
 * @key:		start of the available key area (updated on return)
 * @val_area_end:	end of the value area
 * @val_end:		end of the available value area (updated on return)
 */
void make_special_dir_inode(u64 ino, char *name, struct apfs_kvloc **next_toc,
			    void *key_area, void **key,
			    void *val_area_end, void **val_end)
{
	struct apfs_kvloc *kvloc = *next_toc;
	int len;

	len = make_special_inode_key(ino, *key);
	kvloc->k.off = cpu_to_le16(*key - key_area);
	kvloc->k.len = cpu_to_le16(len);
	*key += len;

	len = make_special_inode_val(ino, name, *val_end);
	kvloc->v.off = cpu_to_le16(val_area_end - *val_end + len);
	kvloc->v.len = cpu_to_le16(len);
	*val_end -= len;

	++*next_toc;
}

/**
 * make_special_dir_dentry - Make dentry record for an empty special dir
 * @ino:		inode number for the directory
 * @name:		directory name
 * @next_toc:		next available toc entry (updated on return)
 * @key_area:		start of the key area
 * @key:		start of the available key area (updated on return)
 * @val_area_end:	end of the value area
 * @val_end:		end of the available value area (updated on return)
 */
void make_special_dir_dentry(u64 ino, char *name, struct apfs_kvloc **next_toc,
			     void *key_area, void **key,
			     void *val_area_end, void **val_end)
{
	struct apfs_kvloc *kvloc = *next_toc;
	int len;

	len = make_special_dentry_key(APFS_ROOT_DIR_PARENT, name, *key);
	kvloc->k.off = cpu_to_le16(*key - key_area);
	kvloc->k.len = cpu_to_le16(len);
	*key += len;

	len = make_special_dentry_val(ino, *val_end);
	kvloc->v.off = cpu_to_le16(val_area_end - *val_end + len);
	kvloc->v.len = cpu_to_le16(len);
	*val_end -= len;

	++*next_toc;
}