1/*
2 * Copyright (c) 2015-2019, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 *  * Redistributions of source code must retain the above copyright notice,
8 *    this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright notice,
10 *    this list of conditions and the following disclaimer in the documentation
11 *    and/or other materials provided with the distribution.
12 *  * Neither the name of Intel Corporation nor the names of its contributors
13 *    may be used to endorse or promote products derived from this software
14 *    without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "pt_section.h"
30#include "pt_section_file.h"
31
32#include "intel-pt.h"
33
34#include <stdlib.h>
35#include <stdio.h>
36
37
38/* This is a variation of ptunit-section.c.
39 *
40 * We provide pt_section_map() et.al. that are normally provided by mmap-based
41 * section implementations.  Our implementation falls back to file-based
42 * sections so we're able to test them.
43 *
44 * The actual test is in ptunit-section.c.
45 */
46
47/* The file status used for detecting changes to a file between unmap and map.
48 *
49 * In our case, the changes always affect the size of the file.
50 */
51struct pt_file_status {
52	/* The size in bytes. */
53	long size;
54};
55
56int pt_section_mk_status(void **pstatus, uint64_t *psize, const char *filename)
57{
58	struct pt_file_status *status;
59	FILE *file;
60	long size;
61	int errcode;
62
63	if (!pstatus || !psize)
64		return -pte_internal;
65
66	file = fopen(filename, "rb");
67	if (!file)
68		return -pte_bad_image;
69
70	errcode = fseek(file, 0, SEEK_END);
71	if (errcode) {
72		errcode = -pte_bad_image;
73		goto out_file;
74	}
75
76	size = ftell(file);
77	if (size < 0) {
78		errcode = -pte_bad_image;
79		goto out_file;
80	}
81
82	status = malloc(sizeof(*status));
83	if (!status) {
84		errcode = -pte_nomem;
85		goto out_file;
86	}
87
88	status->size = size;
89
90	*pstatus = status;
91	*psize = (uint64_t) size;
92
93	errcode = 0;
94
95out_file:
96	fclose(file);
97	return errcode;
98}
99
100static int pt_section_map_success(struct pt_section *section)
101{
102	uint16_t mcount;
103	int errcode, status;
104
105	if (!section)
106		return -pte_internal;
107
108	mcount = section->mcount + 1;
109	if (!mcount) {
110		(void) pt_section_unlock(section);
111		return -pte_overflow;
112	}
113
114	section->mcount = mcount;
115
116	errcode = pt_section_unlock(section);
117	if (errcode < 0)
118		return errcode;
119
120	status = pt_section_on_map(section);
121	if (status < 0) {
122		(void) pt_section_unmap(section);
123		return status;
124	}
125
126	return 0;
127}
128
129int pt_section_map(struct pt_section *section)
130{
131	struct pt_file_status *status;
132	const char *filename;
133	uint16_t mcount;
134	FILE *file;
135	long size;
136	int errcode;
137
138	if (!section)
139		return -pte_internal;
140
141	errcode = pt_section_lock(section);
142	if (errcode < 0)
143		return errcode;
144
145	mcount = section->mcount;
146	if (mcount)
147		return pt_section_map_success(section);
148
149	if (section->mapping)
150		goto out_unlock;
151
152	filename = section->filename;
153	if (!filename)
154		goto out_unlock;
155
156	status = section->status;
157	if (!status)
158		goto out_unlock;
159
160	errcode = -pte_bad_image;
161	file = fopen(filename, "rb");
162	if (!file)
163		goto out_unlock;
164
165	errcode = fseek(file, 0, SEEK_END);
166	if (errcode) {
167		errcode = -pte_bad_image;
168		goto out_file;
169	}
170
171	errcode = -pte_bad_image;
172	size = ftell(file);
173	if (size < 0)
174		goto out_file;
175
176	if (size != status->size)
177		goto out_file;
178
179	/* We need to keep the file open on success.  It will be closed when
180	 * the section is unmapped.
181	 */
182	errcode = pt_sec_file_map(section, file);
183	if (!errcode)
184		return pt_section_map_success(section);
185
186out_file:
187	fclose(file);
188
189out_unlock:
190	(void) pt_section_unlock(section);
191	return errcode;
192}
193