1// attributes.cc
2// Class to manage partition attribute codes. These are binary bit fields,
3// of which only four are currently (2/2011) documented on Wikipedia, and
4// two others found from other sources.
5
6/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
7  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
9#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <stdint.h>
13#include <stdio.h>
14#include <iostream>
15#include <sstream>
16
17#include "attributes.h"
18#include "support.h"
19
20using namespace std;
21
22string Attributes::atNames[NUM_ATR];
23int Attributes::numAttrs = 0;
24//Attributes::staticInit Attributes::staticInitializer;
25
26// Default constructor
27Attributes::Attributes(void) {
28   numAttrs++;
29   if (numAttrs == 1)
30      Setup();
31   attributes = 0;
32} // constructor
33
34// Alternate constructor
35Attributes::Attributes(const uint64_t a) {
36   numAttrs++;
37   if (numAttrs == 1)
38      Setup();
39   attributes = a;
40} // alternate constructor
41
42// Destructor.
43Attributes::~Attributes(void) {
44   numAttrs--;
45} // Attributes destructor
46
47void Attributes::Setup(void) {
48   ostringstream temp;
49
50   // Most bits are undefined, so start by giving them an
51   // appropriate name
52   for (int i = 0; i < NUM_ATR; i++) {
53      temp.str("");
54      temp << "Undefined bit #" << i;
55      Attributes::atNames[i] = temp.str();
56   } // for
57
58   // Now reset those names that are defined....
59   atNames[0] = "system partition"; // required for computer to operate
60   atNames[1] = "hide from EFI";
61   atNames[2] = "legacy BIOS bootable";
62   atNames[60] = "read-only";
63   atNames[62] = "hidden";
64   atNames[63] = "do not automount";
65}  // Attributes::Setup()
66
67// Display current attributes to user
68void Attributes::DisplayAttributes(void) {
69   uint32_t i;
70   int numSet = 0;
71
72   cout << "Attribute value is ";
73   cout.setf(ios::uppercase);
74   cout.fill('0');
75   cout.width(16);
76   cout << hex << attributes << dec << ". Set fields are:\n";
77   for (i = 0; i < NUM_ATR; i++) {
78      if ((UINT64_C(1) << i) & attributes) {
79         cout << i << " (" << GetAttributeName(i) << ")" << "\n";
80         numSet++;
81      } // if
82   } // for
83   cout.fill(' ');
84   if (numSet == 0)
85      cout << "  No fields set\n";
86   cout << "\n";
87} // Attributes::DisplayAttributes()
88
89// Display attributes for a partition. Note that partNum is just passed for
90// immediate display; it's not used to access a particular partition.
91void Attributes::ShowAttributes(const uint32_t partNum) {
92   uint32_t bitNum;
93   bool bitset;
94
95   for (bitNum = 0; bitNum < 64; bitNum++) {
96      bitset = (UINT64_C(1) << bitNum) & attributes;
97      if (bitset) {
98         cout << partNum+1 << ":" << bitNum << ":" << bitset
99         << " (" << GetAttributeName(bitNum) << ")" << endl;
100      } // if
101   } // for
102} // Attributes::ShowAttributes
103
104// Prompt user for attribute changes
105void Attributes::ChangeAttributes(void) {
106   int response;
107   uint64_t bitValue;
108
109   cout << "Known attributes are:\n";
110   ListAttributes();
111   cout << "\n";
112
113   do {
114      DisplayAttributes();
115      response = GetNumber(0, NUM_ATR, 64,
116                           "Toggle which attribute field (0-63, 64 or <Enter> to exit): ");
117      if (response != 64) {
118         bitValue = UINT64_C(1) << response; // Find the integer value of the bit
119         if (bitValue & attributes) { // bit is set
120            attributes &= ~bitValue; // so unset it
121	         cout << "Have disabled the '" << atNames[response] << "' attribute.\n";
122         } else { // bit is not set
123            attributes |= bitValue; // so set it
124            cout << "Have enabled the '" << atNames[response] << "' attribute.\n";
125         } // if/else
126      } // if
127   } while (response != 64);
128} // Attributes::ChangeAttributes()
129
130// Display all defined attributes on the screen (omits undefined bits).
131void Attributes::ListAttributes(void) {
132   uint32_t bitNum;
133   string tempAttr;
134
135   for (bitNum = 0; bitNum < NUM_ATR; bitNum++) {
136      tempAttr = GetAttributeName(bitNum);
137      if (tempAttr.substr(0, 15) != "Undefined bit #" )
138         cout << bitNum << ": " << Attributes::GetAttributeName(bitNum) << "\n";
139   } // for
140} // Attributes::ListAttributes
141
142// multifaceted attributes access
143// returns true upon success, false upon failure
144bool Attributes::OperateOnAttributes(const uint32_t partNum, const string& attributeOperator, const string& attributeBits) {
145
146   // attribute access opcode
147   typedef enum {
148      ao_or, ao_nand, ao_xor, ao_assignall,  // operate on all attributes (bitmask)
149      ao_unknown, // must be after bitmask operators and before bitnum operators
150      ao_set, ao_clear, ao_toggle, ao_get    // operate on a single attribute (bitnum)
151   } attribute_opcode_t; // typedef enum
152
153   // translate attribute operator into an attribute opcode
154   attribute_opcode_t attributeOpcode = ao_unknown; { // opcode is not known yet
155      if      (attributeOperator == "or")      attributeOpcode = ao_or;
156      else if (attributeOperator == "nand")    attributeOpcode = ao_nand;
157      else if (attributeOperator == "xor")     attributeOpcode = ao_xor;
158      else if (attributeOperator == "=")       attributeOpcode = ao_assignall;
159      else if (attributeOperator == "set")     attributeOpcode = ao_set;
160      else if (attributeOperator == "clear")   attributeOpcode = ao_clear;
161      else if (attributeOperator == "toggle")  attributeOpcode = ao_toggle;
162      else if (attributeOperator == "get")     attributeOpcode = ao_get;
163      else {
164         cerr << "Unknown attributes operator: " << attributeOperator << endl;
165         return false;
166      } // else
167   } // attributeOpcode
168
169   // get bit mask if operating on entire attribute set
170   uint64_t attributeBitMask; { if (attributeOpcode < ao_unknown) {
171      if (1 != sscanf (attributeBits.c_str(), "%qx", (long long unsigned int*) &attributeBitMask)) {
172         cerr << "Could not convert hex attribute mask" << endl;
173         return false;
174      } // if
175   }} // attributeBitMask, if
176
177   // get bit number and calculate bit mask if operating on a single attribute
178   int bitNum; { if (attributeOpcode > ao_unknown) {
179      if (1 != sscanf (attributeBits.c_str(), "%d", &bitNum)) {
180         cerr << "Could not convert bit number" << endl;
181         return false;
182      } // if
183      const uint64_t one = 1;
184      attributeBitMask = one << bitNum;
185   }} // bitNum, if
186
187   switch (attributeOpcode) {
188      // assign all attributes at once
189      case ao_assignall:  attributes = attributeBitMask;    break;
190
191      // set individual attribute(s)
192      case ao_set:
193      case ao_or:         attributes |= attributeBitMask;   break;
194
195      // clear individual attribute(s)
196      case ao_clear:
197      case ao_nand:       attributes &= ~attributeBitMask;  break;
198
199      // toggle individual attribute(s)
200      case ao_toggle:
201      case ao_xor:        attributes ^= attributeBitMask;   break;
202
203      // display a single attribute
204      case ao_get: {
205         cout << partNum+1 << ":" << bitNum << ":"
206              << bool (attributeBitMask & attributes) << endl;
207         break;
208      } // case ao_get
209
210      default: break; // will never get here
211   } // switch
212
213   return true;
214} // Attributes::OperateOnAttributes()
215
216/*******************************
217*                             *
218* Non-class support functions *
219*                             *
220*******************************/
221
222// Display attributes
223ostream & operator<<(ostream & os, const Attributes & data) {
224   os << data.GetAttributes();
225   return os;
226} // operator<<()
227