Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/util/profile/prof_file.c
34889 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* prof_file.c ---- routines that manipulate an individual profile file.
4
*/
5
6
#include "prof_int.h"
7
8
#include <stdio.h>
9
#ifdef HAVE_STDLIB_H
10
#include <stdlib.h>
11
#endif
12
#ifdef HAVE_UNISTD_H
13
#include <unistd.h>
14
#endif
15
#include <string.h>
16
#include <stddef.h>
17
18
#include <sys/types.h>
19
#include <sys/stat.h>
20
#include <errno.h>
21
22
#ifdef HAVE_PWD_H
23
#include <pwd.h>
24
#endif
25
26
#if defined(_WIN32)
27
#include <io.h>
28
#define HAVE_STAT
29
#define stat _stat
30
#ifndef S_ISDIR
31
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
32
#endif
33
#endif
34
35
#include "k5-platform.h"
36
37
struct global_shared_profile_data {
38
/* This is the head of the global list of shared trees */
39
prf_data_t trees;
40
/* Lock for above list. */
41
k5_mutex_t mutex;
42
};
43
#define g_shared_trees (krb5int_profile_shared_data.trees)
44
#define g_shared_trees_mutex (krb5int_profile_shared_data.mutex)
45
46
static struct global_shared_profile_data krb5int_profile_shared_data = {
47
0,
48
K5_MUTEX_PARTIAL_INITIALIZER
49
};
50
51
MAKE_INIT_FUNCTION(profile_library_initializer);
52
MAKE_FINI_FUNCTION(profile_library_finalizer);
53
54
int profile_library_initializer(void)
55
{
56
#ifdef SHOW_INITFINI_FUNCS
57
printf("profile_library_initializer\n");
58
#endif
59
add_error_table(&et_prof_error_table);
60
61
return k5_mutex_finish_init(&g_shared_trees_mutex);
62
}
63
void profile_library_finalizer(void)
64
{
65
if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING()) {
66
#ifdef SHOW_INITFINI_FUNCS
67
printf("profile_library_finalizer: skipping\n");
68
#endif
69
return;
70
}
71
#ifdef SHOW_INITFINI_FUNCS
72
printf("profile_library_finalizer\n");
73
#endif
74
k5_mutex_destroy(&g_shared_trees_mutex);
75
76
remove_error_table(&et_prof_error_table);
77
}
78
79
static void profile_free_file_data(prf_data_t);
80
81
static int rw_access(const_profile_filespec_t filespec)
82
{
83
#ifdef HAVE_ACCESS
84
if (access(filespec, W_OK) == 0)
85
return 1;
86
else
87
return 0;
88
#else
89
/*
90
* We're on a substandard OS that doesn't support access. So
91
* we kludge a test using stdio routines, and hope fopen
92
* checks the r/w permissions.
93
*/
94
FILE *f;
95
96
f = fopen(filespec, "r+");
97
if (f) {
98
fclose(f);
99
return 1;
100
}
101
return 0;
102
#endif
103
}
104
105
static int r_access(const_profile_filespec_t filespec)
106
{
107
#ifdef HAVE_ACCESS
108
if (access(filespec, R_OK) == 0)
109
return 1;
110
else
111
return 0;
112
#else
113
/*
114
* We're on a substandard OS that doesn't support access. So
115
* we kludge a test using stdio routines, and hope fopen
116
* checks the r/w permissions.
117
*/
118
FILE *f;
119
120
f = fopen(filespec, "r");
121
if (f) {
122
fclose(f);
123
return 1;
124
}
125
return 0;
126
#endif
127
}
128
129
int profile_file_is_writable(prf_file_t profile)
130
{
131
if (profile && profile->data) {
132
return rw_access(profile->data->filespec);
133
} else {
134
return 0;
135
}
136
}
137
138
prf_data_t
139
profile_make_prf_data(const char *filename)
140
{
141
prf_data_t d;
142
size_t len, flen, slen;
143
char *fcopy;
144
145
flen = strlen(filename);
146
slen = offsetof(struct _prf_data_t, filespec);
147
len = slen + flen + 1;
148
if (len < sizeof(struct _prf_data_t))
149
len = sizeof(struct _prf_data_t);
150
d = malloc(len);
151
if (d == NULL)
152
return NULL;
153
memset(d, 0, len);
154
fcopy = (char *) d + slen;
155
assert(fcopy == d->filespec);
156
strlcpy(fcopy, filename, flen + 1);
157
d->refcount = 1;
158
d->magic = PROF_MAGIC_FILE_DATA;
159
d->root = NULL;
160
d->next = NULL;
161
d->fslen = flen;
162
if (k5_mutex_init(&d->lock) != 0) {
163
free(d);
164
return NULL;
165
}
166
return d;
167
}
168
169
errcode_t profile_open_file(const_profile_filespec_t filespec,
170
prf_file_t *ret_prof, char **ret_modspec)
171
{
172
prf_file_t prf;
173
errcode_t retval;
174
char *home_env = 0;
175
prf_data_t data;
176
char *expanded_filename;
177
178
retval = CALL_INIT_FUNCTION(profile_library_initializer);
179
if (retval)
180
return retval;
181
182
prf = malloc(sizeof(struct _prf_file_t));
183
if (!prf)
184
return ENOMEM;
185
memset(prf, 0, sizeof(struct _prf_file_t));
186
prf->magic = PROF_MAGIC_FILE;
187
188
if (filespec[0] == '~' && filespec[1] == '/') {
189
home_env = secure_getenv("HOME");
190
#ifdef HAVE_PWD_H
191
if (home_env == NULL) {
192
uid_t uid;
193
struct passwd *pw, pwx;
194
char pwbuf[BUFSIZ];
195
196
uid = getuid();
197
if (!k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
198
&& pw != NULL && pw->pw_dir[0] != 0)
199
home_env = pw->pw_dir;
200
}
201
#endif
202
}
203
if (home_env) {
204
if (asprintf(&expanded_filename, "%s%s", home_env,
205
filespec + 1) < 0)
206
expanded_filename = 0;
207
} else
208
expanded_filename = strdup(filespec);
209
if (expanded_filename == 0) {
210
free(prf);
211
return ENOMEM;
212
}
213
214
k5_mutex_lock(&g_shared_trees_mutex);
215
for (data = g_shared_trees; data; data = data->next) {
216
if (!strcmp(data->filespec, expanded_filename)
217
/* Check that current uid has read access. */
218
&& r_access(data->filespec))
219
break;
220
}
221
if (data) {
222
data->refcount++;
223
data->last_stat = 0; /* Make sure to stat when updating. */
224
k5_mutex_unlock(&g_shared_trees_mutex);
225
retval = profile_update_file_data(data, NULL);
226
free(expanded_filename);
227
if (retval) {
228
profile_dereference_data(data);
229
free(prf);
230
return retval;
231
}
232
prf->data = data;
233
*ret_prof = prf;
234
return 0;
235
}
236
k5_mutex_unlock(&g_shared_trees_mutex);
237
data = profile_make_prf_data(expanded_filename);
238
if (data == NULL) {
239
free(prf);
240
free(expanded_filename);
241
return ENOMEM;
242
}
243
free(expanded_filename);
244
prf->data = data;
245
246
retval = profile_update_file(prf, ret_modspec);
247
if (retval) {
248
profile_close_file(prf);
249
return retval;
250
}
251
252
k5_mutex_lock(&g_shared_trees_mutex);
253
data->flags |= PROFILE_FILE_SHARED;
254
data->next = g_shared_trees;
255
g_shared_trees = data;
256
k5_mutex_unlock(&g_shared_trees_mutex);
257
258
*ret_prof = prf;
259
return 0;
260
}
261
262
prf_file_t profile_open_memory(void)
263
{
264
struct profile_node *root = NULL;
265
prf_file_t file = NULL;
266
prf_data_t data;
267
268
file = calloc(1, sizeof(*file));
269
if (file == NULL)
270
goto errout;
271
file->magic = PROF_MAGIC_FILE;
272
273
if (profile_create_node("(root)", NULL, &root) != 0)
274
goto errout;
275
276
data = profile_make_prf_data("");
277
if (data == NULL)
278
goto errout;
279
280
data->root = root;
281
data->flags = PROFILE_FILE_NO_RELOAD | PROFILE_FILE_DIRTY;
282
file->data = data;
283
file->next = NULL;
284
return file;
285
286
errout:
287
free(file);
288
if (root != NULL)
289
profile_free_node(root);
290
return NULL;
291
}
292
293
errcode_t profile_update_file_data_locked(prf_data_t data, char **ret_modspec)
294
{
295
errcode_t retval;
296
#ifdef HAVE_STAT
297
struct stat st;
298
unsigned long frac;
299
time_t now;
300
#endif
301
FILE *f;
302
int isdir = 0;
303
304
/* Don't reload if the backing file isn't a regular file. */
305
if ((data->flags & PROFILE_FILE_NO_RELOAD) && data->root != NULL)
306
return 0;
307
/* Don't reload a modified data object, as the modifications may be
308
* important for this object's use. */
309
if (data->flags & PROFILE_FILE_DIRTY)
310
return 0;
311
312
#ifdef HAVE_STAT
313
now = time(0);
314
if (now == data->last_stat && data->root != NULL) {
315
return 0;
316
}
317
if (stat(data->filespec, &st)) {
318
return errno;
319
}
320
data->last_stat = now;
321
#if defined HAVE_STRUCT_STAT_ST_MTIMENSEC
322
frac = st.st_mtimensec;
323
#elif defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
324
frac = st.st_mtimespec.tv_nsec;
325
#elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
326
frac = st.st_mtim.tv_nsec;
327
#else
328
frac = 0;
329
#endif
330
if (st.st_mtime == data->timestamp
331
&& frac == data->frac_ts
332
&& data->root != NULL) {
333
return 0;
334
}
335
if (data->root) {
336
profile_free_node(data->root);
337
data->root = 0;
338
}
339
340
/* Only try to reload regular files, not devices such as pipes. */
341
if ((st.st_mode & S_IFMT) != S_IFREG)
342
data->flags |= PROFILE_FILE_NO_RELOAD;
343
#else
344
/*
345
* If we don't have the stat() call, assume that our in-core
346
* memory image is correct. That is, we won't reread the
347
* profile file if it changes.
348
*/
349
if (data->root) {
350
return 0;
351
}
352
#endif
353
354
#ifdef HAVE_STAT
355
isdir = S_ISDIR(st.st_mode);
356
#endif
357
if (!isdir) {
358
errno = 0;
359
f = fopen(data->filespec, "r");
360
if (f == NULL)
361
return (errno != 0) ? errno : ENOENT;
362
set_cloexec_file(f);
363
}
364
365
data->upd_serial++;
366
367
if (isdir) {
368
retval = profile_process_directory(data->filespec, &data->root);
369
} else {
370
retval = profile_parse_file(f, &data->root, ret_modspec);
371
(void)fclose(f);
372
}
373
if (retval) {
374
return retval;
375
}
376
assert(data->root != NULL);
377
#ifdef HAVE_STAT
378
data->timestamp = st.st_mtime;
379
data->frac_ts = frac;
380
#endif
381
return 0;
382
}
383
384
errcode_t profile_update_file_data(prf_data_t data, char **ret_modspec)
385
{
386
errcode_t retval;
387
388
k5_mutex_lock(&data->lock);
389
retval = profile_update_file_data_locked(data, ret_modspec);
390
k5_mutex_unlock(&data->lock);
391
return retval;
392
}
393
394
static int
395
make_hard_link(const char *oldpath, const char *newpath)
396
{
397
#ifdef _WIN32
398
return -1;
399
#else
400
return link(oldpath, newpath);
401
#endif
402
}
403
404
static errcode_t write_data_to_file(prf_data_t data, const char *outfile,
405
int can_create)
406
{
407
FILE *f;
408
profile_filespec_t new_file;
409
profile_filespec_t old_file;
410
errcode_t retval = 0;
411
412
retval = ENOMEM;
413
414
new_file = old_file = 0;
415
if (asprintf(&new_file, "%s.$$$", outfile) < 0) {
416
new_file = NULL;
417
goto errout;
418
}
419
if (asprintf(&old_file, "%s.bak", outfile) < 0) {
420
old_file = NULL;
421
goto errout;
422
}
423
424
errno = 0;
425
426
f = fopen(new_file, "w");
427
if (!f) {
428
retval = errno;
429
if (retval == 0)
430
retval = PROF_FAIL_OPEN;
431
goto errout;
432
}
433
434
set_cloexec_file(f);
435
profile_write_tree_file(data->root, f);
436
if (fclose(f) != 0) {
437
retval = errno;
438
goto errout;
439
}
440
441
unlink(old_file);
442
if (make_hard_link(outfile, old_file) == 0) {
443
/* Okay, got the hard link. Yay. Now we've got our
444
backup version, so just put the new version in
445
place. */
446
if (rename(new_file, outfile)) {
447
/* Weird, the rename didn't work. But the old version
448
should still be in place, so no special cleanup is
449
needed. */
450
retval = errno;
451
goto errout;
452
}
453
} else if (errno == ENOENT && can_create) {
454
if (rename(new_file, outfile)) {
455
retval = errno;
456
goto errout;
457
}
458
} else {
459
/* Couldn't make the hard link, so there's going to be a
460
small window where data->filespec does not refer to
461
either version. */
462
#ifndef _WIN32
463
sync();
464
#endif
465
if (rename(outfile, old_file)) {
466
retval = errno;
467
goto errout;
468
}
469
if (rename(new_file, outfile)) {
470
retval = errno;
471
rename(old_file, outfile); /* back out... */
472
goto errout;
473
}
474
}
475
476
retval = 0;
477
478
errout:
479
if (new_file)
480
free(new_file);
481
if (old_file)
482
free(old_file);
483
return retval;
484
}
485
486
errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp)
487
{
488
errcode_t retval;
489
490
k5_mutex_lock(&data->lock);
491
retval = profile_write_tree_to_buffer(data->root, bufp);
492
k5_mutex_unlock(&data->lock);
493
return retval;
494
}
495
496
errcode_t profile_flush_file_data(prf_data_t data)
497
{
498
errcode_t retval = 0;
499
500
if (!data || data->magic != PROF_MAGIC_FILE_DATA)
501
return PROF_MAGIC_FILE_DATA;
502
503
/* Do nothing if this data object has no backing file. */
504
if (*data->filespec == '\0')
505
return 0;
506
507
k5_mutex_lock(&data->lock);
508
509
if ((data->flags & PROFILE_FILE_DIRTY) == 0) {
510
k5_mutex_unlock(&data->lock);
511
return 0;
512
}
513
514
retval = write_data_to_file(data, data->filespec, 0);
515
data->flags &= ~PROFILE_FILE_DIRTY;
516
k5_mutex_unlock(&data->lock);
517
return retval;
518
}
519
520
errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile)
521
{
522
errcode_t retval = 0;
523
524
if (!data || data->magic != PROF_MAGIC_FILE_DATA)
525
return PROF_MAGIC_FILE_DATA;
526
527
k5_mutex_lock(&data->lock);
528
retval = write_data_to_file(data, outfile, 1);
529
k5_mutex_unlock(&data->lock);
530
return retval;
531
}
532
533
534
535
void profile_dereference_data(prf_data_t data)
536
{
537
k5_mutex_lock(&g_shared_trees_mutex);
538
profile_dereference_data_locked(data);
539
k5_mutex_unlock(&g_shared_trees_mutex);
540
}
541
void profile_dereference_data_locked(prf_data_t data)
542
{
543
data->refcount--;
544
if (data->refcount == 0)
545
profile_free_file_data(data);
546
}
547
548
void profile_lock_global(void)
549
{
550
k5_mutex_lock(&g_shared_trees_mutex);
551
}
552
void profile_unlock_global(void)
553
{
554
k5_mutex_unlock(&g_shared_trees_mutex);
555
}
556
557
prf_file_t profile_copy_file(prf_file_t oldfile)
558
{
559
prf_file_t file;
560
561
file = calloc(1, sizeof(*file));
562
if (file == NULL)
563
return NULL;
564
file->magic = PROF_MAGIC_FILE;
565
566
/* Shared data objects can just have their reference counts incremented. */
567
if (oldfile->data->flags & PROFILE_FILE_SHARED) {
568
profile_lock_global();
569
oldfile->data->refcount++;
570
profile_unlock_global();
571
file->data = oldfile->data;
572
return file;
573
}
574
575
/* Otherwise we need to copy the data object. */
576
file->data = profile_make_prf_data(oldfile->data->filespec);
577
if (file->data == NULL) {
578
free(file);
579
return NULL;
580
}
581
k5_mutex_lock(&oldfile->data->lock);
582
file->data->flags = oldfile->data->flags;
583
file->data->last_stat = oldfile->data->last_stat;
584
file->data->frac_ts = oldfile->data->frac_ts;
585
file->data->root = profile_copy_node(oldfile->data->root);
586
k5_mutex_unlock(&oldfile->data->lock);
587
if (file->data->root == NULL) {
588
profile_free_file(file);
589
return NULL;
590
}
591
592
return file;
593
}
594
595
void profile_free_file(prf_file_t prf)
596
{
597
profile_dereference_data(prf->data);
598
free(prf);
599
}
600
601
/* Call with mutex locked! */
602
static void profile_free_file_data(prf_data_t data)
603
{
604
if (data->flags & PROFILE_FILE_SHARED) {
605
/* Remove from linked list. */
606
if (g_shared_trees == data)
607
g_shared_trees = data->next;
608
else {
609
prf_data_t prev, next;
610
prev = g_shared_trees;
611
next = prev->next;
612
while (next) {
613
if (next == data) {
614
prev->next = next->next;
615
break;
616
}
617
prev = next;
618
next = next->next;
619
}
620
}
621
}
622
if (data->root)
623
profile_free_node(data->root);
624
data->magic = 0;
625
k5_mutex_destroy(&data->lock);
626
free(data);
627
}
628
629
errcode_t profile_close_file(prf_file_t prf)
630
{
631
errcode_t retval;
632
633
retval = profile_flush_file(prf);
634
if (retval)
635
return retval;
636
profile_free_file(prf);
637
return 0;
638
}
639
640