[apparmor] [PATCH 25/31] parser: Create initial interface for policy cache
John Johansen
john.johansen at canonical.com
Wed Jan 28 20:16:32 UTC 2015
On 01/22/2015 07:08 PM, Tyler Hicks wrote:
> On 2015-01-22 10:15:48, John Johansen wrote:
>> On 12/05/2014 04:22 PM, Tyler Hicks wrote:
>>> This API has the same look-and-feel of the previous aa_match and
>>> aa_features APIs.
>>>
>>> The cache setup code was heavily dependent on globals set by CLI
>>> options. Options such as "skip the read cache", or "skip the write
>>> cache", or "don't clear the cache if it isn't valid", won't be useful
>>> for all aa_policy_cache API users so some of that logic was lifted out
>>> of the API. The constructor function still provides a bool parameter
>>> that specifies if the cache should be created or not.
>>>
>>> If the policy cache is invalid (currently meaning that the cache
>>> features file doesn't match the kernel features file), then a new
>>> aa_policy_cache object is still created but a call to
>>> aa_policy_cache_is_valid() will return false. The caller can then decide
>>> what to do (create a new valid cache, stop, etc.)
>>>
>>> Signed-off-by: Tyler Hicks <tyhicks at canonical.com>
>>
Acked-by: John Johansen <john.johansen at canonical.com>
>> comments inline
>>
>>> ---
>>> parser/parser_main.c | 56 +++++++++++------
>>> parser/policy_cache.c | 171 +++++++++++++++++++++++++++++++++++++++-----------
>>> parser/policy_cache.h | 12 +++-
>>> 3 files changed, 182 insertions(+), 57 deletions(-)
>>>
>>> diff --git a/parser/parser_main.c b/parser/parser_main.c
>>> index 5b0e215..2de8edd 100644
>>> --- a/parser/parser_main.c
>>> +++ b/parser/parser_main.c
>>> @@ -860,6 +860,7 @@ static void setup_flags(void)
>>>
>>> int main(int argc, char *argv[])
>>> {
>>> + aa_policy_cache *policy_cache;
>>> int retval, last_error;
>>> int i;
>>> int optind;
>>> @@ -892,29 +893,48 @@ int main(int argc, char *argv[])
>>>
>>> setup_flags();
>>>
>>> - if (!cacheloc && asprintf(&cacheloc, "%s/cache", basedir) == -1) {
>>> - PERROR(_("Memory allocation error."));
>>> - return 1;
>>> - }
>>> -
>>> - if (force_clear_cache) {
>>> - if (clear_cache_files(cacheloc)) {
>>> - PERROR(_("Failed to clear cache files (%s): %s\n"),
>>> - cacheloc, strerror(errno));
>>> + if ((!skip_cache && (write_cache || !skip_read_cache)) ||
>>> + force_clear_cache) {
>>> + if (!cacheloc && asprintf(&cacheloc, "%s/cache", basedir) == -1) {
>>> + PERROR(_("Memory allocation error."));
>>> return 1;
>>> }
>>>
>>> - return 0;
>>> - }
>>> + if (force_clear_cache) {
>>> + if (clear_cache_files(cacheloc)) {
>>> + PERROR(_("Failed to clear cache files (%s): %s\n"),
>>> + cacheloc, strerror(errno));
>>> + return 1;
>>> + }
>>> +
>>> + return 0;
>>> + }
>>
>> Not a criticism of the patch but a question of is this the behavior we want for --force_clear_cache?
>> That is if we succeed to abort with exit code 0 (ie. if force-clear-cache is specified we will never
>> process any profiles are anything
>
> I'm not sure that I'm following what you're saying. This is the same
> behavior that we have in trunk today. If you specify
> --force-clear-cache, the only thing that happens is the cache files are
> cleared and the parser exits.
>
> From main() of parser_main.c:
>
> if (force_clear_cache)
> exit(clear_cache_files(cacheloc));
>
>>>
>>> - if (create_cache_dir)
>>> - pwarn(_("The --create-cache-dir option is deprecated. Please use --write-cache.\n"));
>>> + if (create_cache_dir)
>>> + pwarn(_("The --create-cache-dir option is deprecated. Please use --write-cache.\n"));
>>>
>>> - retval = setup_cache(features, cacheloc);
>>> - if (retval) {
>>> - PERROR(_("Failed setting up policy cache (%s): %s\n"),
>>> - cacheloc, strerror(errno));
>>> - return 1;
>>> + retval = aa_policy_cache_new(&policy_cache, features, cacheloc,
>>> + write_cache);
>>> + if (retval) {
>>> + if (errno != ENOENT) {
>>> + PERROR(_("Failed setting up policy cache (%s): %s\n"),
>>> + cacheloc, strerror(errno));
>>> + return 1;
>>> + }
>>> +
>>> + write_cache = 0;
>>> + skip_read_cache = 0;
>>> + } else if (!aa_policy_cache_is_valid(policy_cache)) {
>>> + if (write_cache && cond_clear_cache &&
>>> + aa_policy_cache_create(policy_cache)) {
>>> + skip_read_cache = 1;
>>> + } else if (!write_cache || !cond_clear_cache) {
>>> + if (show_cache)
>>> + PERROR("Cache read/write disabled: Policy cache is invalid\n");
>>> + write_cache = 0;
>>> + skip_read_cache = 1;
>>> + }
>>> + }
>>
>> The way this is setup we change cache behavior
>>
>> If write_cache is specified the cache is always cleared at aa_policy_cache_new
>> and the following cond_clear_cache test is useless
>
> I don't think this is correct.
>
> The cache is only cleared, before consulting cond_clear_cache, if
> aa_features_new() fails when called by init_cache_features(). It only
> fails if there's not a valid features file due to the dir not existing,
> the .features file not existing, etc.
>
gah, yep sorry I flipped a condition around aa_features_new
> If there is a valid .features file, aa_policy_cache_new() will return
> success. Then there's the check to aa_policy_cache_is_valid(). If that
> fails and write_cache and cond_clear_cache are true, then we clear the
> cache.
>
>>
>> now whether we want to always clear the cache on write cache is the features
>> differ is another question
>>
>>> }
>>>
>>> retval = last_error = 0;
>>> diff --git a/parser/policy_cache.c b/parser/policy_cache.c
>>> index 6c8d5ca..f5061b1 100644
>>> --- a/parser/policy_cache.c
>>> +++ b/parser/policy_cache.c
>>> @@ -35,6 +35,14 @@
>>>
>>> #define le16_to_cpu(x) ((uint16_t)(le16toh (*(uint16_t *) x)))
>>>
>>> +struct aa_policy_cache {
>>> + unsigned int ref_count;
>>> + aa_features *features;
>>> + aa_features *kernel_features;
>>> + char *path;
>>> + char *features_path;
>>> +};
>>> +
>>> const char header_string[] = "\004\010\000version\000\002";
>>> #define HEADER_STRING_SIZE 12
>>> bool valid_cached_file_version(const char *cachename)
>>> @@ -107,37 +115,44 @@ int clear_cache_files(const char *path)
>>> return dirat_for_each(NULL, path, NULL, clear_cache_cb);
>>> }
>>>
>>> -int create_cache(const char *cachedir, const char *path, aa_features *features)
>>> +static int create_cache(aa_policy_cache *policy_cache, aa_features *features)
>>> {
>>> struct stat stat_file;
>>> autofclose FILE * f = NULL;
>>>
>>> - if (clear_cache_files(cachedir) != 0)
>>> + if (clear_cache_files(policy_cache->path) != 0)
>>> goto error;
>>>
>>> create_file:
>>> - if (aa_features_write_to_file(features, path) == -1)
>>> + if (aa_features_write_to_file(features,
>>> + policy_cache->features_path) == -1)
>>> goto error;
>>>
>>> + aa_features_unref(policy_cache->features);
>>> + policy_cache->features = aa_features_ref(features);
>>> return 0;
>>>
>>> error:
>>> /* does the dir exist? */
>>> - if (stat(cachedir, &stat_file) == -1) {
>>> - if (mkdir(cachedir, 0700) == 0)
>>> + if (stat(policy_cache->path, &stat_file) == -1) {
>>> + if (mkdir(policy_cache->path, 0700) == 0)
>>> goto create_file;
>>> if (show_cache)
>>> - PERROR(_("Can't create cache directory: %s\n"), cachedir);
>>> + PERROR(_("Can't create cache directory: %s\n"),
>>> + policy_cache->path);
>>> } else if (!S_ISDIR(stat_file.st_mode)) {
>>> if (show_cache)
>>> - PERROR(_("File in cache directory location: %s\n"), cachedir);
>>> + PERROR(_("File in cache directory location: %s\n"),
>>> + policy_cache->path);
>>> } else {
>>> if (show_cache)
>>> - PERROR(_("Can't update cache directory: %s\n"), cachedir);
>>> + PERROR(_("Can't update cache directory: %s\n"),
>>> + policy_cache->path);
>>> }
>>>
>>> if (show_cache)
>>> - PERROR("Cache write disabled: cannot create %s\n", path);
>>> + PERROR("Cache write disabled: cannot create %s\n",
>>> + policy_cache->features_path);
>>> write_cache = 0;
>>>
>>> return -1;
>>> @@ -224,45 +239,127 @@ void install_cache(const char *cachetmpname, const char *cachename)
>>> }
>>> }
>>>
>>> -int setup_cache(aa_features *kernel_features, const char *cacheloc)
>>> +static int init_cache_features(aa_policy_cache *policy_cache,
>>> + aa_features *kernel_features, bool create)
>>> {
>>> - autofree char *cache_features_path = NULL;
>>> - aa_features *cache_features;
>>> + if (aa_features_new(&policy_cache->features,
>>> + policy_cache->features_path)) {
>>> + policy_cache->features = NULL;
>>> + if (!create || errno != ENOENT)
>>> + return -1;
>>> +
>>> + if (create_cache(policy_cache, kernel_features))
>>> + return -1;
>> this could be just
>> return create_cache(...
>
> I've made this change in my local tree. Additionally, I carried the
> change into the "libapparmor: Move the aa_policy_cache API" patch in the
> second set of patches.
>
>>
>>> + }
>>> +
>>> + return 0;
>>> +}
>>>
>>> - if (!cacheloc) {
>>> +/**
>>> + * aa_policy_cache_new - create a new policy_cache from a path
>>> + * @policy_cache: will point to the address of an allocated and initialized
>>> + * aa_policy_cache_new object upon success
>>> + * @kernel_features: features representing the currently running kernel
>>> + * @path: path to the policy cache
>>> + * @create: true if the cache should be created if it doesn't already exist
>>> + *
>>> + * Returns: 0 on success, -1 on error with errno set and *@policy_cache
>>> + * pointing to NULL
>>> + */
>>> +int aa_policy_cache_new(aa_policy_cache **policy_cache,
>>> + aa_features *kernel_features, const char *path,
>>> + bool create)
>>> +{
>>> + aa_policy_cache *pc;
>>> +
>>> + *policy_cache = NULL;
>>> +
>>> + if (!path) {
>>> errno = EINVAL;
>>> return -1;
>>> }
>>>
>>> - /*
>>> - * Deal with cache directory versioning:
>>> - * - If cache/.features is missing, create it if --write-cache.
>>> - * - If cache/.features exists, and does not match features_string,
>>> - * force cache reading/writing off.
>>> - */
>>> - if (asprintf(&cache_features_path, "%s/.features", cacheloc) == -1) {
>>> - PERROR(_("Memory allocation error."));
>>> + pc = (aa_policy_cache *) calloc(1, sizeof(*pc));
>>> + if (!pc) {
>>> errno = ENOMEM;
>>> return -1;
>>> }
>>> + aa_policy_cache_ref(pc);
>>>
>>> - if (!aa_features_new(&cache_features, cache_features_path)) {
>>> - if (!aa_features_is_equal(kernel_features, cache_features)) {
>>> - if (write_cache && cond_clear_cache) {
>>> - if (create_cache(cacheloc, cache_features_path,
>>> - kernel_features))
>>> - skip_read_cache = 1;
>>> - } else {
>>> - if (show_cache)
>>> - PERROR("Cache read/write disabled: Police cache is invalid\n");
>>> - write_cache = 0;
>>> - skip_read_cache = 1;
>>> - }
>>> - }
>>> - aa_features_unref(cache_features);
>>> - } else if (write_cache) {
>>> - create_cache(cacheloc, cache_features_path, kernel_features);
>>> + pc->path = strdup(path);
>>> + if (!pc->path) {
>>> + aa_policy_cache_unref(pc);
>>> + errno = ENOMEM;
>>> + return -1;
>>> + }
>>> +
>>> + if (asprintf(&pc->features_path, "%s/.features", pc->path) == -1) {
>>> + pc->features_path = NULL;
>>> + aa_policy_cache_unref(pc);
>>> + errno = ENOMEM;
>>> + return -1;
>>> + }
>>> +
>>> + if (init_cache_features(pc, kernel_features, create)) {
>>> + int save = errno;
>>> +
>>> + aa_policy_cache_unref(pc);
>>> + errno = save;
>>> + return -1;
>>> }
>>>
>>> + pc->kernel_features = aa_features_ref(kernel_features);
>>> + *policy_cache = pc;
>>> +
>>> return 0;
>>> }
>>> +
>>> +/**
>>> + * aa_policy_cache_ref - increments the ref count of a policy_cache
>>> + * @policy_cache: the policy_cache
>>> + *
>>> + * Returns: the policy_cache
>>> + */
>>> +aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache)
>>> +{
>>> + atomic_inc(&policy_cache->ref_count);
>>> + return policy_cache;
>>> +}
>>> +
>>> +/**
>>> + * aa_policy_cache_unref - decrements the ref count and frees the policy_cache when 0
>>> + * @policy_cache: the policy_cache (can be NULL)
>>> + */
>>> +void aa_policy_cache_unref(aa_policy_cache *policy_cache)
>>> +{
>>> + if (policy_cache && atomic_dec_and_test(&policy_cache->ref_count)) {
>>> + free(policy_cache->features_path);
>>> + free(policy_cache->path);
>>> + free(policy_cache);
>>> + }
>>> +}
>>> +
>>> +/**
>>> + * aa_policy_cache_is_valid - checks if the policy_cache is valid for the currently running kernel
>>> + * @policy_cache: the policy_cache
>>> + *
>>> + * Returns: true if the policy_cache is valid for the currently running kernel,
>>> + * false if not
>>> + */
>>> +bool aa_policy_cache_is_valid(aa_policy_cache *policy_cache)
>>> +{
>>> + return aa_features_is_equal(policy_cache->features,
>>> + policy_cache->kernel_features);
>> So this is just me griping about wanting to get away from the equality test
>>
>> this is currently equiv to what we have but not really a true validity test
>>
>> It is possible to have cache that has more features compiled into it than the
>> current kernel supports and the kernel will still enforce it correctly. It is
>> also possible the kernel won't and the cache must be cleared (it depends on the
>> feature).
>>
>> The reverse situation where the cache supports fewer features than the current
>> kernel (kernel backport situation). Is interesting in that we currently handle
>> it poorly. The compiler, though it doesn't support the full feature set, will
>> store the kernel feature set in .features file but set a different abi in each
>> of the compiled cache files. The abi lets us detect the cache file is obsolete
>> but only on a per cache file basis, Not a global cache valid basis. So if we
>> are in an upgrade situation it is possible that if the userspace only used
>> aa_policy_cache_is_valid() it would start loading old files that should be
>> removed.
>>
>> And we haven't even gotten into some of the other feature issues that would
>> let us eliminate duplication and compile times between different kernels,
>> but then storage is cheap now so maybe those aren't worth solving.
>
> This is exactly why I named the function aa_policy_is_valid() instead of
> something like aa_policy_cache_features_equals_kernel_features(). We can
> change the check to whatever we want in the future. We can change the
> implementation from doing a dumb equality check and do the intelligent
> checks that you mentioned. Callers of this function won't know the
> difference.
>
> I see the systemd support and intelligent cache handling as two
> different chunks of work. Hopefully we can come up with an API that
> allows for systemd support now and intelligent cache handling in the
> future. Otherwise, we need to make these intelligent caching changes
> first and then turn it all into a library.
>
> Tyler
>
okay, like I said just my griping about equality and wanting to make sure
that is not considered what is valid.
>>> +}
>>> +
>>> +/**
>>> + * aa_policy_cache_create - creates a valid policy_cache for the currently running kernel
>>> + * @policy_cache: the policy_cache
>>> + *
>>> + * Returns: 0 on success, -1 on error with errno set and features pointing to
>>> + * NULL
>>> + */
>>> +int aa_policy_cache_create(aa_policy_cache *policy_cache)
>>> +{
>>> + return create_cache(policy_cache, policy_cache->kernel_features);
>>> +}
>>> diff --git a/parser/policy_cache.h b/parser/policy_cache.h
>>> index 728de57..7192939 100644
>>> --- a/parser/policy_cache.h
>>> +++ b/parser/policy_cache.h
>>> @@ -42,12 +42,20 @@ void set_mru_tstamp(struct timespec t);
>>> void update_mru_tstamp(FILE *file, const char *path);
>>> bool valid_cached_file_version(const char *cachename);
>>> int clear_cache_files(const char *path);
>>> -int create_cache(const char *cachedir, const char *path, const char *features);
>>> char *cache_filename(const char *cachedir, const char *basename);
>>> void valid_read_cache(const char *cachename);
>>> int cache_hit(const char *cachename);
>>> int setup_cache_tmp(const char **cachetmpname, const char *cachename);
>>> void install_cache(const char *cachetmpname, const char *cachename);
>>> -int setup_cache(aa_features *kernel_features, const char *cacheloc);
>>> +
>>> +typedef struct aa_policy_cache aa_policy_cache;
>>> +
>>> +int aa_policy_cache_new(aa_policy_cache **policy_cache,
>>> + aa_features *kernel_features, const char *path,
>>> + bool create);
>>> +aa_policy_cache *aa_policy_cache_ref(aa_policy_cache *policy_cache);
>>> +void aa_policy_cache_unref(aa_policy_cache *policy_cache);
>>> +bool aa_policy_cache_is_valid(aa_policy_cache *policy_cache);
>>> +int aa_policy_cache_create(aa_policy_cache *policy_cache);
>>>
>>> #endif /* __AA_POLICY_CACHE_H */
>>>
>>
>>
More information about the AppArmor
mailing list